diff options
Diffstat (limited to 'erts/example')
-rw-r--r-- | erts/example/Makefile | 62 | ||||
-rw-r--r-- | erts/example/next_perm.cc | 137 | ||||
-rw-r--r-- | erts/example/next_perm.erl | 66 | ||||
-rw-r--r-- | erts/example/pg_async.c | 224 | ||||
-rw-r--r-- | erts/example/pg_async.erl | 57 | ||||
-rw-r--r-- | erts/example/pg_async2.c | 244 | ||||
-rw-r--r-- | erts/example/pg_async2.erl | 53 | ||||
-rw-r--r-- | erts/example/pg_encode.c | 79 | ||||
-rw-r--r-- | erts/example/pg_encode.h | 21 | ||||
-rw-r--r-- | erts/example/pg_encode2.c | 82 | ||||
-rw-r--r-- | erts/example/pg_encode2.h | 21 | ||||
-rw-r--r-- | erts/example/pg_sync.c | 180 | ||||
-rw-r--r-- | erts/example/pg_sync.erl | 46 |
13 files changed, 1272 insertions, 0 deletions
diff --git a/erts/example/Makefile b/erts/example/Makefile new file mode 100644 index 0000000000..6e1a88b4da --- /dev/null +++ b/erts/example/Makefile @@ -0,0 +1,62 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2006-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +EI_ROOT = $(ERL_TOP)/lib/erl_interface +EI_INCLUDE = -I$(EI_ROOT)/include -I$(ERL_TOP)/erts/emulator/beam +EI_LIB = -L$(EI_ROOT)/obj/$(TARGET) -lei + +PQ_LIB = -lpq + +OUR_C_FLAGS = -g -Wall -fpic $(EI_INCLUDE) +CFLAGS += $(OUR_C_FLAGS) +CXXFLAGS += $(OUR_C_FLAGS) + +TARGETS = pg_sync.beam pg_async.beam pg_sync.so pg_async.so \ +next_perm.so next_perm.beam + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) *.o + rm -f pg_async2.so pg_encode2.beam pg_async2.beam + rm -f core erl_crash.dump + rm -f *~ + +pg_async2.o pg_encode2.o: pg_encode2.h + +pg_sync.o pg_async.o pg_encode.o: pg_encode.h + + +pg_async2.so: pg_encode2.o + +pg_sync.so pg_async.so: pg_encode.o + +pg_async2.so: pg_async2.o + $(CC) $(CFLAGS) pg_encode2.o -shared $< $(EI_LIB) $(PQ_LIB) -o $@ + +%.so: %.cc + $(CXX) $(CXXFLAGS) $< -shared -o $@ + +%.so: %.o + $(CC) $(CFLAGS) pg_encode.o -shared $< $(EI_LIB) $(PQ_LIB) -o $@ + +%: %.cc + $(CXX) $(CXXFLAGS) $< -o $@ diff --git a/erts/example/next_perm.cc b/erts/example/next_perm.cc new file mode 100644 index 0000000000..ee81cb0404 --- /dev/null +++ b/erts/example/next_perm.cc @@ -0,0 +1,137 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: A driver using libpq to connect to Postgres + * from erlang, a sample for the driver documentation + */ + +#include <erl_driver.h> + +#include <algorithm> +#include <vector> + +using namespace std; + +#include <iostream> +#define L cerr << __LINE__ << "\r\n"; + +/* Driver interface declarations */ +static ErlDrvData start(ErlDrvPort port, char*); +static void output(ErlDrvData drv_data, char *buf, int len); +static void ready_async(ErlDrvData, ErlDrvThreadData); + +static ErlDrvEntry next_perm_driver_entry = { + NULL, /* init */ + start, + NULL, /* stop */ + output, + NULL, /* ready_input */ + NULL, /* ready_output */ + "next_perm", /* the name of the driver */ + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + ready_async, + NULL, /* flush */ + NULL, /* call */ + NULL /* event */ +}; + +/* INITIALIZATION AFTER LOADING */ + +/* + * This is the init function called after this driver has been loaded. + * It must *not* be declared static. Must return the address to + * the driver entry. + */ + +#ifdef __cplusplus +extern "C" { // shouldn't this be in the DRIVER_INIT macro? +#endif +DRIVER_INIT(next_perm) +{ + return &next_perm_driver_entry; +} +#ifdef __cplusplus +} +#endif + +/* DRIVER INTERFACE */ +static ErlDrvData start(ErlDrvPort port, char *) +{ + if (port == NULL) + return ERL_DRV_ERROR_GENERAL; + return (ErlDrvData)port; +} + + +struct our_async_data { + bool prev; + vector<int> data; + our_async_data(ErlDrvPort p, int command, const char* buf, int len); +}; + +our_async_data::our_async_data(ErlDrvPort p, int command, + const char* buf, int len) + : prev(command == 2), + data((int*)buf, (int*)buf + len / sizeof(int)) +{ +} + +static void do_perm(void* async_data); + +static void output(ErlDrvData drv_data, char *buf, int len) +{ + if (*buf < 1 || *buf > 2) return; + ErlDrvPort port = reinterpret_cast<ErlDrvPort>(drv_data); + void* async_data = new our_async_data(port, *buf, buf+1, len); + driver_async(port, NULL, do_perm, async_data, NULL); +} + +static void do_perm(void* async_data) +{ + our_async_data* d = reinterpret_cast<our_async_data*>(async_data); + if (d->prev) + prev_permutation(d->data.begin(), d->data.end()); + else + next_permutation(d->data.begin(), d->data.end()); +} + +static void ready_async(ErlDrvData drv_data, ErlDrvThreadData async_data) +{ + ErlDrvPort port = reinterpret_cast<ErlDrvPort>(drv_data); + our_async_data* d = reinterpret_cast<our_async_data*>(async_data); + int n = d->data.size(), result_n = n*2 + 5; + ErlDrvTermData* result = new ErlDrvTermData[result_n], * rp = result; + *rp++ = ERL_DRV_PORT; + *rp++ = driver_mk_port(port); + for (vector<int>::iterator i = d->data.begin(); + i != d->data.end(); ++i) { + *rp++ = ERL_DRV_INT; + *rp++ = *i; + } + *rp++ = ERL_DRV_NIL; + *rp++ = ERL_DRV_LIST; + *rp++ = n+2; + driver_output_term(port, result, result_n); + delete[] result; + delete d; +} diff --git a/erts/example/next_perm.erl b/erts/example/next_perm.erl new file mode 100644 index 0000000000..40a5c24d35 --- /dev/null +++ b/erts/example/next_perm.erl @@ -0,0 +1,66 @@ +%%
+%% %CopyrightBegin%
+%% +%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd%
+%%
+-module(next_perm).
+
+-export([next_perm/1, prev_perm/1, load/0, all_perm/1]).
+
+load() ->
+ case whereis(next_perm) of
+ undefined ->
+ case erl_ddll:load_driver(".", "next_perm") of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ E -> exit(E)
+ end,
+ Port = open_port({spawn, "next_perm"}, []),
+ register(next_perm, Port);
+ _ ->
+ ok
+ end.
+
+list_to_integer_binaries(L) ->
+ [<<I:32/integer-native>> || I <- L].
+
+next_perm(L) ->
+ next_perm(L, 1).
+
+prev_perm(L) ->
+ next_perm(L, 2).
+
+next_perm(L, Nxt) ->
+ load(),
+ B = list_to_integer_binaries(L),
+ Port = whereis(next_perm),
+ port_command(Port, [Nxt, B]),
+ receive
+ [Port | Result] ->
+ Result
+ end.
+
+all_perm(L) ->
+ New = prev_perm(L),
+ all_perm(New, L, [New]).
+
+all_perm(L, L, Acc) ->
+ Acc;
+all_perm(L, Orig, Acc) ->
+ New = prev_perm(L),
+ all_perm(New, Orig, [New | Acc]).
+
+
diff --git a/erts/example/pg_async.c b/erts/example/pg_async.c new file mode 100644 index 0000000000..7ffb4bb1f3 --- /dev/null +++ b/erts/example/pg_async.c @@ -0,0 +1,224 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: A driver using libpq to connect to Postgres + * from erlang, a sample for the driver documentation + */ + +#include <erl_driver.h> + +#include <libpq-fe.h> + +#include <ei.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "pg_encode.h" + +/* Driver interface declarations */ +static ErlDrvData start(ErlDrvPort port, char *command); +static void stop(ErlDrvData drv_data); +static int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen); +static void ready_io(ErlDrvData drv_data, ErlDrvEvent event); + +static ErlDrvEntry pq_driver_entry = { + NULL, /* init */ + start, + stop, + NULL, /* output */ + ready_io, /* ready_input */ + ready_io, /* ready_output */ + "pg_async", /* the name of the driver */ + NULL, /* finish */ + NULL, /* handle */ + control, + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL /* event */ +}; + +typedef struct our_data_t { + PGconn* conn; + ErlDrvPort port; + int socket; + int connecting; +} our_data_t; + +/* Keep the following definitions in alignment with the FUNC_LIST + * in erl_pq_sync.erl + */ + +#define DRV_CONNECT 'C' +#define DRV_DISCONNECT 'D' +#define DRV_SELECT 'S' + +/* #define L fprintf(stderr, "%d\r\n", __LINE__) */ + +/* INITIALIZATION AFTER LOADING */ + +/* + * This is the init function called after this driver has been loaded. + * It must *not* be declared static. Must return the address to + * the driver entry. + */ +DRIVER_INIT(pq_drv) +{ + return &pq_driver_entry; +} + +static char* get_s(const char* buf, int len); +static int do_connect(const char *s, our_data_t* data); +static int do_disconnect(our_data_t* data); +static int do_select(const char* s, our_data_t* data); + +/* DRIVER INTERFACE */ +static ErlDrvData start(ErlDrvPort port, char *command) +{ + our_data_t* data = driver_alloc(sizeof(our_data_t)); + data->port = port; + data->conn = NULL; + return (ErlDrvData)data; +} + +static void stop(ErlDrvData drv_data) +{ + do_disconnect((our_data_t*)drv_data); +} + +static int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen) +{ + int r; + char* s = get_s(buf, len); + our_data_t* data = (our_data_t*)drv_data; + switch (command) { + case DRV_CONNECT: r = do_connect(s, data); break; + case DRV_DISCONNECT: r = do_disconnect(data); break; + case DRV_SELECT: r = do_select(s, data); break; + default: r = -1; break; + } + driver_free(s); + return r; +} + +static int do_connect(const char *s, our_data_t* data) +{ + PGconn* conn = PQconnectStart(s); + if (PQstatus(conn) == CONNECTION_BAD) { + ei_x_buff x; + ei_x_new_with_version(&x); + encode_error(&x, conn); + PQfinish(conn); + conn = NULL; + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + } + PQconnectPoll(conn); + int socket = PQsocket(conn); + data->socket = socket; + driver_select(data->port, (ErlDrvEvent)socket, DO_READ, 1); + driver_select(data->port, (ErlDrvEvent)socket, DO_WRITE, 1); + data->conn = conn; + data->connecting = 1; + return 0; +} + +static int do_disconnect(our_data_t* data) +{ + ei_x_buff x; + driver_select(data->port, (ErlDrvEvent)data->socket, DO_READ, 0); + driver_select(data->port, (ErlDrvEvent)data->socket, DO_WRITE, 0); + PQfinish(data->conn); + data->conn = NULL; + ei_x_new_with_version(&x); + encode_ok(&x); + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + return 0; +} + +static int do_select(const char* s, our_data_t* data) +{ + data->connecting = 0; + PGconn* conn = data->conn; + /* if there's an error return it now */ + if (PQsendQuery(conn, s) == 0) { + ei_x_buff x; + ei_x_new_with_version(&x); + encode_error(&x, conn); + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + } + /* else wait for ready_output to get results */ + return 0; +} + +static void ready_io(ErlDrvData drv_data, ErlDrvEvent event) +{ + PGresult* res = NULL; + our_data_t* data = (our_data_t*)drv_data; + PGconn* conn = data->conn; + ei_x_buff x; + ei_x_new_with_version(&x); + if (data->connecting) { + ConnStatusType status; + PQconnectPoll(conn); + status = PQstatus(conn); + if (status == CONNECTION_OK) + encode_ok(&x); + else if (status == CONNECTION_BAD) + encode_error(&x, conn); + } else { + PQconsumeInput(conn); + if (PQisBusy(conn)) + return; + res = PQgetResult(conn); + encode_result(&x, res, conn); + PQclear(res); + for (;;) { + res = PQgetResult(conn); + if (res == NULL) + break; + PQclear(res); + } + } + if (x.index > 1) { + driver_output(data->port, x.buff, x.index); + if (data->connecting) + driver_select(data->port, (ErlDrvEvent)data->socket, DO_WRITE, 0); + } + ei_x_free(&x); +} + +/* utilities */ +static char* get_s(const char* buf, int len) +{ + char* result; + if (len < 1 || len > 1000) return NULL; + result = driver_alloc(len+1); + memcpy(result, buf, len); + result[len] = '\0'; + return result; +} diff --git a/erts/example/pg_async.erl b/erts/example/pg_async.erl new file mode 100644 index 0000000000..10506bfe9f --- /dev/null +++ b/erts/example/pg_async.erl @@ -0,0 +1,57 @@ +%%
+%% %CopyrightBegin%
+%% +%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd%
+%%
+-module(pg_async).
+
+-define(DRV_CONNECT, $C).
+-define(DRV_DISCONNECT, $D).
+-define(DRV_SELECT, $S).
+
+-export([connect/1, disconnect/1, select/2]).
+
+connect(ConnectStr) ->
+ case erl_ddll:load_driver(".", "pg_async") of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ E -> exit(E)
+ end,
+ Port = open_port({spawn, ?MODULE}, [binary]),
+ port_control(Port, ?DRV_CONNECT, ConnectStr),
+ case return_port_data(Port) of
+ ok ->
+ {ok, Port};
+ Error ->
+ Error
+ end.
+
+disconnect(Port) ->
+ port_control(Port, ?DRV_DISCONNECT, ""),
+ R = return_port_data(Port),
+ port_close(Port),
+ R.
+
+select(Port, Query) ->
+ port_control(Port, ?DRV_SELECT, Query),
+ return_port_data(Port).
+
+return_port_data(Port) ->
+ receive
+ {Port, {data, Data}} ->
+ binary_to_term(Data)
+ end.
+
diff --git a/erts/example/pg_async2.c b/erts/example/pg_async2.c new file mode 100644 index 0000000000..368f9d32d0 --- /dev/null +++ b/erts/example/pg_async2.c @@ -0,0 +1,244 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: A driver using libpq to connect to Postgres + * from erlang, a sample for the driver documentation + */ + +#include <erl_driver.h> + +#include <libpq-fe.h> + +#include <ei.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "pg_encode2.h" + +#define L fprintf(stderr, "%d\r\n", __LINE__) + +/* Driver interface declarations */ +static ErlDrvData start(ErlDrvPort port, char *command); +static void stop(ErlDrvData drv_data); +static int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen); +static void ready_input(ErlDrvData drv_data, ErlDrvEvent event); + +static ErlDrvEntry pq_driver_entry = { + NULL, /* init */ + start, + stop, + NULL, /* output */ + ready_input, /* ready_input */ + NULL, /* ready_output */ + "pg_async2", + NULL, /* finish */ + NULL, /* handle */ + control, + NULL, /* timeout */ + NULL, /* outputv */ + NULL, + NULL, + NULL, + NULL +}; + +typedef struct our_data_t { + PGconn* conn; + ErlDrvPort port; + int socket; + char* s; +} our_data_t; + +our_data_t our_data; + +/* Keep the following definitions in alignment with the FUNC_LIST + * in erl_pq_sync.erl + */ + +#define DRV_CONNECT 1 +#define DRV_DISCONNECT 2 +#define DRV_SELECT 3 + +/* INITIALIZATION AFTER LOADING */ + +/* + * This is the init function called after this driver has been loaded. + * It must *not* be declared static. Must return the address to + * the driver entry. + */ + +#ifdef __cplusplus +extern "C" { /* this should be in the DRIVER_INIT macro! */ +#endif +DRIVER_INIT(pq_drv) +{ + return &pq_driver_entry; +} +#ifdef __cplusplus +} +#endif + +/* DRIVER INTERFACE */ +static ErlDrvData start(ErlDrvPort port, char *command) +{ + our_data_t* data = driver_alloc(sizeof(our_data_t)); + data->port = port; + data->conn = NULL; + return (ErlDrvData)data; +} + + +static char* get_s(const char* buf, int len); +static void free_s(char* s); + +static int do_connect(const char *s, our_data_t* data); +static int do_disconnect(our_data_t* data); +static int do_select(const char* s, our_data_t* data); + +static void stop(ErlDrvData drv_data) +{ + do_disconnect((our_data_t*)drv_data); +} + + +/* Since we are operating in binary mode, the return value from control + * is irrelevant, as long as it is not negative. + */ +static int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen) +{ + int r; + char* s; + + s = get_s(buf, len); + switch (command) { + case DRV_CONNECT: + r = do_connect(s, (our_data_t*)drv_data); + break; + case DRV_DISCONNECT: + r = do_disconnect((our_data_t*)drv_data); + break; + case DRV_SELECT: + r = do_select(s, (our_data_t*)drv_data); + break; + default: + r = -1; + break; + } + free_s(s); + return r; +} + +static int do_connect(const char *s, our_data_t* data) +{ + ei_x_buff x; + PGconn* conn = PQconnectdb(s); + + ei_x_new_with_version(&x); + if (PQstatus(conn) != CONNECTION_OK) { + encode_error(&x, conn); + PQfinish(conn); + conn = NULL; + } else { + encode_ok(&x); + data->socket = PQsocket(conn); + driver_select(data->port, (ErlDrvEvent)data->socket, DO_READ, 1); + } + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + data->conn = conn; + return 0; +} + +static int do_disconnect(our_data_t* data) +{ + ei_x_buff x; + + if (data->socket == 0) + return 0; + driver_select(data->port, (ErlDrvEvent)data->socket, DO_READ, 0); + data->socket = 0; + PQfinish(data->conn); + data->conn = NULL; + ei_x_new_with_version(&x); + encode_ok(&x); + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + return 0; +} + +static int do_select(const char* s, our_data_t* data) +{ + PGconn* conn = data->conn; + + /* if there's an error return it now */ + if (PQsendQueryParams(conn, s, 0, NULL, NULL, NULL, NULL, 1) == 0) { + ei_x_buff x; + ei_x_new_with_version(&x); + encode_error(&x, conn); + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + } + /* else wait for ready_output to get results */ + return 0; +} + +static void ready_input(ErlDrvData drv_data, ErlDrvEvent event) +{ + our_data_t* data = (our_data_t*)drv_data; + PGconn* conn = data->conn; + ei_x_buff x; + PGresult* res; + + PQconsumeInput(conn); + if (PQisBusy(conn)) + return; + ei_x_new_with_version(&x); + res = PQgetResult(conn); + encode_result(&x, res, conn); + driver_output(data->port, x.buff, x.index); + ei_x_free(&x); + PQclear(res); + for (;;) { + res = PQgetResult(conn); + if (res == NULL) + break; + PQclear(res); + } +} + +/* utilities */ + +static char* get_s(const char* buf, int len) +{ + char* result; + if (len < 1 || len > 1000) return NULL; + result = driver_alloc(len+1); + memcpy(result, buf, len); + result[len] = '\0'; + return result; +} + +static void free_s(char* s) +{ + driver_free(s); +} diff --git a/erts/example/pg_async2.erl b/erts/example/pg_async2.erl new file mode 100644 index 0000000000..4803abf508 --- /dev/null +++ b/erts/example/pg_async2.erl @@ -0,0 +1,53 @@ +%%
+%% %CopyrightBegin%
+%% +%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd%
+%%
+-module(pg_async2).
+
+-define(DRV_CONNECT, 1).
+-define(DRV_DISCONNECT, 2).
+-define(DRV_SELECT, 3).
+
+-export([connect/1, disconnect/1, select/2]).
+
+connect(ConnectStr) ->
+ case erl_ddll:load_driver(".", "pg_async2") of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ _ -> exit({error, could_not_load_driver})
+ end,
+ Port = open_port({spawn, ?MODULE}, [binary]),
+ port_control(Port, ?DRV_CONNECT, ConnectStr),
+ case return_port_data(Port) of
+ ok -> {ok, Port};
+ Error -> Error
+ end.
+
+disconnect(Port) ->
+ port_control(Port, ?DRV_DISCONNECT, ""),
+ return_port_data(Port).
+
+select(Port, Query) ->
+ port_control(Port, ?DRV_SELECT, Query),
+ return_port_data(Port).
+
+return_port_data(Port) ->
+ receive
+ {Port, {data, Data}} ->
+ binary_to_term(Data)
+ end.
+
diff --git a/erts/example/pg_encode.c b/erts/example/pg_encode.c new file mode 100644 index 0000000000..34ca1fe46c --- /dev/null +++ b/erts/example/pg_encode.c @@ -0,0 +1,79 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#include <erl_driver.h> + +#include <libpq-fe.h> + +#include <ei.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "pg_encode.h" + +void encode_ok(ei_x_buff* x) +{ + const char* k_ok = "ok"; + ei_x_encode_atom(x, k_ok); +} + +void encode_error(ei_x_buff* x, PGconn* conn) +{ + const char* k_error = "error"; + ei_x_encode_tuple_header(x, 2); + ei_x_encode_atom(x, k_error); + ei_x_encode_string(x, PQerrorMessage(conn)); +} + +void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn) +{ + int row, n_rows, col, n_cols; + switch (PQresultStatus(res)) { + case PGRES_TUPLES_OK: + n_rows = PQntuples(res); + n_cols = PQnfields(res); + ei_x_encode_tuple_header(x, 2); + encode_ok(x); + ei_x_encode_list_header(x, n_rows+1); + ei_x_encode_list_header(x, n_cols); + for (col = 0; col < n_cols; ++col) { + ei_x_encode_string(x, PQfname(res, col)); + } + ei_x_encode_empty_list(x); + for (row = 0; row < n_rows; ++row) { + ei_x_encode_list_header(x, n_cols); + for (col = 0; col < n_cols; ++col) { + ei_x_encode_string(x, PQgetvalue(res, row, col)); + } + ei_x_encode_empty_list(x); + } + ei_x_encode_empty_list(x); + break; + case PGRES_COMMAND_OK: + ei_x_encode_tuple_header(x, 2); + encode_ok(x); + ei_x_encode_string(x, PQcmdTuples(res)); + break; + default: + encode_error(x, conn); + break; + } +} + diff --git a/erts/example/pg_encode.h b/erts/example/pg_encode.h new file mode 100644 index 0000000000..4477c0c079 --- /dev/null +++ b/erts/example/pg_encode.h @@ -0,0 +1,21 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +void encode_ok(ei_x_buff* x); +void encode_error(ei_x_buff* x, PGconn* conn); +void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn); diff --git a/erts/example/pg_encode2.c b/erts/example/pg_encode2.c new file mode 100644 index 0000000000..a0e99ba3b3 --- /dev/null +++ b/erts/example/pg_encode2.c @@ -0,0 +1,82 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#include <erl_driver.h> + +#include <libpq-fe.h> + +#include <ei.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "pg_encode2.h" + +void encode_ok(ei_x_buff* x) +{ + const char* k_ok = "ok"; + ei_x_encode_atom(x, k_ok); +} + +void encode_error(ei_x_buff* x, PGconn* conn) +{ + const char* k_error = "error"; + ei_x_encode_tuple_header(x, 2); + ei_x_encode_atom(x, k_error); + ei_x_encode_string(x, PQerrorMessage(conn)); +} + +void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn) +{ + int row, n_rows, col, n_cols, fsize; + + switch (PQresultStatus(res)) { + case PGRES_TUPLES_OK: + n_rows = PQntuples(res); + n_cols = PQnfields(res); + ei_x_encode_tuple_header(x, 2); + encode_ok(x); + ei_x_encode_list_header(x, 1); + for (col = 0; col < n_cols; ++col) { + ei_x_encode_list_header(x, 1); + ei_x_encode_string(x, PQfname(res, col)); + } + ei_x_encode_empty_list(x); + for (row = 0; row < n_rows; ++row) { + ei_x_encode_list_header(x, 1); + for (col = 0; col < n_cols; ++col) { + ei_x_encode_list_header(x, 1); + fsize = PQgetlength(res, row, col); + ei_x_encode_binary(x, PQgetvalue(res, row, col), fsize); + } + ei_x_encode_empty_list(x); + } + ei_x_encode_empty_list(x); + break; + case PGRES_COMMAND_OK: + ei_x_encode_tuple_header(x, 2); + encode_ok(x); + ei_x_encode_string(x, PQcmdTuples(res)); + break; + default: + encode_error(x, conn); + break; + } +} + diff --git a/erts/example/pg_encode2.h b/erts/example/pg_encode2.h new file mode 100644 index 0000000000..4477c0c079 --- /dev/null +++ b/erts/example/pg_encode2.h @@ -0,0 +1,21 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +void encode_ok(ei_x_buff* x); +void encode_error(ei_x_buff* x, PGconn* conn); +void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn); diff --git a/erts/example/pg_sync.c b/erts/example/pg_sync.c new file mode 100644 index 0000000000..6eaa6138e6 --- /dev/null +++ b/erts/example/pg_sync.c @@ -0,0 +1,180 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: A driver using libpq to connect to Postgres + * from erlang, a sample for the driver documentation + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <libpq-fe.h> + +#include <erl_driver.h> +#include <ei.h> + +#include "pg_encode.h" + +/* Driver interface declarations */ +static ErlDrvData start(ErlDrvPort port, char *command); +static void stop(ErlDrvData drv_data); +static int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen); + +static ErlDrvEntry pq_driver_entry = { + NULL, /* init */ + start, + stop, + NULL, /* output */ + NULL, /* ready_input */ + NULL, /* ready_output */ + "pg_sync", /* the name of the driver */ + NULL, /* finish */ + NULL, /* handle */ + control, + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL /* event */ +}; + +typedef struct our_data_t { + PGconn* conn; +} our_data_t; + +/* Keep the following definitions in alignment with the + * defines in erl_pq_sync.erl + */ + +#define DRV_CONNECT 'C' +#define DRV_DISCONNECT 'D' +#define DRV_SELECT 'S' + +/* INITIALIZATION AFTER LOADING */ + +/* + * This is the init function called after this driver has been loaded. + * It must *not* be declared static. Must return the address to + * the driver entry. + */ + +DRIVER_INIT(pq_drv) +{ + return &pq_driver_entry; +} + +/* DRIVER INTERFACE */ +static ErlDrvData start(ErlDrvPort port, char *command) +{ + our_data_t* data; + + data = (our_data_t*)driver_alloc(sizeof(our_data_t)); + data->conn = NULL; + set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); + return (ErlDrvData)data; +} + +static int do_disconnect(our_data_t* data, ei_x_buff* x); + +static void stop(ErlDrvData drv_data) +{ + do_disconnect((our_data_t*)drv_data, NULL); +} + +static ErlDrvBinary* ei_x_to_new_binary(ei_x_buff* x) +{ + ErlDrvBinary* bin = driver_alloc_binary(x->index); + if (bin != NULL) + memcpy(&bin->orig_bytes[0], x->buff, x->index); + return bin; +} + +static char* get_s(const char* buf, int len); +static int do_connect(const char *s, our_data_t* data, ei_x_buff* x); +static int do_select(const char* s, our_data_t* data, ei_x_buff* x); + +/* Since we are operating in binary mode, the return value from control + * is irrelevant, as long as it is not negative. + */ +static int control(ErlDrvData drv_data, unsigned int command, char *buf, + int len, char **rbuf, int rlen) +{ + int r; + ei_x_buff x; + our_data_t* data = (our_data_t*)drv_data; + char* s = get_s(buf, len); + ei_x_new_with_version(&x); + switch (command) { + case DRV_CONNECT: r = do_connect(s, data, &x); break; + case DRV_DISCONNECT: r = do_disconnect(data, &x); break; + case DRV_SELECT: r = do_select(s, data, &x); break; + default: r = -1; break; + } + *rbuf = (char*)ei_x_to_new_binary(&x); + ei_x_free(&x); + driver_free(s); + return r; +} + +static int do_connect(const char *s, our_data_t* data, ei_x_buff* x) +{ + PGconn* conn = PQconnectdb(s); + if (PQstatus(conn) != CONNECTION_OK) { + encode_error(x, conn); + PQfinish(conn); + conn = NULL; + } else { + encode_ok(x); + } + data->conn = conn; + return 0; +} + +static int do_disconnect(our_data_t* data, ei_x_buff* x) +{ + if (data->conn == NULL) + return 0; + PQfinish(data->conn); + data->conn = NULL; + if (x != NULL) + encode_ok(x); + return 0; +} + +static int do_select(const char* s, our_data_t* data, ei_x_buff* x) +{ + PGresult* res = PQexec(data->conn, s); + encode_result(x, res, data->conn); + PQclear(res); + return 0; +} + +/* utilities */ +static char* get_s(const char* buf, int len) +{ + char* result; + if (len < 1 || len > 10000) return NULL; + result = driver_alloc(len+1); + memcpy(result, buf, len); + result[len] = '\0'; + return result; +} diff --git a/erts/example/pg_sync.erl b/erts/example/pg_sync.erl new file mode 100644 index 0000000000..58cc149e12 --- /dev/null +++ b/erts/example/pg_sync.erl @@ -0,0 +1,46 @@ +%%
+%% %CopyrightBegin%
+%% +%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd%
+%%
+-module(pg_sync).
+
+-define(DRV_CONNECT, $C).
+-define(DRV_DISCONNECT, $D).
+-define(DRV_SELECT, $S).
+
+-export([connect/1, disconnect/1, select/2]).
+
+connect(ConnectStr) ->
+ case erl_ddll:load_driver(".", "pg_sync") of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ E -> exit(E)
+ end,
+ Port = open_port({spawn, ?MODULE}, []),
+ case binary_to_term(port_control(Port, ?DRV_CONNECT, ConnectStr)) of
+ ok -> {ok, Port};
+ Error -> Error
+ end.
+
+disconnect(Port) ->
+ R = binary_to_term(port_control(Port, ?DRV_DISCONNECT, "")),
+ port_close(Port),
+ R.
+
+select(Port, Query) ->
+ binary_to_term(port_control(Port, ?DRV_SELECT, Query)).
+
|