This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
hedera/plugin/pg/db-pg.c

881 lines
21 KiB
C
Raw Normal View History

2013-10-11 23:07:35 +00:00
/*
* Copyright (C) 2012 - Juan Ferrer Toribio
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libpq-fe.h>
#include <postgres_fe.h>
#include <catalog/pg_type.h>
// Macros to avoid redefinition warnings for constants
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#undef _
#include <stdlib.h>
#include <string.h>
#include <db/db-row.h>
#include "db-pg.h"
G_DEFINE_TYPE (DbPg, db_pg, DB_TYPE_PLUGIN);
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
static void db_pg_close (DbPlugin * obj)
{
DbPg * pg = DB_PG (obj);
if (pg->pg_cancel)
{
PQfreeCancel (pg->pg_cancel);
pg->pg_cancel = NULL;
}
if (pg->conn)
{
PQfinish (pg->conn);
pg->conn = NULL;
pg->socket = -1;
}
}
static gboolean db_pg_open (DbPlugin * obj, const gchar * host,
const gchar * schema, const gchar * user, const gchar * pass, GError ** err)
{
DbPg * pg = DB_PG (obj);
pg->conn = PQsetdbLogin (
host
,NULL
,NULL
,NULL
,schema
,user
,pass
);
if (!pg->conn)
{
g_set_error (err
,DB_CONN_LOG_DOMAIN
,DB_CONN_ERROR_OPENING
,_("Can't allocate the needed memory")
);
return FALSE;
}
if (PQstatus (pg->conn) != CONNECTION_OK)
{
g_set_error (err
,DB_CONN_LOG_DOMAIN
,DB_CONN_ERROR_OPENING
,"%s", PQerrorMessage (pg->conn)
);
db_pg_close (obj);
return FALSE;
}
pg->pg_cancel = PQgetCancel (pg->conn);
pg->socket = PQsocket (pg->conn);
return TRUE;
}
static void db_pg_set_ssl (DbPg * obj, const gchar * ca)
{
g_warning ("DbPg: SSL not supported by this plugin");
}
//+++++++ Internal Methods for db_pg_query ()
static GType db_pg_get_g_type (Oid type)
{
switch (type)
{
case BOOLOID:
return G_TYPE_BOOLEAN;
case INT8OID:
return G_TYPE_INT64;
case INT2OID:
case INT4OID:
return G_TYPE_INT;
case TIDOID:
case OIDOID:
return G_TYPE_UINT;
case FLOAT4OID:
return G_TYPE_FLOAT;
case FLOAT8OID:
case NUMERICOID:
return G_TYPE_DOUBLE;
case DATEOID:
return G_TYPE_DATE;
case TIMESTAMPOID:
case TIMESTAMPTZOID:
return G_TYPE_DATE_TIME;
case TIMEOID:
case TIMETZOID:
return GVN_TYPE_TIME;
case BYTEAOID:
return G_TYPE_BYTES;
case ABSTIMEOID:
case RELTIMEOID:
case TINTERVALOID:
case CHAROID:
case TEXTOID:
case NAMEOID:
case XMLOID:
case CSTRINGOID:
case BPCHAROID:
case VARCHAROID:
default:
return G_TYPE_STRING;
}
}
static void db_pg_set_date_value (GValue * val, Oid oid, gchar * pg_val)
{
gpointer date = NULL;
switch(oid)
{
case TIMEOID: // hh:mm:ss
{
gchar ** hms = g_strsplit (pg_val, ":", 3);
if (g_strv_length (hms) >= 3)
date = gvn_time_new (atoi (hms[0]), atoi (hms[1]),
atoi (hms[2]), 0);
g_value_init (val, GVN_TYPE_TIME);
g_strfreev (hms);
break;
}
case TIMETZOID: // hh:mm:ss±zz
{
gchar ** hmsz = g_strsplit_set (pg_val, ":+-", 4);
if (g_strv_length (hmsz) >= 4)
date = gvn_time_new (atoi (hmsz[0]), atoi(hmsz[1]),
atoi(hmsz[2]), atoi (hmsz[3]));
g_value_init (val, GVN_TYPE_TIME);
g_strfreev (hmsz);
break;
}
case TIMESTAMPOID: // yyyy-mm-dd hh:mn:ss
{
gchar ** dt = g_strsplit_set (pg_val, " -:", 6);
if (g_strv_length (dt) >= 6)
date = g_date_time_new_local
(atoi (dt[0]), atoi (dt[1]), atoi (dt[2]),
atoi (dt[3]), atoi (dt[4]), atoi (dt[5]));
g_value_init (val, G_TYPE_DATE_TIME);
g_strfreev (dt);
break;
}
case TIMESTAMPTZOID: // yyyy-mm-dd hh:mm:ss±zz
{
GTimeZone * tz = NULL;
gchar ** dtz = g_strsplit_set (pg_val, " -:+", 7);
if (strlen (pg_val) >= 16 && g_strv_length (dtz) >= 6)
date = g_date_time_new
(tz = g_time_zone_new (pg_val + 16),
atoi (dtz[0]), atoi (dtz[1]), atoi (dtz[2]),
atoi (dtz[3]), atoi (dtz[4]), atoi (dtz[5]));
g_value_init (val, G_TYPE_DATE_TIME);
g_strfreev (dtz);
g_time_zone_unref (tz);
break;
}
case DATEOID: // yyyy-mm-dd
{
gchar ** ymd = g_strsplit (pg_val, "-", 3);
if (g_strv_length (ymd) >= 3)
date = g_date_new_dmy
(atoi (ymd[2]), atoi (ymd[1]) ,atoi (ymd[0]));
g_value_init (val, G_TYPE_DATE);
g_strfreev (ymd);
break;
}
case TINTERVALOID:
// TODO If needed
break;
}
g_value_take_boxed (val, date);
}
static void db_pg_set_g_value (GValue * val, GType type, gchar * pg_val)
{
val = g_value_init (val, type);
switch (type)
{
case G_TYPE_BOOLEAN:
if(!g_strcmp0(pg_val, "t"))
g_value_set_boolean (val, TRUE);
else
g_value_set_boolean (val, FALSE);
break;
case G_TYPE_UINT:
g_value_set_uint(val, (guint) g_ascii_strtoull (pg_val, NULL, 10));
break;
case G_TYPE_INT:
g_value_set_int (val, atoi (pg_val));
break;
case G_TYPE_INT64:
g_value_set_int64 (val, g_ascii_strtoll (pg_val, NULL, 10));
break;
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
g_value_set_double (val, g_strtod (pg_val, NULL));
break;
case G_TYPE_STRING:
g_value_set_string (val, pg_val);
break;
default:
if (type == G_TYPE_BYTES)
{
gsize l;
guchar * esc = PQunescapeBytea ((guchar *) pg_val, &l);
GBytes * bin = g_bytes_new (esc, l);
g_free (esc);
g_value_set_boxed (val, bin);
g_bytes_unref (bin);
}
}
}
static inline gint added (GSList * list, const gchar * target)
{
gint r = 0;
GSList * n;
for (n = list; n; n = n->next, r++)
if (!g_strcmp0 ((gchar *) n->data, target))
return r;
return -1;
}
static inline void free_array (const gpointer array)
{
g_ptr_array_free (array, TRUE);
}
static DbResultSet * __db_pg_query
(DbPlugin * obj, const gchar * sql, gboolean columns, GError ** err)
{
DbPg * pg = DB_PG (obj);
PGresult * res = NULL;
DbResult * result;
DbResultSet * set;
gint i = 0, j = 0;
gint ntup = 0, ncol = 0;
guint error_code = 0;
guint query_count = 0;
guint sel_count = 0;
GSList * list = NULL;
gboolean failed = FALSE;
guint s = 5;
GPtrArray * ind_select = g_ptr_array_sized_new (s);
GPtrArray * types = g_ptr_array_new_full (s, (GDestroyNotify) g_free);
GPtrArray * oid_types = g_ptr_array_new_full (s, (GDestroyNotify) g_free);
GPtrArray * rel_oid = g_ptr_array_new_full (s, (GDestroyNotify) free_array);
GPtrArray * col = g_ptr_array_new_full (s,(GDestroyNotify) free_array);
GPtrArray * name = g_ptr_array_new_full (s,(GDestroyNotify) free_array);
set = db_result_set_new ();
if (PQsendQuery (pg->conn, sql) != 1)
{
g_set_error (err
,DB_CONN_LOG_DOMAIN
,DB_CONN_ERROR_QUERY_FAILED
,"%s", PQerrorMessage (pg->conn)
);
failed = TRUE;
}
else while ((res = PQgetResult (pg->conn)))
{
result = g_new (DbResult, 1);
result->data = NULL;
result->column = NULL;
result->nrows = 0;
result->ncols = 0;
switch (PQresultStatus (res))
{
case PGRES_COMMAND_OK:
case PGRES_TUPLES_OK:
{
gchar ** q_t = NULL;
q_t = g_strsplit (PQcmdStatus (res), " ", G_MAXINT);
if (!g_strcmp0 (q_t[0],"SELECT"))
{
ntup = PQntuples (res);
result->nrows = ntup;
ncol = PQnfields (res);
result->ncols = ncol;
gchar * pg_val;
GPtrArray * row = g_ptr_array_new_full
((guint) ntup + 15, (GDestroyNotify) db_row_free);
GPtrArray * oid = g_ptr_array_sized_new ((guint) ncol);
GPtrArray * col_num = g_ptr_array_sized_new ((guint) ncol);
GType * type = g_new (GType, ncol);
Oid * type_oid = g_new (Oid, ncol);
GPtrArray * col_name = g_ptr_array_new_full ((guint) ncol, (GDestroyNotify) g_free);
sel_count++;
for (j = 0; j < ncol; j++)
{
type_oid[j] = PQftype (res, j);
type[j] = db_pg_get_g_type (type_oid[j]);
g_ptr_array_add (col_num, GINT_TO_POINTER (PQftablecol (res, j)));
g_ptr_array_add (oid, GUINT_TO_POINTER (PQftable (res, j)));
g_ptr_array_add (col_name, g_strdup (PQfname (res, j)));
}
if (ntup > 0)
for (i = 0; i < ntup; i++)
{
DbRow * r = db_row_new (result->ncols, i);
for (j = 0; j < ncol; j++)
{
if (!PQgetisnull (res, i, j))
{
pg_val = PQgetvalue (res, i, j);
if (type[j] == G_TYPE_DATE
|| type[j] == G_TYPE_DATE_TIME
|| type[j] == GVN_TYPE_TIME)
db_pg_set_date_value (&(r->value)[j],
type_oid[j], pg_val);
else
db_pg_set_g_value (&(r->value)[j], type[j], pg_val);
}
else // IF NULL: GvnNull type
g_value_init (&(r->value[j]), GVN_TYPE_NULL);
}
g_ptr_array_add (row, r);
}
result->data = row;
g_ptr_array_add (rel_oid, oid);
g_ptr_array_add (col, col_num);
g_ptr_array_add (name, col_name);
g_ptr_array_add (types, type);
g_ptr_array_add (oid_types, type_oid);
g_ptr_array_add (ind_select, GUINT_TO_POINTER (query_count));
}
if (!g_strcmp0 (q_t[0], "INSERT")
|| !g_strcmp0 (q_t[0], "UPDATE")
|| !g_strcmp0 (q_t[0], "DELETE"))
result->nrows = atoi (PQcmdTuples (res));
g_strfreev (q_t);
break;
}
case PGRES_BAD_RESPONSE:
error_code = DB_CONN_ERROR_BAD_RESPONSE;
break;
case PGRES_EMPTY_QUERY:
error_code = DB_CONN_ERROR_QUERY_EMPTY;
break;
case PGRES_COPY_OUT:
case PGRES_COPY_IN:
case PGRES_COPY_BOTH:
case PGRES_NONFATAL_ERROR:
error_code = DB_CONN_ERROR_QUERY_NONFATAL;
break;
case PGRES_FATAL_ERROR:
error_code = DB_CONN_ERROR_QUERY_FATAL;
break;
} // switch STATUS
if (error_code && !failed)
{
PGresult * discarded_res;
while ((discarded_res = PQgetResult (pg->conn)))
PQclear (discarded_res);
g_set_error (err
,DB_CONN_LOG_DOMAIN
,error_code
,"%s", PQresultErrorMessage (res)
);
failed = TRUE;
}
query_count++;
list = g_slist_prepend (list, result);
PQclear (res);
} // while (res) END
if (err && *err && PQstatus (pg->conn) != CONNECTION_OK)
(*err)->code = DB_CONN_ERROR_LOST;
list = g_slist_reverse (list);
if (!failed && sel_count > 0 && columns)
{
gint k;
Oid t_oid;
GPtrArray * col_num = NULL;
GPtrArray * oid = NULL;
gchar * info_query = g_strdup ("");
gchar * pkey_query;
gchar * pkey_buff = NULL // buffers for memory leak avoidance
,* info_buff = NULL;
// Set info_query string using the previously stored table OIDs and column numbers
for (i = 0; i < (gint) sel_count; i++)
{
pkey_query = g_strdup ("");
oid = g_ptr_array_index (rel_oid, i);
col_num = g_ptr_array_index (col, i);
info_buff = info_query;
info_query = g_strconcat (info_query,
"SELECT attname, attrelid, attnotnull, adsrc, attnum "
"FROM pg_attribute "
"INNER JOIN pg_class c ON c.oid = attrelid "
"LEFT JOIN pg_attrdef a "
"ON a.adrelid = attrelid AND a.adnum = attnum "
"WHERE attisdropped = false AND (c.oid, attnum) IN ("
, NULL);
g_free (info_buff);
pkey_buff = pkey_query;
pkey_query = g_strconcat (pkey_query,
"SELECT c.oid, relname, indkey "
"FROM pg_class c "
"LEFT JOIN pg_index i ON indrelid = c.oid "
"AND indisprimary "
"WHERE c.oid IN ("
, NULL);
g_free (pkey_buff);
gint ncolumns = (gint) oid->len;
gchar * buff_j = NULL;
for (j = 0; j < ncolumns; j++)
{
t_oid = GPOINTER_TO_UINT (g_ptr_array_index (oid, j));
buff_j = g_strdup_printf ("%d", t_oid);
pkey_buff = pkey_query;
pkey_query = g_strconcat (pkey_query, buff_j, NULL);
g_free (pkey_buff);
g_free (buff_j);
if (j < ncolumns-1 && ncolumns > 1)
{
pkey_buff = pkey_query;
pkey_query = g_strconcat (pkey_query, ",", NULL);
g_free (pkey_buff);
}
buff_j = g_strdup_printf ("(%d,%d)"
,t_oid
,GPOINTER_TO_INT (g_ptr_array_index (col_num, j)));
info_buff = info_query;
info_query = g_strconcat (info_query, buff_j, NULL);
g_free (buff_j);
g_free (info_buff);
info_buff = info_query;
if (j < ncolumns-1 && ncolumns > 1)
info_query = g_strconcat (info_query, ",", NULL);
else
info_query = g_strconcat (info_query, ")", NULL);
g_free (info_buff);
}
pkey_buff = pkey_query;
pkey_query = g_strconcat (pkey_query, ");", NULL);
g_free (pkey_buff);
info_buff = info_query;
info_query = g_strconcat (info_query, ";", NULL);
g_free (info_buff);
info_buff = info_query;
info_query = g_strconcat (info_query, pkey_query, NULL);
g_free (info_buff);
g_free (pkey_query);
}
PGresult * res_col;
PQsendQuery (pg->conn, info_query);
DbResult * r = NULL;
GValue * def = NULL;
Oid * tab_oid = NULL;
gboolean * nullable = NULL;
guint * col_n = NULL;
i = 0;
while ((res_col = PQgetResult (pg->conn)) && !failed)
{
if (PQresultStatus (res_col) != PGRES_TUPLES_OK)
{
g_set_error (err
,DB_CONN_LOG_DOMAIN
,DB_CONN_ERROR_QUERY_FAILED
,"%s", PQresultErrorMessage (res_col)
);
failed = TRUE;
}
else
{
guint ind = i/2;
ntup = PQntuples (res_col);
guint x = i%2;
if (!x)
{
GPtrArray * col_iter = g_ptr_array_index (col, ind)
,* rel_iter = g_ptr_array_index (rel_oid, ind)
,* name_array;
r = g_slist_nth_data
(list, GPOINTER_TO_UINT (g_ptr_array_index (ind_select, ind)));
ncol = r->ncols;
r->column = g_new (DbColumn, ncol);
// Relates each column with its corresponding row in the aux query
gint col_tup[ncol];
// Array of the default values for the columns
def = g_new0 (GValue, ncol);
// Array of GvnParamSpecFlags for the columns
nullable = g_new (gboolean, ncol);
tab_oid = g_new (Oid, ncol);
col_n = g_new (guint, ncol);
for (j = 0; j < ncol; j++)
{
r->column[j].spec = NULL;
r->column[j].info = 0;
r->column[j].name = NULL;
r->column[j].table = NULL;
if (GPOINTER_TO_INT (g_ptr_array_index (col_iter, j)) == 0)
{
gchar * fname;
name_array = g_ptr_array_index (name, ind);
fname = g_strdup (g_ptr_array_index (name_array, j));
// Set the metadata if it is a *CALCULATED FIELD*
col_tup[j] = -1;
if (fname[0] == '?')
{
r->column[j].name = g_strdup ("");
g_free (fname);
}
else
r->column[j].name = fname;
r->column[j].display = g_strdup (r->column[j].name);
r->column[j].table = g_strdup ("");
r->column[j].spec = gvn_param_spec_new_with_attrs
(((GType*) g_ptr_array_index (types, ind))[j]
, FALSE, FALSE, NULL);
}
else
for (k = 0; k < ntup; k++)
if (GPOINTER_TO_INT (g_ptr_array_index (col_iter, j))
== atoi (PQgetvalue (res_col, k, 4))
&& GPOINTER_TO_INT (g_ptr_array_index (rel_iter, j))
== atoi (PQgetvalue (res_col, k, 1)))
{
col_tup[j] = k;
break;
}
if (col_tup[j] >= 0) // NOT a calculated field.
{
gint ctup = col_tup[j];
GType type = ((GType*) g_ptr_array_index (types, ind))[j];
Oid oid = ((Oid*) g_ptr_array_index (oid_types, ind))[j];
gchar * fname = PQgetvalue (res_col, ctup, 0);
gchar * fdisp;
name_array = g_ptr_array_index (name, ind);
fdisp = g_ptr_array_index (name_array, j);
r->column[j].name = g_strdup (fname);
r->column[j].display = g_strdup (fdisp);
// Getting the default value from res_col //FIXME use the parser
if (!PQgetisnull (res_col, ctup, 3))
{
gchar * pg_val = PQgetvalue (res_col, ctup, 3);
if (type == G_TYPE_STRING || type == G_TYPE_CHAR
|| type == G_TYPE_BYTES || type == G_TYPE_DATE
|| type == G_TYPE_DATE_TIME || type == GVN_TYPE_TIME)
{
gchar ** split = NULL;
if (type == G_TYPE_DATE
|| type == G_TYPE_DATE_TIME
|| type == GVN_TYPE_TIME)
db_pg_set_date_value (&def[j], oid
,(split = g_strsplit
(pg_val, "'", G_MAXINT))[1]);
else
db_pg_set_g_value (&def[j], type
,(split = g_strsplit
(pg_val, "'", G_MAXINT))[1]);
g_strfreev (split);
}
else
{
if (g_str_has_prefix (pg_val, "nextval"))
{// Serial fields
GValue * v = g_new0 (GValue, 1);
gchar ** split = g_strsplit_set (pg_val, "(':)", G_MAXINT8);
SqlObject * function = sql_function_new ("currval", NULL);
g_value_set_string (g_value_init (v, G_TYPE_STRING), split[2]);
sql_object_add_child (function, "params", sql_value_new_with_value (v));
g_value_unset (v);
g_free (v);
g_value_take_object (g_value_init (&def[j], SQL_TYPE_FUNCTION),
g_object_ref_sink (function));
g_strfreev (split);
}
else
db_pg_set_g_value (&def[j], type, pg_val);
}
}
else
g_value_init (&def[j], GVN_TYPE_NULL);
// Checking whether the column can be NULL
nullable[j] = !g_strcmp0 (PQgetvalue (res_col, ctup, 2),"t")
? FALSE : TRUE;
tab_oid[j] = atoi (PQgetvalue (res_col, ctup, 1));
r->column[j].info = 0;
col_n[j] = atoi (PQgetvalue (res_col, ctup, 4));
}
}
}
else
{
guint nkeys;
gchar ** pkey = NULL;
GSList * prev_tables = NULL;
guint nedit = 0, l;
struct
{
gchar * name;
guint keys[ncol];
guint num;
guint total;
}
edit[ntup];
for (j = 0; j < ncol; j++)
{
GType type;
for (k = 0; k < ntup; k++)
if (tab_oid[j] == atoi (PQgetvalue (res_col, k, 0)))
{
guint n;
if (!r->column[j].table)
r->column[j].table =
g_strdup (PQgetvalue (res_col, k, 1));
g_strfreev (pkey);
pkey = g_strsplit (PQgetvalue (res_col, k, 2), " ", G_MAXINT);
nkeys = g_strv_length (pkey);
for (n = 0; n < nkeys; n++)
if (atoi (pkey[n]) == col_n[j])
{
gint pos = added (prev_tables,
r->column[j].table);
if (pos >= 0)
{
edit[pos].keys[edit[pos].num] = j;
edit[pos].num++;
}
else
{
edit[nedit].name = r->column[j].table;
edit[nedit].keys[0] = j;
edit[nedit].num = 1;
edit[nedit].total = nkeys;
nedit++;
prev_tables = g_slist_append
(prev_tables, r->column[j].table);
}
}
}
type = ((GType *) g_ptr_array_index (types, ind))[j];
if (!r->column[j].spec)
r->column[j].spec = gvn_param_spec_new_with_attrs
(type, FALSE, nullable[j], &def[j]);
if (G_IS_VALUE (&def[j]))
g_value_unset (&def[j]);
}
for (j = 0; j < ncol; j++)
for (k = 0; k < nedit; k++)
if (edit[k].num == edit[k].total
&& !g_strcmp0 (r->column[j].table, edit[k].name))
for (l = 0; l < edit[k].num; l++)
r->column[edit[k].keys[l]].info |= DB_COLUMN_PRI_KEY;
g_free (nullable);
g_free (def);
g_free (col_n);
g_free (tab_oid);
g_strfreev (pkey);
g_slist_free (prev_tables);
}
}
i++;
PQclear (res_col);
}
g_free (info_query);
PQclear (res_col);
}
free_array (ind_select);
free_array (types);
free_array (oid_types);
free_array (rel_oid);
free_array (name);
free_array (col);
set->results = list;
if (failed)
{
db_result_set_free (set);
return NULL;
}
return set;
}
static DbResultSet * db_pg_query (DbPlugin * obj, const gchar * sql, GError ** err)
{
return __db_pg_query (obj, sql, TRUE, err);
}
static void db_pg_kill_query (DbPg * obj)
{
if (obj->conn)
{
gboolean killed = FALSE;
if (obj->pg_cancel)
{
gchar errbuf[256] = "";
if (PQcancel (obj->pg_cancel, errbuf, 256) == 1)
killed = TRUE;
else
g_warning ("DbPg: %s", errbuf);
}
if (!killed && obj->socket != -1 && !shutdown (obj->socket, SHUT_RDWR))
obj->socket = -1;
}
}
static void db_pg_value_render (SqlValue * obj, SqlRender * render)
{
if (G_VALUE_TYPE (obj->value) == G_TYPE_BYTES)
{
gsize len;
gchar * buffer;
GBytes * bin = g_value_get_boxed (obj->value);
buffer = (gchar *) PQescapeByteaConn (
DB_PG (render->data)->conn
,g_bytes_get_data (bin, NULL)
,g_bytes_get_size (bin)
,&len
);
sql_render_printf (render, "E'%s'::bytea", buffer);
PQfreemem (buffer);
}
else
sql_object_render (SQL_OBJECT (obj), render);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
static void db_pg_init (DbPg * obj)
{
SqlRender * render = sql_render_new ('"');
sql_render_register_function (render, SQL_TYPE_VALUE,
(SqlRenderFunc) db_pg_value_render);
DB_PLUGIN (obj)->render = render;
obj->conn = NULL;
obj->socket = -1;
}
static void db_pg_class_init (DbPgClass * k)
{
DbPluginClass * klass = DB_PLUGIN_CLASS (k);
klass->open = db_pg_open;
klass->close = db_pg_close;
klass->set_ssl = (DbPluginSetSSL) db_pg_set_ssl;
klass->query = db_pg_query;
klass->kill_query = (DbPluginKillQueryFunc) db_pg_kill_query;
}