881 lines
21 KiB
C
881 lines
21 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|