/* * %CopyrightBegin% * * Copyright Ericsson AB 2006-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ /* * Purpose: A driver using libpq to connect to Postgres * from erlang, a sample for the driver documentation */ #include #include #include #include #include #include #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; }