00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00030 #include "config.h"
00031 #include "perr.h"
00032
00033 #ifdef USE_LIBPG
00034
00035 #include <string.h>
00036 #include <time.h>
00037 #include <postgresql/libpq-fe.h>
00038
00039 #include "dui-libpg.h"
00040 #include "dui-initdb-p.h"
00041
00042
00043
00044
00046 struct xxxtimespec64
00047 {
00048 long long int tv_sec;
00049 long int tv_nsec;
00050 };
00051 typedef struct xxxtimespec64 xxxTimespec;
00052
00053 xxxTimespec xxxgnc_iso8601_to_timespec_gmt(const char *str);
00054
00055
00056 typedef struct DuiLibPGConnection_s DuiLibPGConnection;
00057 typedef struct DuiLibPGRecordSet_s DuiLibPGRecordSet;
00058
00059 struct DuiLibPGConnection_s
00060 {
00061 DuiDBConnection dbcon;
00062 char * dbname;
00063 char * username;
00064 PGconn *conn;
00065
00066 GList * free_pool;
00067 };
00068
00069 struct DuiLibPGRecordSet_s
00070 {
00071 DuiDBRecordSet recset;
00072 DuiLibPGConnection *conn;
00073 PGresult *result;
00074 long nrows;
00075 long currow;
00076 };
00077
00078
00079
00080 static DuiDBConnection *
00081 dui_libpg_connection_new (const char * dbname,
00082 const char * username,
00083 const char * authentication_token)
00084 {
00085 PGconn *conn;
00086 DuiLibPGConnection *dui_conn;
00087 char * conninfo;
00088 ConnStatusType cs;
00089
00090 conninfo = g_strdup_printf ("host=localhost "
00091 "dbname='%s' user='%s' password='%s'",
00092 dbname, username, authentication_token);
00093
00094 conn = PQconnectdb(conninfo);
00095 g_free (conninfo);
00096 if (!conn)
00097 {
00098 PERR ("Couldn't connect to database with libpg\n");
00099 return NULL;
00100 }
00101
00102 cs = PQstatus(conn);
00103 if (CONNECTION_OK != cs)
00104 {
00105 char *emsg = PQerrorMessage(conn);
00106 PERR ("Failed to connect to database with libpg:\n\t%s\n", emsg);
00107
00108 return NULL;
00109 }
00110
00111 dui_conn = g_new0 (DuiLibPGConnection, 1);
00112 dui_conn->conn = conn;
00113 dui_conn->free_pool = NULL;
00114
00115 dui_conn->dbname = g_strdup (dbname);
00116 dui_conn->username = g_strdup (username);
00117
00118 return &dui_conn->dbcon;
00119 }
00120
00121
00122 static void dui_libpg_recordset_free (DuiLibPGRecordSet *rs);
00123
00124 static void
00125 dui_libpg_connection_free (DuiDBConnection *dbc)
00126 {
00127 GList *node;
00128 DuiLibPGConnection *conn = (DuiLibPGConnection *)dbc;
00129 if (!conn) return;
00130
00131 for (node=conn->free_pool; node; node=node->next)
00132 {
00133 dui_libpg_recordset_free (node->data);
00134 }
00135 g_list_free (conn->free_pool);
00136 conn->free_pool = NULL;
00137
00138 g_free (conn->dbname);
00139 g_free (conn->username);
00140 PQfinish (conn->conn);
00141 conn->conn = NULL;
00142 }
00143
00144
00145
00146 #define DUIPG_CHECK_CONN(retval) \
00147 if (!rc) { \
00148 const char * ret_str = NULL; \
00149 ret_str = PQerrorMessage (conn->conn); \
00150 PERR ("Database query execution error:\n%s\n", ret_str); \
00151 return retval; \
00152 }
00153
00154 #define DUIPG_CHECK_RESULT(retval) \
00155 ExecStatusType status; \
00156 if (NULL == result) { \
00157 char * msg = PQerrorMessage(conn->conn); \
00158 PERR ("Failed to get response from DB: %s\n", msg); \
00159 return retval; \
00160 } \
00161 status = PQresultStatus(result); \
00162 if ((PGRES_COMMAND_OK != status) && (PGRES_TUPLES_OK != status)) { \
00163 char * msg = PQresultErrorMessage(result); \
00164 PQclear (result); \
00165 PERR ("Failed to get any results from DB: %s\n", msg); \
00166 return retval; \
00167 } \
00168 if (0 >= PQntuples (result)) { \
00169 char * msg = PQresultErrorMessage(result); \
00170 PQclear (result); \
00171 PERR ("Failed to get any results from DB: %s\n", msg); \
00172 return retval; \
00173 }
00174
00175 static struct timespec
00176 dui_libpg_get_now (DuiDBConnection *dbc)
00177 {
00178 const gchar * val;
00179 PGresult *result;
00180 xxxTimespec xts;
00181 struct timespec ts = {0,-1};
00182 DuiLibPGConnection *conn = (DuiLibPGConnection *) dbc;
00183 int rc;
00184
00185 if (!conn) return ts;
00186
00187 rc = PQsendQuery(conn->conn, "SELECT CURRENT_TIMESTAMP AS qwertyuiopasdfghjkl;");
00188 DUIPG_CHECK_CONN (ts);
00189
00190 result = PQgetResult (conn->conn);
00191 {
00192 DUIPG_CHECK_RESULT (ts);
00193 }
00194
00195 val = PQgetvalue (result, 0, 0);
00196 xts = xxxgnc_iso8601_to_timespec_gmt (val);
00197 ts.tv_sec = xts.tv_sec;
00198 ts.tv_nsec = xts.tv_nsec;
00199 while (result)
00200 {
00201 PQclear(result);
00202 result = PQgetResult (conn->conn);
00203 }
00204
00205 return ts;
00206 }
00207
00208
00209
00210 static void
00211 dui_libpg_lock_table (DuiDBConnection *dbc, const char *table)
00212 {
00213 gchar * str;
00214 gint rc;
00215 PGresult *result;
00216 DuiLibPGConnection *conn = (DuiLibPGConnection *) dbc;
00217 if (!conn) return;
00218
00219 g_strdup_printf ("LOCK TABLE %s;\n", table);
00220 PQsendQuery(conn->conn, str);
00221 g_free (str);
00222 DUIPG_CHECK_CONN ( );
00223
00224 result = PQgetResult (conn->conn);
00225 while (result)
00226 {
00227 PQclear(result);
00228 result = PQgetResult (conn->conn);
00229 }
00230 }
00231
00232
00233
00234 static void
00235 dui_libpg_unlock_table (DuiDBConnection *dbc, const char *table)
00236 {
00237 gchar * str;
00238 gint rc;
00239 PGresult *result;
00240 DuiLibPGConnection *conn = (DuiLibPGConnection *) dbc;
00241 if (!conn) return;
00242
00243 str = g_strdup_printf ("UNLOCK TABLE %s;\n", table);
00244 rc = PQsendQuery(conn->conn, str);
00245 g_free (str);
00246 DUIPG_CHECK_CONN ( );
00247
00248 result = PQgetResult (conn->conn);
00249 while (result)
00250 {
00251 PQclear(result);
00252 result = PQgetResult (conn->conn);
00253 }
00254 }
00255
00256
00257
00258 static DuiDBRecordSet *
00259 dui_libpg_connection_exec (DuiDBConnection *dbc, const char * buff)
00260 {
00261 DuiLibPGConnection *conn = (DuiLibPGConnection *) dbc;
00262 DuiLibPGRecordSet *rs;
00263 int rc;
00264
00265 if (!conn) return NULL;
00266
00267 PINFO ("query=%s", buff);
00268 rc = PQsendQuery(conn->conn, buff);
00269 if (!rc)
00270 {
00271 const char * ret_str = NULL;
00272 ret_str = PQerrorMessage (conn->conn);
00273 PERR ("Database query execution error:\n%s\n", ret_str);
00274 return NULL;
00275 }
00276
00277 if (conn->free_pool)
00278 {
00279 rs = conn->free_pool->data;
00280 conn->free_pool = g_list_remove (conn->free_pool, rs);
00281 }
00282 else
00283 {
00284 rs = g_new (DuiLibPGRecordSet, 1);
00285 rs->conn = conn;
00286 }
00287
00288 rs->nrows = -1;
00289 rs->result = NULL;
00290 return &rs->recset;
00291 }
00292
00293
00294
00295 static void
00296 dui_libpg_recordset_release (DuiDBRecordSet *recset)
00297 {
00298 PGconn *conn;
00299 DuiLibPGRecordSet *rs = (DuiLibPGRecordSet *) recset;
00300 if (!rs) return;
00301
00302 conn = rs->conn->conn;
00303
00304
00305 if (NULL == rs->result) rs->result = PQgetResult (conn);
00306 while (rs->result)
00307 {
00308 PQclear(rs->result);
00309 rs->result = PQgetResult (conn);
00310 }
00311 rs->conn->free_pool = g_list_prepend (rs->conn->free_pool, rs);
00312 }
00313
00314
00315
00316 static void
00317 dui_libpg_recordset_free (DuiLibPGRecordSet *rs)
00318 {
00319 if (!rs) return;
00320 g_free (rs);
00321 }
00322
00323
00324
00325 static void
00326 dui_libpg_fetch_results (DuiDBRecordSet *recset)
00327 {
00328 ExecStatusType status;
00329 DuiLibPGRecordSet *rs = (DuiLibPGRecordSet *) recset;
00330
00331
00332
00333
00334 PGconn *conn = rs->conn->conn;
00335 rs->result = PQgetResult (conn);
00336 if (NULL == rs->result)
00337 {
00338 char * msg = PQerrorMessage(conn);
00339 PERR ("Failed to get response from DB: %s\n", msg);
00340 rs->nrows = 0;
00341 return;
00342 }
00343
00344 status = PQresultStatus(rs->result);
00345 if ((PGRES_COMMAND_OK != status) &&
00346 (PGRES_TUPLES_OK != status))
00347 {
00348 char * msg = PQresultErrorMessage(rs->result);
00349 PQclear (rs->result);
00350 rs->result = NULL;
00351 rs->nrows = 0;
00352 PERR ("Failed to get any results from DB: %s\n", msg);
00353 return;
00354 }
00355
00356 rs->nrows = PQntuples (rs->result);
00357 rs->currow = -1;
00358 }
00359
00360
00361
00362 static gint
00363 dui_libpg_recordset_rewind (DuiDBRecordSet *recset)
00364 {
00365 DuiLibPGRecordSet *rs = (DuiLibPGRecordSet *) recset;
00366 if (!rs) return 0;
00367 if (0 > rs->nrows && NULL == rs->result)
00368 {
00369 dui_libpg_fetch_results (recset);
00370 }
00371 rs->currow = 0;
00372 return rs->nrows;
00373 }
00374
00375
00376
00377 static gint
00378 dui_libpg_recordset_fetch_row (DuiDBRecordSet *recset)
00379 {
00380 DuiLibPGRecordSet *rs = (DuiLibPGRecordSet *) recset;
00381
00382 if (!rs) return 0;
00383
00384 if (0 > rs->nrows && NULL == rs->result)
00385 {
00386 dui_libpg_fetch_results (recset);
00387 }
00388
00389 rs->currow ++;
00390 if (rs->currow >= rs->nrows) return 0;
00391 return 1;
00392 }
00393
00394
00395
00396 static const gchar *
00397 dui_libpg_recordset_get_value (DuiDBRecordSet *recset, const char * fieldname)
00398 {
00399 DuiLibPGRecordSet *rs = (DuiLibPGRecordSet *) recset;
00400 const char *val;
00401 const char *fn;
00402
00403 if (!rs) return NULL;
00404
00405
00406
00407 fn = strrchr (fieldname, '.');
00408 if (fn) fn++;
00409 if (!fn) fn = fieldname;
00410 if (!fn) return NULL;
00411 if ('\0' == fn[0]) return NULL;
00412
00413 val = PQgetvalue (rs->result, rs->currow,
00414 PQfnumber (rs->result, fn));
00415 return val;
00416 }
00417
00418
00419
00420 static gint
00421 dui_libpg_recordset_get_error (DuiDBRecordSet *recset, gchar **ret_str)
00422 {
00423 DuiLibPGRecordSet *rs = (DuiLibPGRecordSet *) recset;
00424 if (!rs)
00425 {
00426 if (ret_str) *ret_str = "Called with null recordset";
00427 return -1;
00428 }
00429 #if 0
00430 pg_error_flag rc = pg_conn_error_flag (rs->conn->conn);
00431 pg_conn_error (rs->conn->conn, (const char **) ret_str);
00432 return (int) rc;
00433 #endif
00434 return 0;
00435 }
00436
00437
00438
00439 static void
00440 dui_libpg_plugin_free (DuiDBPlugin *plg)
00441 {
00442 g_free (plg);
00443 }
00444
00445
00446
00447 static DuiDBPlugin *
00448 dui_libpg_plugin_new (void)
00449 {
00450 DuiDBPlugin *plg;
00451 plg = g_new0 (DuiDBPlugin, 1);
00452 plg->db_provider_name = "libpg";
00453 plg->plugin_free = dui_libpg_plugin_free;
00454 plg->connection_new = dui_libpg_connection_new;
00455 plg->connection_free = dui_libpg_connection_free;
00456 plg->get_now = dui_libpg_get_now;
00457 plg->lock = dui_libpg_lock_table;
00458 plg->unlock = dui_libpg_unlock_table;
00459 plg->connection_exec = dui_libpg_connection_exec;
00460 plg->connection_tables = NULL;
00461 plg->connection_table_columns = NULL;
00462 plg->recordset_free = dui_libpg_recordset_release;
00463 plg->recordset_rewind = dui_libpg_recordset_rewind;
00464 plg->recordset_fetch_row = dui_libpg_recordset_fetch_row;
00465 plg->recordset_get_value = dui_libpg_recordset_get_value;
00466 plg->recordset_get_error = dui_libpg_recordset_get_error;
00467
00468 return plg;
00469 }
00470
00471 #endif
00472
00473
00474 static void G_GNUC_UNUSED
00475 dui_libpg_init (void)
00476 {
00477 #ifdef USE_LIBPG
00478 DuiDBPlugin *plg;
00479 plg = dui_libpg_plugin_new();
00480 dui_db_provider_register (plg);
00481 #else
00482
00483 PERR ("The DWI db drivers were compiled without Postgres support");
00484 #endif
00485 }
00486
00487