aboutsummaryrefslogtreecommitdiffstats
path: root/erts/example
diff options
context:
space:
mode:
Diffstat (limited to 'erts/example')
-rw-r--r--erts/example/Makefile62
-rw-r--r--erts/example/next_perm.cc137
-rw-r--r--erts/example/next_perm.erl66
-rw-r--r--erts/example/pg_async.c224
-rw-r--r--erts/example/pg_async.erl57
-rw-r--r--erts/example/pg_async2.c244
-rw-r--r--erts/example/pg_async2.erl53
-rw-r--r--erts/example/pg_encode.c79
-rw-r--r--erts/example/pg_encode.h21
-rw-r--r--erts/example/pg_encode2.c82
-rw-r--r--erts/example/pg_encode2.h21
-rw-r--r--erts/example/pg_sync.c180
-rw-r--r--erts/example/pg_sync.erl46
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)).
+