📄 main.c
字号:
/*************************************************************************** main.c PostgreSQL driver (c) 2000-2003 Beno� Minisini <gambas@users.sourceforge.net> 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 1, 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.***************************************************************************/#define __MAIN_C#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <libpq-fe.h>#include <postgres.h>#include <pg_type.h>#include "main.h"GB_INTERFACE GB;DB_INTERFACE DB;static char _buffer[32];static DB_DRIVER _driver;/*static int _print_query = FALSE;*//* Internal function to check the result of a query */static int check_result(PGresult *res, const char *err){ if (!res) { GB.Error("Out of memory"); return TRUE; } switch (PQresultStatus(res)) { case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: return FALSE; default: if (err) GB.Error(err, PQresultErrorMessage(res)); PQclear(res); return TRUE; }}/* internal function to unquote a value stored as a string */static char *unquote(char *val){ int i; unsigned char c; if (!val || *val == 0 || *val != '\'') return val; DB.Query.Init(); for (i = 1;; i++) { c = val[i]; if (!c || c == '\'') break; if (c == '\\') i++; DB.Query.AddLength(&val[i], 1); } return DB.Query.Get();}/* Internal function to convert a database type into a Gambas type */static GB_TYPE conv_type(Oid type){ switch(type) { case BOOLOID: return GB_T_BOOLEAN; case INT2OID: case INT4OID: return GB_T_INTEGER; case NUMERICOID: case FLOAT4OID: case FLOAT8OID: case INT8OID: return GB_T_FLOAT; case ABSTIMEOID: case RELTIMEOID: case DATEOID: case TIMEOID: case TIMESTAMPOID:#ifdef DATETIMEOID case DATETIMEOID:#endif#ifdef TIMESTAMPTZOID case TIMESTAMPTZOID:#endif return GB_T_DATE; case CHAROID: case BPCHAROID: case VARCHAROID: case TEXTOID: case NAMEOID: case BYTEAOID: case CASHOID: default: return GB_T_STRING; }}/* Internal function to convert a database boolean value */static int conv_boolean(const char *data){ return strcasecmp(data, "t") == 0 || strcasecmp(data, "'t'") == 0;}/* Internal function to convert a database value into a Gambas variant value */static void conv_data(char *data, GB_VARIANT_VALUE *val, Oid type){ GB_VALUE conv; GB_DATE_SERIAL date; double sec; long len; bool bc; switch (type) { case BOOLOID: val->_boolean.type = GB_T_BOOLEAN; val->_boolean.value = conv_boolean(data); break; case INT2OID: case INT4OID: GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv); val->_integer.type = GB_T_INTEGER; val->_integer.value = ((GB_INTEGER *)&conv)->value; break; case NUMERICOID: case FLOAT4OID: case FLOAT8OID: case INT8OID: GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv); val->_float.type = GB_T_FLOAT; val->_float.value = ((GB_FLOAT *)&conv)->value; break; case ABSTIMEOID: case RELTIMEOID: case DATEOID: case TIMEOID: case TIMESTAMPOID: #ifdef DATETIMEOID case DATETIMEOID: #endif #ifdef TIMESTAMPTZOID case TIMESTAMPTZOID: #endif memset(&date, 0, sizeof(date)); len = strlen(data); if (len > 3 && strcmp(&data[len - 2], "BC") == 0) bc = TRUE; else bc = FALSE; switch(type) { case ABSTIMEOID: case RELTIMEOID: case DATEOID: sscanf(data, "%4hu-%2hu-%2hu", &date.year, &date.month, &date.day); break; case TIMEOID: sscanf(data, "%2hu:%2hu:%lf", &date.hour, &date.min, &sec); date.sec = (short)sec; date.msec = (short)((sec - date.sec) * 1000 + 0.5); break; case TIMESTAMPOID: #ifdef DATETIMEOID case DATETIMEOID: #endif #ifdef TIMESTAMPTZOID case TIMESTAMPTZOID: #endif sscanf(data, "%4hu-%2hu-%2hu %2hu:%2hu:%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec); date.sec = (short)sec; date.msec = (short)((sec - date.sec) * 1000 + 0.5); break; } if (bc) date.year = (-date.year); GB.MakeDate(&date, (GB_DATE *)&conv); val->_date.type = GB_T_DATE; val->_date.date = ((GB_DATE *)&conv)->value.date; val->_date.time = ((GB_DATE *)&conv)->value.time; break; case CHAROID: case BPCHAROID: case VARCHAROID: case TEXTOID: case NAMEOID: case BYTEAOID: case CASHOID: default: //val->_string.type = GB_T_STRING; //GB.NewString(&val->_string.value, data, 0); val->_string.type = GB_T_CSTRING; val->_string.value = data; break; }}/* Internal function to substitute the table name into a query */static char *query_param[3];static void query_get_param(int index, char **str, long *len){ if (index > 3) return; index--; *str = query_param[index]; *len = strlen(*str);}/* Internal function to run a query */static int do_query(PGconn *conn, const char *error, PGresult **pres, const char *qtemp, int nsubst, ...){ va_list args; int i; const char *query; PGresult *res; int ret; if (nsubst) { va_start(args, nsubst); if (nsubst > 3) nsubst = 3; for (i = 0; i < nsubst; i++) query_param[i] = va_arg(args, char *); query = GB.SubstString(qtemp, 0, query_get_param); } else query = qtemp; if (DB.IsDebug()) { fprintf(stderr, "postgresql: %p: %s\n", conn, query); fflush(stderr); } res = PQexec(conn, query); ret = check_result(res, error); if (!ret) { if (pres) *pres = res; else PQclear(res); } return ret;}/* Internal function to check database version number */long db_version(PGconn *conn){ //Check db version const char *vquery = "select substring(version(),12,5)"; long dbversion =0; PGresult *res; if (!do_query(conn, NULL, &res, vquery, 0)) { unsigned int verMain, verMajor, verMinor; sscanf(PQgetvalue(res, 0, 0),"%2u.%2u.%2u", &verMain, &verMajor, &verMinor); dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor); PQclear(res); } return dbversion;}/***************************************************************************** get_quote() Returns the character used for quoting object names.*****************************************************************************/static char *get_quote(void){ return QUOTE_STRING;}/***************************************************************************** open_database() Connect to a database. <desc> points at a structure describing each connection parameter. This function must return a database handle, or NULL if the connection has failed. The name of the database can be NULL, meaning a default database.*****************************************************************************/static DB_DATABASE open_database(DB_DESC *desc, char **charset){ const char *query = "show client_encoding"; PGconn *conn; PGresult *res; int status; char *name; if (desc->name) name = desc->name; else name = "template1"; conn = PQsetdbLogin(desc->host, desc->port, NULL, NULL, name, desc->user, desc->password); if (!conn) { GB.Error("Out of memory"); return NULL; } if (PQstatus(conn) == CONNECTION_BAD) { GB.Error("Cannot open database: &1", PQerrorMessage(conn)); PQfinish(conn); return NULL; } res = PQexec(conn, "set datestyle=ISO"); status = PQresultStatus(res); if (status != PGRES_COMMAND_OK) { GB.Error("Cannot set datestyle to ISO: &1", PQerrorMessage(conn)); PQclear(res); PQfinish(conn); return NULL; } /* encoding */ if (PQsetClientEncoding(conn, GB.System.Charset())) fprintf(stderr, "WARNING: cannot set encoding to %s\n", GB.System.Charset()); if (!do_query(conn, NULL, &res, query, 0)) { GB.NewString(charset, PQgetvalue(res, 0, 0), 0); PQclear(res); } else *charset = NULL; /* get version */ desc->version = db_version(conn); return (DB_DATABASE)conn;}/***************************************************************************** close_database() Terminates the database connection. <handle> contains the database handle.*****************************************************************************/static void close_database(DB_DATABASE handle){ PGconn *conn = (PGconn *)handle; if (conn) PQfinish(conn);}/***************************************************************************** format_value() This function transforms a gambas value into a string value that can be inserted into a SQL query. <arg> points to the value. <add> is a callback called to insert the string into the query. This function must return TRUE if it translates the value, and FALSE if it does not. If the value is not translated, then a default translation is used.*****************************************************************************/static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add){ int l; GB_DATE_SERIAL *date; bool bc; switch (arg->type) { case GB_T_BOOLEAN: if (VALUE((GB_BOOLEAN *)arg)) add("TRUE", 4); else add("FALSE", 5); return TRUE; case GB_T_STRING: case GB_T_CSTRING: return FALSE; case GB_T_DATE: date = GB.SplitDate((GB_DATE *)arg); bc = date->year < 0; l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d", abs(date->year), date->month, date->day, date->hour, date->min, date->sec); add(_buffer, l); if (date->msec) { l = sprintf(_buffer, ".%03d", date->msec); add(_buffer, l); } if (bc) add(" BC", 3); add("'", 1); return TRUE; default: return FALSE; }}/***************************************************************************** exec_query() Send a query to the server and gets the result. <handle> is the database handle, as returned by open_database() <query> is the query string. <result> will receive the result handle of the query. <err> is an error message used when the query failed. <result> can be NULL, when we don't care getting the result.*****************************************************************************/static int exec_query(DB_DATABASE handle, char *query, DB_RESULT *result, char *err){ return do_query((PGconn *)handle, err, (PGresult **)result, query, 0);}/***************************************************************************** query_init() Initialize an info structure from a query result. <result> is the handle of the query result. <info> points to the info structure. <count> will receive the number of records returned by the query. This function must initialize the info->nfield field with the number of field in the query result.*****************************************************************************/static void query_init(DB_RESULT result, DB_INFO *info, int *count){ PGresult *res = (PGresult *)result;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -