Skip to content
Snippets Groups Projects
Commit e4bc343b authored by Francesco Chemolli's avatar Francesco Chemolli Committed by Henrik (Grubba) Grubbström
Browse files

Postgres 0.5

Rev: src/modules/Postgres/ChangeLog:1.1.1.1
Rev: src/modules/Postgres/Makefile.in:1.1.1.1
Rev: src/modules/Postgres/configure.in:1.1.1.1
Rev: src/modules/Postgres/extras/test_notify.pike:1.1.1.1
Rev: src/modules/Postgres/extras/test_schema.pike:1.1.1.1
Rev: src/modules/Postgres/pg_types.h:1.1.1.1
Rev: src/modules/Postgres/pgres_config.h.in:1.1.1.1
Rev: src/modules/Postgres/pgresult.c:1.1.1.1
Rev: src/modules/Postgres/pgresult.h:1.1.1.1
Rev: src/modules/Postgres/postgres.c:1.1.1.1
Rev: src/modules/Postgres/quickmanual.txt:1.1.1.1
Rev: src/modules/Postgres/testsuite.in:1.1.1.1
parent 25ce50da
Branches branches/kinkie
Tags msqlmod/1.1pre6 postgres/0.5
No related merge requests found
...@@ -113,6 +113,10 @@ testfont binary ...@@ -113,6 +113,10 @@ testfont binary
/src/modules/Oracle/oracle.c foreign_ident /src/modules/Oracle/oracle.c foreign_ident
/src/modules/Pipe/acconfig.h foreign_ident /src/modules/Pipe/acconfig.h foreign_ident
/src/modules/Pipe/pipe.c foreign_ident /src/modules/Pipe/pipe.c foreign_ident
/src/modules/Postgres/Makefile.in foreign_ident
/src/modules/Postgres/configure.in foreign_ident
/src/modules/Postgres/pgres_config.h.in foreign_ident
/src/modules/Postgres/postgres.c foreign_ident
/src/modules/Regexp/acconfig.h foreign_ident /src/modules/Regexp/acconfig.h foreign_ident
/src/modules/Ssleay/ssleay.c foreign_ident /src/modules/Ssleay/ssleay.c foreign_ident
/src/modules/Yp/acconfig.h foreign_ident /src/modules/Yp/acconfig.h foreign_ident
......
Tue Jul 8 20:23:06 1997 Francesco Chemolli <kinkie@ai-chan>
* postgres.c:
Corrected a small bug which manifested itself when building in a
non_threaded environment.
Mon Jun 30 13:57:08 1997 Francesco Chemolli <kinkie@ai-chan>
* configure.in, postgres.c, configure:
More configure.in fixes and postgres/6.1 compatibility issues addressed
* configure: *** empty log message ***
* configure.in:
Corretto un leggero baco nella versione aggiornata (un path sballato)
* configure.in, configure:
Razionalizzato configure.in, ora calcola il reasonable searchpath facendo
il merge di un insieme di directories. Dovrebbe essere impercettibilmente piu`
lento ma molto piu` flessibile e semplice da mantenere. Penso che esportero`
il meccanismo anche verso altri moduli.
Fri Jun 27 09:55:51 1997 Francesco Chemolli <kinkie@ai-chan>
* quickmanual.txt: Quickmanual.txt: aggiornata la documentazione
* lib/postgres.pike: Postgres.pike:
- modificato in modo da gestire l'interfaccia con meno argomenti di
Postgres.postgres
- modificato il costruttore: invece che
create(host,db,user,pass,port) (tutti facoltativi)
e`:
create([host[:port]],[db],[user],[pass])
Questo permette di funzionare al modulo SQL generico senza grossi problemi
* postgres.c:
Postgres.c: modificato in modo da prendere solo tre argomenti.
Tanto non e` interface-compliant in ogni caso, tanto vale fare una cosa
proprietaria ma semplice. Ci pensera` poi il modulo di interfaccia a
gestire la traduzione e a ottenere la compliancy.
Thu Jun 19 12:35:12 1997 Francesco Chemolli <kinkie@ai-chan>
* lib/postgres.pike: Corretto un baco in lib/postgres.pike
* pgresult.c: *** empty log message ***
* pgresult.h: Aggiunto pgresult.h
* dependencies: Repackaging minore.
* lib/postgres_result.pike, lib/postgres.h, lib/postgres.pike, configure.in, pgresult.c, postgres.c, Makefile.in, configure:
Aggiornato a pike/0.5b8
Ora la classe in Sql.pmod puo` ereditare. Piu` efficiente, e migliore
controllo degli errori.
Wed Apr 2 11:56:14 1997 Francesco Chemolli <kinkie@ai-chan>
* Makefile.in, configure, configure.in, extras/test_notify.pike, extras/test_schema.pike, lib/postgres.h, pg_types.h, pgres_config.h.in, pgresult.c, postgres.c, quickmanual.txt, testsuite.in:
Initial revision
* Makefile.in, configure, configure.in, extras/test_notify.pike, extras/test_schema.pike, lib/postgres.h, pg_types.h, pgres_config.h.in, pgresult.c, postgres.c, quickmanual.txt, testsuite.in:
Initial version of the Postgres module for Pike.
Apparently working.
# $Id: Makefile.in,v 1.1.1.1 1997/10/14 22:07:21 grubba Exp $
# (C) 1997 Francesco Chemolli <kinkie@comedia.it>
SRCDIR=@srcdir@
VPATH=@srcdir@:@srcdir@/../..:../..
MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@
MODULE_LDFLAGS=@LDFLAGS@ @LIBS@
OBJS=postgres.o pgresult.o
POSTGRES_SUPPORTED=@POSTGRES_SUPPORTED@
all: module_install
module_install: include_install glue_install mod_install
include_install: lib/postgres.h
if [ x$(POSTGRES_SUPPORTED) = xyes ]; then cp $^ $(TMP_LIBDIR)/include; fi
glue_install: lib/postgres.pike lib/postgres_result.pike
if [ x$(POSTGRES_SUPPORTED) = xyes ]; then cp $^ $(TMP_LIBDIR)/modules/Sql.pmod; fi
mod_install:
if [ x$(POSTGRES_SUPPORTED) = xyes ] ; then make dummy; fi
@dynamic_module_makefile@
spotless: clean
-rm config.* pgres_config.h
@dependencies@
dnl $Id: configure.in,v 1.1.1.1 1997/10/14 22:07:21 grubba Exp $
dnl (C) 1997 Francesco Chemolli <kinkie@comedia.it>
AC_INIT(postgres.c)
AC_CONFIG_HEADER(pgres_config.h)
echo "Configuring Postgres module, (C) 1997 Francesco Chemolli <kinkie@comedia.it>"
AC_ARG_WITH(postgres,[ --with(out)-postgres postgres95 DB server support],
[],[with_postgres=yes])
sinclude(../module_configure.in)
ac_pike_postgres_skip=no
dnl set up "reasonable" search paths
pike_postgres_reasonable_prefixes="/usr/local /usr /opt"
pike_postgres_reasonable_directories="postgres95 postgres pgsql /"
pike_postgres_extra_include_directories="$HOME/include"
pike_postgres_extra_lib_directories="$HOME/lib"
for a in $pike_postgres_reasonable_prefixes
do
for b in $pike_postgres_reasonable_directories
do
pike_postgres_reasonable_include_searchpath="$pike_postgres_reasonable_include_searchpath $a/$b/include"
pike_postgres_reasonable_lib_searchpath="$pike_postgres_reasonable_lib_searchpath $a/$b/lib"
done
done
pike_postgres_reasonable_include_searchpath="$pike_postgres_reasonable_include_searchpath $pike_postgres_extra_include_directories"
pike_postgres_reasonable_lib_searchpath="$pike_postgres_reasonable_lib_searchpath $pike_postgres_extra_lib_directories"
dnl start actual work
if test x$with_postgres = xyes; then
OLD_CPPFLAGS=$CPPFLAGS
OLD_LIBS=$LIBS
OLD_LDFLAGS=$LDFLAGS
AC_MSG_CHECKING(for location of the Postgres include files)
AC_CACHE_VAL(pike_cv_pgres_include_dir, [
for pike_cv_pgres_include_dir in $pike_postgres_reasonable_include_searchpath no
do
if test -f $pike_cv_pgres_include_dir/postgres.h; then
break
fi
done
])
if test x$pike_cv_pgres_include_dir != xno; then
AC_MSG_RESULT(found.)
else
AC_MSG_RESULT(not found.)
fi
if test x$pike_cv_pgres_include_dir != xno; then
CPPFLAGS="$CPPFLAGS -I$pike_cv_pgres_include_dir"
fi
AC_CHECK_HEADERS(postgres.h)
AC_CHECK_HEADERS(libpq-fe.h)
if test x$ac_cv_header_libpq_fe_h = xno ; then
ac_cv_header_postgres_h=no
ac_pike_postgres_skip=yes
fi
if test x$ac_pike_postgres_skip = xno; then
AC_MSG_CHECKING(for location of the Postgres library)
AC_CACHE_VAL(pike_cv_pgres_lib_dir,[
for pike_cv_pgres_lib_dir in $pike_postgres_reasonable_lib_searchpath no
do
if test -f $pike_cv_pgres_lib_dir/libpq.a; then
break
fi
done
])
if test x$pike_cv_pgres_lib_dir != xno; then
AC_MSG_RESULT(found.)
else
AC_MSG_RESULT(not found.)
fi
if test x$pike_cv_pgres_lib_dir != xno; then
LDFLAGS="$LDFLAGS -L$pike_cv_pgres_lib_dir"
fi
AC_CHECK_LIB(pq,PQclear)
fi
fi
if test x$ac_cv_lib_pq_PQclear != xyes; then
CPPFLAGS=$OLD_CPPFLAGS
LDFLAGS=$OLD_LDFLAGS
LIBS=$OLD_LIBS
echo "Postgres not found. We won't support it."
else
echo "Postgres found, module configured. Thank you for your collaboration."
POSTGRES_SUPPORTED="yes"
fi
AC_SUBST(POSTGRES_SUPPORTED)
AC_OUTPUT(Makefile)
#include <postgres.h>
/* This simple program connects to the template1 database "template1"
* (it comes default with postgres95) and sits there waiting for
* someone to issue a notify sql command on a table named "prova".
* When this happens, it prints a message and exits.
* To test it, do this:
* $ psql
* => create table prova (bah int)\g
* [other shell] $ pike test_notify.pike
* => notify prova\g
* =>\q
* If it worked you should have read 'notification: "prova"' as output
* from the script.
*/
void notify_cb(string s) {
write("notification: \""+s+"\"\n");
// exit(0);
}
int main () {
object o,r;
array row;
mixed err;
int pid;
o=Sql.postgres("","template1");
write ("Setting notify callback\n");
o->set_notify_callback(notify_cb,2);
write ("Trying to trigger the notify callback.\n");
o->big_query("LISTEN prova");
return -1;
}
//#include "postgres.h"
#include <sql.h>
int main () {
object o,r;
array row;
mixed err;
int pid;
o=Sql.postgres("","template1");
write(sprintf("****** Databases: ******\n%O\n",o->list_dbs()));
write(sprintf("****** Tables: ******\n%O\n",o->list_tables()));
write(sprintf("****** Attributes: ******\n%O\n",o->list_fields("prova")));
}
#ifndef _PG_TYPES_H_
#define _PG_TYPES_H_
#include <program.h>
#include <svalue.h>
struct postgres_result_object_data {
PGresult * result;
int cursor;
};
struct pgres_object_data {
PGconn *dblink;
struct pike_string *last_error;
PGresult * last_result;
struct svalue * notify_callback;
};
/* The header name could be deceiving, but who cares? */
extern struct program *postgres_program, *pgresult_program;
#endif
/* $Id: pgres_config.h.in,v 1.1.1.1 1997/10/14 22:07:21 grubba Exp $ */
#undef STDC_HEADERS
#undef HAVE_POSTGRES_H
#undef HAVE_LIBPQ_FE_H
#undef HAVE_LIBPQ
/* End of autoconfigurable section */
#if defined (HAVE_LIBPQ) && defined (HAVE_POSTGRES_H) && defined (HAVE_LIBPQ_FE_H)
#define HAVE_POSTGRES
#endif
/*
* Postgres95 support for pike/0.5 and up
*
* (C) 1997 Francesco Chemolli <kinkie@comedia.it>
*
* This code is provided AS IS, and may be copied and distributed freely,
* under the terms of the GNU General Public License, version 2.
*
* You might notice that this code resembles Henrik Grubbestrom's mysql
* module. It's just the most efficient way of doing things. This doesn't
* imply I didn't peek at his code ^_^'
*/
/*
* Although Postgres allows great flexibility in returning values from the
* backend connection, in order to keep this code interface-compliant
* I'll have to do some serious emulation stuff, somehow making pike
* code less easy to write.
* I'm sticking to the object result interface because it allows
* for bigger query results.
* For now I'm handling only text. (external) type handling for postgres
* is _REALLY_ messy. Not to talk the big_objects handling, and COPY and
* the status... yuck.
* The potential to powerfully store (also) binary data is there, but
* it's dampened by a really messy interface implementation.
*/
/*
* Notes for Henrik:
* - I suggest allowing negative seek() arguments for the generic interface,
* moving the check inside the actual sql classes.
*/
#include "pgres_config.h"
#ifdef HAVE_POSTGRES
/* #define PGRESDEBUG */
/* <sigh> Postgres stores strings internally padding with whitespaces
* to their field length. If CUT_TRAILING_SPACES is defined, all
* trailing spaces will be cut, regardless they were meant or not.
* This is meant for 'compatibility' versus other database servers, like
* Msql, where a declaration of char(20) means 'any string long at most
* 20 characters', where in Postgres it means 'any string long exactly 20
* characters'. With Postgres you have to use type varchar otherwise, but
* this makes its SQL incompatible with other servers'.
*/
#define CUT_TRAILING_SPACES
#include <stdio.h>
#include <libpq-fe.h>
/* Pike includes */
#include <global.h>
#include <stralloc.h>
#include <object.h>
#include <threads.h>
#include <array.h>
#include <mapping.h>
#include <builtin_functions.h>
#include <module_support.h>
#ifdef _REENTRANT
MUTEX_T pike_postgres_result_mutex;
#define PQ_LOCK() mt_lock(&pike_postgres_mutex)
#define PQ_UNLOCK() mt_unlock(&pike_postgres_mutex)
#define THREAD() THREADS_ALLOW();PQ_LOCK()
#define UNTHREAD() THREADS_DISQLLOW(); PQ_UNLOCK()
#else
#define THREAD() /**/
#define UNTHREAD() /**/
#endif
#include "pg_types.h"
#define THIS ((struct postgres_result_object_data *) fp->current_storage)
#ifdef PGRESDEBUG
#define pgdebug printf
#else
static void pgdebug (char * a, ...) {}
#endif
void result_create (struct object * o) {
pgdebug("result_create().\n");
THIS->result=NULL;
THIS->cursor=0;
}
void result_destroy (struct object * o) {
pgdebug("result_destroy().\n");
PQclear(THIS->result);
}
static void f_create (INT32 args)
{
char *storage;
check_all_args("postgres_result->create",args,BIT_OBJECT,0);
pgdebug("result->f_create(%d).\n",args);
#if OLD
if (sp[-args].u.object->prog != postgres_program)
error ("Need a postgres type object.\n");
THIS->result=
((struct pgres_object_data *)sp[-args].u.object->storage)->last_result;
((struct pgres_object_data *) sp[-args].u.object->storage)->last_result=NULL;
#endif
storage=get_storage(sp[-args].u.object,postgres_program);
if (!storage)
error ("I need a Postgres object or an heir of it.\n");
THIS->result=((struct pgres_object_data *)storage)->last_result;
((struct pgres_object_data *) sp[-args].u.object->storage)->last_result=NULL;
/* no fear of memory leaks, we've only moved the pointer from there to here */
pop_n_elems(args);
if (!THIS->result) /*this ensures we _DO_ have a result*/
error ("Bad result.\n");
#ifdef PGRESDEBUG
pgdebug("Got %d tuples.\n",PQntuples(THIS->result));
#endif
}
static void f_num_rows (INT32 args)
{
check_all_args("postgres_result->num_rows",args,0);
if (PQresultStatus(THIS->result)!=PGRES_TUPLES_OK) {
push_int(0);
return;
}
push_int(PQntuples(THIS->result));
return;
}
static void f_num_fields (INT32 args)
{
check_all_args("postgres_result->num_fields",args,0);
if (PQresultStatus(THIS->result)!=PGRES_TUPLES_OK) {
push_int(0);
return;
}
push_int(PQnfields(THIS->result));
return;
}
static void f_fetch_fields (INT32 args)
{
int j, numfields, tmp;
PGresult * res=THIS->result;
check_all_args("postgres_result->fetch_fields",args,0);
numfields=PQnfields(res);
for (j=0;j<numfields;j++)
{
push_text("name");
push_text(PQfname(res,j));
/* no table information is availible */
/* no default value information is availible */
push_text("type");
push_int(PQftype(res,j));
/* ARGH! I'd kill 'em! How am I supposed to know how types are coded
* internally!?!?!?!?
*/
push_text("length");
tmp=PQfsize(res,j);
if (tmp>=0)
push_int(tmp);
else
push_text("variable");
f_aggregate_mapping(6);
}
f_aggregate(numfields);
return;
}
static void f_seek (INT32 args)
{
int howmuch;
check_all_args("postgres_result->seek",args,BIT_INT,0);
howmuch=sp[-args].u.integer;
if (THIS->cursor+howmuch < 0)
error ("Cannot seek to negative result indexes!\n");
if (THIS->cursor+howmuch > PQntuples(THIS->result))
error ("Cannot seek past result's end!.\n");
pop_n_elems(args);
THIS->cursor += howmuch;
return;
}
static void f_fetch_row (INT32 args)
{
int j,k,numfields;
char * value;
check_all_args("postgres_result->fetch_row",args,0);
pgdebug("f_fectch_row(); cursor=%d.\n",THIS->cursor);
if (THIS->cursor>=PQntuples(THIS->result)) {
push_int(0);
return;
}
numfields=PQnfields(THIS->result);
for (j=0;j<numfields;j++) {
value=PQgetvalue(THIS->result,THIS->cursor,j);
#ifdef CUT_TRAILING_SPACES
/* damn! We need to cut the trailing whitespace... */
for (k=PQgetlength(THIS->result,THIS->cursor,j)-1;k>=0;k--)
if (value[k]!=' ')
break;
push_string(make_shared_binary_string(value,k+1));
#else
push_text(value);
#endif
}
f_aggregate(numfields);
THIS->cursor++;
return;
}
struct program * pgresult_program;
void pgresult_init (void)
{
start_new_program();
add_storage(sizeof(struct postgres_result_object_data));
set_init_callback(result_create);
set_exit_callback(result_destroy);
add_function("create",f_create,"function(object:void)",OPT_SIDE_EFFECT);
add_function("num_rows",f_num_rows,"function(void:int)",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
add_function("num_fields",f_num_fields,"function(void:int)",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
add_function("fetch_fields",f_fetch_fields,
"function(void:void|array(mapping(string:mixed)))",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
add_function("seek",f_seek,"function(int:void)",OPT_SIDE_EFFECT);
add_function("fetch_row",f_fetch_row,"function(void:void|array(mixed))",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
pgresult_program=end_program();
add_program_constant("postgres_result",pgresult_program,0);
pgresult_program->refs++;
}
#endif
void pgresult_init(void);
/*
* Postgres95 support for pike/0.5 and up
*
* (C) 1997 Francesco Chemolli <kinkie@comedia.it>
*
* This code is provided AS IS, and may be distributed under the terms
* of the GNU General Public License, version 2.
*/
#include "pgres_config.h"
#ifdef HAVE_POSTGRES
/* #define PGDEBUG */
/* System includes */
#include <stdlib.h>
#include <stdio.h> /* Needed or libpq-fe.h pukes :( */
#include <malloc.h>
#include <string.h>
/* Pike includes */
#include <global.h>
#include <las.h>
#include <machine.h>
#include <svalue.h>
#include <threads.h>
#include <stralloc.h>
#include <object.h>
#include <module_support.h>
#include <operators.h>
/* Postgres includes */
/* A hack, because DEBUG is defined both in pike's machine.h and in postgres */
#ifdef DEBUG
#undef DEBUG
#endif
#include <postgres.h>
#include <libpq-fe.h>
#include "pgresult.h"
/* Actual code */
#ifdef _REENTRANT
MUTEX_T pike_postgres_mutex;
#define PQ_LOCK() mt_lock(&pike_postgres_mutex);
#define PQ_UNLOCK() mt_unlock(&pike_postgres_mutex);
#else
#define PQ_LOCK()
#define PQ_UNLOCK()
#endif
#include "pg_types.h"
#ifdef PGDEBUG
#define pgdebug printf
#else
static void pgdebug (char * a, ...) {}
#endif
struct program * postgres_program;
RCSID("$Id: postgres.c,v 1.1.1.1 1997/10/14 22:07:21 grubba Exp $");
#define THIS ((struct pgres_object_data *) fp->current_storage)
static void set_error (char * newerror)
{
pgdebug("set_error(%s).\n",newerror);
if (THIS->last_error)
free_string(THIS->last_error);
THIS->last_error=make_shared_string(newerror);
return;
}
static void pgres_create (struct object * o) {
pgdebug ("pgres_create().\n");
THIS->dblink=NULL;
THIS->last_error=NULL;
THIS->notify_callback=malloc(sizeof(struct svalue));
if (!THIS->notify_callback)
error ("Memory allocation error.\n");
THIS->notify_callback->type=T_INT;
}
static void pgres_destroy (struct object * o)
{
PGconn * conn;
pgdebug ("pgres_destroy().\n");
if ((conn=THIS->dblink)) {
PQ_LOCK();
THREADS_ALLOW();
PQfinish(conn);
THREADS_DISALLOW();
PQ_UNLOCK();
THIS->dblink=NULL;
}
if(THIS->last_error) {
if (THIS->last_error)
free_string(THIS->last_error);
THIS->last_error=NULL;
}
if (THIS->notify_callback->type!=T_INT) {
free_svalue(THIS->notify_callback);
free(THIS->notify_callback);
}
}
static void f_create (INT32 args)
{
char * host=NULL, *db=NULL, *port=NULL;
PGconn * conn;
check_all_args("Postgres->create",args,
BIT_STRING|BIT_VOID,BIT_STRING|BIT_VOID,
BIT_INT|BIT_VOID,0);
if (THIS->dblink) {
conn=THIS->dblink;
PQ_LOCK();
THREADS_ALLOW();
PQfinish(conn);
THREADS_DISALLOW();
PQ_UNLOCK();
}
if (args>=1)
if(sp[-args].u.string->len)
host=sp[-args].u.string->str;
/* postgres docs say they use hardwired defaults if no variable is found*/
if (args>=2)
if (sp[1-args].u.string->len)
db=sp[1-args].u.string->str;
if (args==3)
if (sp[2-args].u.integer <=65535 && sp[2-args].u.integer>0) {
port=malloc(10*sizeof(char)); /*it's enough, we need only 6*/
sprintf(port,"%d",sp[2-args].u.integer);
}
else
error ("You must specify a TCP/IP port number as argument 5 to postgres->create().\n");
PQ_LOCK();
THREADS_ALLOW();
pgdebug("f_create(%s,%s,%s).\n",host,port,db);
conn=PQsetdb(host,port,NULL,NULL,db);
THREADS_DISALLOW();
PQ_UNLOCK();
if (!conn)
error ("Internal error: PQserdb returned NULL!\n");
if (PQstatus(conn)!=CONNECTION_OK) {
set_error(PQerrorMessage(conn));
PQ_LOCK();
THREADS_ALLOW();
PQfinish(conn);
THREADS_DISALLOW();
PQ_UNLOCK();
error("Could not connect to database.\n");
}
THIS->dblink=conn;
if (!THIS->dblink)
error ("Huh? Weirdness here! Internal error!\n");
pop_n_elems(args);
}
static void f_select_db (INT32 args)
{
char *host, *port, *options, *tty, *db;
PGconn * conn, *newconn;
check_all_args("Postgres->select_db",args,BIT_STRING,0);
if (!THIS->dblink)
error ("Internal error. How can you possibly not be linked to a "
"database already?\n");
conn=THIS->dblink;
PQ_LOCK();
THREADS_ALLOW();
host=PQhost(conn);
port=PQport(conn);
options=PQoptions(conn);
tty=PQtty(conn);
db=PQdb(conn);
THREADS_DISALLOW();
PQ_UNLOCK();
#if 0
/* This is an optimization, but people may want to reset a connection
* re-selecting its database.
*/
if (!strcmp(sp[-args].u.string->str,db)) {
pop_n_elems(args);
return;
}
#endif
db=sp[-args].u.string->str;
/* This could be really done calling f_create, but it's more efficient this
* way */
PQ_LOCK();
THREADS_ALLOW();
/* using newconn is necessary or otherwise the datastructures I use
* as arguments get freed by PQfinish. Could be a problem under extreme
* situations (i.e. if the temporary use of _one_ more filedescriptor
* is not possible.
*/
newconn=PQsetdb(host,port,options,tty,db);
PQfinish(conn);
conn=newconn;
THREADS_DISALLOW();
PQ_UNLOCK();
if (PQstatus(conn)==CONNECTION_BAD) {
set_error(PQerrorMessage(conn));
PQfinish(conn);
error("Could not connect to database.\n");
}
THIS->dblink=conn;
pop_n_elems(args);
}
static void f_big_query(INT32 args)
{
PGconn *conn = THIS->dblink;
PGresult * res;
PGnotify * notification;
char *query;
check_all_args("Postgres->big_query",args,BIT_STRING,0);
if (!conn)
error ("Not connected.\n");
if (sp[-args].u.string->len)
query=sp[-args].u.string->str;
else
query=" ";
PQ_LOCK();
THREADS_ALLOW();
pgdebug("f_big_query(\"%s\")\n",query);
res=PQexec(conn,query);
notification=PQnotifies(conn);
THREADS_DISALLOW();
PQ_UNLOCK();
pop_n_elems(args);
if (notification!=NULL) {
pgdebug("Incoming notification: \"%s\"\n",notification->relname);
push_text(notification->relname);
apply_svalue(THIS->notify_callback,1);
/* apply_svalue simply returns if the first argument is a T_INT */
free (notification);
}
if (!res) {
set_error(PQerrorMessage(conn));
if (!strncmp(THIS->last_error->str,"WARN",4)) {
/* Sigh... woldn't a NONFATAL_ERROR be MUCH better? */
push_int(1);
return;
}
error ("Error in query.\n");
}
switch (PQresultStatus(res))
{
case PGRES_EMPTY_QUERY:
case PGRES_COMMAND_OK:
pgdebug("\tOk.\n");
THIS->last_result=NULL;
PQclear(res);
push_int(0);
return;
case PGRES_NONFATAL_ERROR:
pgdebug ("Warning.\n");
set_error(PQerrorMessage(conn));
push_int(1);
return;
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
pgdebug("\tBad.\n");
set_error(PQerrorMessage(conn));
PQclear(res);
error ("Error in frontend-backend communications.\n");
case PGRES_TUPLES_OK:
pgdebug("\tResult.\n");
THIS->last_result=res;
push_object(this_object());
push_object(clone_object(pgresult_program,1));
return;
default:
error ("Unimplemented server feature.\n");
}
error ("Internal error in postgresmodule.\n");
}
static void f_error (INT32 args)
{
check_all_args("Postgres->error",args,0);
if (THIS->last_error)
push_string(THIS->last_error);
else
push_int(0);
return;
}
static void f_reset (INT32 args)
{
PGconn * conn;
check_all_args("Postgres->reset",args,0);
if (!THIS->dblink)
error ("Not connected.\n");
conn=THIS->dblink;
PQ_LOCK();
THREADS_ALLOW();
PQreset(conn);
THREADS_DISALLOW();
PQ_UNLOCK();
if (PQstatus(conn)==CONNECTION_BAD) {
set_error(PQerrorMessage(conn));
error("Bad connection.\n");
}
}
#if 0
/* This was cut (for now) because of the difficulty of obtaining
* a valid FILE * from a fileobject.
*/
static void f_trace (INT32 args)
{
if (args!=1)
error ("Wrong args for trace().\n");
if (sp[-args].type==T_INT)
if (sp[-args].u.integer==0)
PQuntrace(THIS->dblink);
else
error ("Wrong argument for postgres->trace().\n");
else
/* I really don't know how to check that the argument is an instance of
* /precompiled/file... I guess there's the name stored somewhere..
* For now let's presume that if it's an object, then it's a /precompiled/file*/
PQtrace(THIS->dblink,
((struct file_struct*)sp[-args].u.object->storage)->fd);
}
#endif
/* I hope I'm doing this right... */
static void f_callback(INT32 args)
{
if (sp[-args].type==T_INT) {
if (THIS->notify_callback->type!=T_INT) {
free_svalue(THIS->notify_callback);
THIS->notify_callback->type=T_INT;
}
pop_n_elems(args);
return;
}
/*let's assume it's a function otherwise*/
assign_svalue(THIS->notify_callback,sp-args);
pop_n_elems(args);
}
static void f_host_info (INT32 args)
{
check_all_args("Postgres->host_info",args,0);
if (PQstatus(THIS->dblink)!=CONNECTION_BAD) {
push_text("TCP/IP connection to ");
push_text(PQhost(THIS->dblink));
f_add(2);
return;
}
set_error(PQerrorMessage(THIS->dblink));
error ("Bad connection.\n");
}
void pike_module_init (void)
{
start_new_program();
add_storage(sizeof(struct pgres_object_data));
set_init_callback(pgres_create);
set_exit_callback(pgres_destroy);
/* sql-interface compliant functions */
add_function ("create",f_create,
"function(void|string,void|string,int|void:void)",
OPT_EXTERNAL_DEPEND);
/* That is: create(hostname,database,port)
* It depends on the environment variables:
* PGHOST, PGOPTIONS, PGPORT, PGTTY(don't use!), PGDATABASE
* Notice: Postgres _requires_ a database to be selected upon connection
*/
add_function("select_db",f_select_db,"function(string:void)",
OPT_EXTERNAL_DEPEND);
add_function("big_query",f_big_query, "function(string:int|object)",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
add_function("error",f_error,"function(void:string)",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
add_function("host_info",f_host_info,"function(void:string)",
OPT_EXTERNAL_DEPEND|OPT_RETURN);
/* postgres-specific functions */
add_function("reset",f_reset,
"function(void:void)",OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
#if 0
add_function("trace",f_trace,"function(object|int:void)",
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
/* If given a clone of /precompiled/file, traces to that file.
* If given 0, stops tracing.
* See note on the implementation.
*/
#endif
add_function("_set_notify_callback",f_callback,
"function(int|function(string:void):void)",
OPT_SIDE_EFFECT);
postgres_program = end_program();
add_program_constant("postgres",postgres_program,0);
postgres_program->refs++;
pgresult_init();
}
#else /* HAVE_POSTGRES */
void pike_module_init(void) {}
#endif /* HAVE_POSTGRES */
void pike_module_exit(void) {}
Note: these are the notes about version pre1 for pike/0.4. Differences
for version 0.5b4 and later of the Pike interpreter are at the bottom of
the file.
I don't have the time now to work on a BMML or HTML manual, so here's
a quick'n dirty one.
This module implements the generic interface to databases as defined
by Henrik Grubbstrom and myself.
There are a few noticeable differences, due to the peculiarities of
the postgres database. Unluckily, it can't be helped.
The programs in this connectivity systems are:
/precompiled/raw/sql/postgres
/precompiled/sql/postgres
/precompiled/sql/postgres_result
The first should not be used. Some functionalities are better offered
via a pike wrapper than by the atual connection tool, and those are implemented
by /precompiled/sql/postgres. You should also consider using the
generic SQL interface instead, as defined in /precompiled/sql
/precompiled/raw/sql/postgres has all that is needed to do queries, but lacks
all schema-related functions, since those muse be implemented via queries
to the system tables in the postgres database.
It implements the following functions (when nothing is specified,
it means they are interface-compilant):
- create : this is not exactly interface-compliant in that:
-it ALWAYS has to bind to a database. In postgres connecting to a server
and to a database on that server are undistingushable operations.
It accepts four arguments as the default interface,
plus a fifth optional argument which is the port number to connect to.
- select_db
- big_query : in Postgres there are different kind of errors
(that means: different errors are reported differently by the frontend
library). Those errors can be divided into three main categories:
fatal errors, nonfatal errors, no errors. This is reflected
by the returned values of the function: a result object if there is a result
to return, 0 if all went fine but there is no result to return, an exception
if there has been an error fatal error, or 1 if there has been a nonfatal
error (maybe this one should be reported via exceptions too?)
- error
- void reset() : tries to reset the connection to the database
- void set_notify_callback (int|function(string:void))
Postgres supports asyncronous notification to clients.
If this callback is set, it will be invoked when such a notification
is received. Notice that notifications are carried piggy-back on
query results. In the /precompiled/sql/posgres this function will
allow a second argument, which will be the delay of a virtual cycle
(implemented via call_out()s) of empty queries to the server, to
allow such notifications to be delivered.
Giving an integer argument resets the callback.
big_query() returns a clone of /precompiled/sql/postgres_result, which
implements:
- num_rows()
- num_fields()
- fetch_fields(): returns 0 or an array of mappings describing the fields.
- seek(n): seeks to the (current + n-th) row. n can be negative.
- fetch_row(): returns 0 or an array(mixed) with the data in the row.
Notice that postgres pads strings that shorter than the declared field
length with whitespaces (otherwise you have to use the varchar type).
This is incompatible with other servers, so I implemented a feature
that all trailing whitespaces in queries are cut. To change this behavior,
undefine CUT_TRAILING_SPACES in pgresult.c.
/precompiled/sql/postgres is a wrapper around the actual postgres
class, and handles all the schema-related parts of the interface,
as well as some high-level core functions. Its main difference over the
/precompiled/raw/sql/postgres is in the set_notify_callback function.
This one accepts a second (optional) integer argument. If given it
specifies the number of seconds to wait to poll the database.
************* CHANGES FOR VERSION 0.2pre1 ****************
Notice: I HAVEN'T TESTED IT YET!
0.1pre seemed to work, and the actual interface code hasn't changed.
I just adapted the interface to the pike counterpart and added arguments
typechecking.
The module system is still pretty much a mystery to me in its deepest
implications, and other modules (in particular Grubba's Mysql module) can
help me only so much, since my interface involves 3 pike programs and not
two. Furthermore, the three programs are not of the same 'type', since two
are C programs, the other one is a pike wrapper program. To sum it up, I might
have come up with a way to reference them all in a consistent way, or I
might have just failed miserably ;)
The three modules are referenced as "Sql.postgres", "Postgres.raw_postgres"
and "Sql.postgres_result" or "Postgres.postgres_result" (it should be the same,
really). Although the raw module _is_ in fact a complete module,
I strongly discourage you to use it.
To use them, you have to copy the files in the modules/Postgres/lib directory
by hand. postgres.h goes to the pike include dir
(i.e. /usr/local/lib/pike/include)
while the files in the directory Sql.pmod go to the Sql.pmod subdirectory in
your pike modules directory (i.e. /usr/local/lib/pike/modules/Sql.pmod).
In the extras/ directory there are a couple example files.
********* VERSION 0.3 *****************
Adapts the interface to Pike/0.5beta6 and later.
Also cleaned up the installation process: now install only if postgres is
really supported
*********** VERSION 0.4 ***************
Changes in the create() interface:
Postgres.postgres()->create() now accepts only 3 arguments (all optional):
-string hostname
-string database name
-int port
The interface compliancy is however guarranteed by Sql.postgres:
Sql.postgres()->create() accepts 4 arguments (instead of 5):
-string hostname
-string database name
-string user (ignored)
-string password (ignored)
the port number is encoded in the hostname, which now takes the form:
"hostname[:portnumber]"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment