aboutsummaryrefslogtreecommitdiffstats
path: root/erts/example/pg_sync.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/example/pg_sync.c')
-rw-r--r--erts/example/pg_sync.c180
1 files changed, 180 insertions, 0 deletions
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;
+}