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