Common subdirectories: orig/dbdpg/CVS and dbdpg/CVS diff -uN orig/dbdpg/MANIFEST dbdpg/MANIFEST --- orig/dbdpg/MANIFEST Wed Nov 27 12:50:16 2002 +++ dbdpg/MANIFEST Tue Feb 11 19:43:47 2003 @@ -9,6 +9,14 @@ dbd-pg.pod dbdimp.c dbdimp.h +large_object.c +large_object.h +prescan_stmt.c +quote.c +quote.h +types.c +types.h +pg_typeOID.h eg/ApacheDBI.pl eg/lotest.pl eg/notify_test.patch diff -uN orig/dbdpg/Makefile.PL dbdpg/Makefile.PL --- orig/dbdpg/Makefile.PL Wed Nov 27 12:01:32 2002 +++ dbdpg/Makefile.PL Sat Feb 8 12:40:38 2003 @@ -57,7 +57,7 @@ NAME => 'DBD::Pg', VERSION_FROM => 'Pg.pm', INC => "-I$POSTGRES_INCLUDE -I$dbi_arch_dir", - OBJECT => "Pg\$(OBJ_EXT) dbdimp\$(OBJ_EXT)", + OBJECT => "Pg\$(OBJ_EXT) dbdimp\$(OBJ_EXT) quote\$(OBJ_EXT) types\$(OBJ_EXT)", LIBS => ["-L$POSTGRES_LIB -lpq"], AUTHOR => 'http://gborg.postgresql.org/project/dbdpg/projdisplay.php', ABSTRACT => 'PostgreSQL database driver for the DBI module', @@ -75,6 +75,9 @@ if ($Config{dlsrc} =~ /dl_none/) { $opts{LINKTYPE} = 'static'; } + +sub MY::postamble { return &dbd_postamble; } + WriteMakefile(%opts); diff -uN orig/dbdpg/Pg.pm dbdpg/Pg.pm --- orig/dbdpg/Pg.pm Sun Dec 29 23:59:05 2002 +++ dbdpg/Pg.pm Sat Feb 8 00:18:50 2003 @@ -757,28 +757,6 @@ return $ti; } - - # Characters that need to be escaped by quote(). - my %esc = ( "'" => '\\047', # '\\' . sprintf("%03o", ord("'")), # ISO SQL 2 - '\\' => '\\134', # '\\' . sprintf("%03o", ord("\\")), - ); - - # Set up lookup for SQL types we don't want to escape. - my %no_escape = map { $_ => 1 } - DBI::SQL_INTEGER, DBI::SQL_SMALLINT, DBI::SQL_DECIMAL, - DBI::SQL_FLOAT, DBI::SQL_REAL, DBI::SQL_DOUBLE, DBI::SQL_NUMERIC; - - sub quote { - my ($dbh, $str, $data_type) = @_; - return "NULL" unless defined $str; - return $str if $data_type && $no_escape{$data_type}; - - $dbh->DBI::set_err(1, "Use of SQL_BINARY invalid in quote()") - if $data_type && $data_type == DBI::SQL_BINARY; - - $str =~ s/(['\\\0])/$esc{$1}/g; - return "'$str'"; - } } { package DBD::Pg::st; # ====== STATEMENT ====== diff -uN orig/dbdpg/Pg.xs dbdpg/Pg.xs --- orig/dbdpg/Pg.xs Wed Nov 27 05:21:13 2002 +++ dbdpg/Pg.xs Tue Feb 11 19:36:29 2003 @@ -11,19 +11,20 @@ #include "Pg.h" - +#include "quote.h" +#include "types.h" +#include "pg_typeOID.h" #ifdef _MSC_VER #define strncasecmp(a,b,c) _strnicmp((a),(b),(c)) #endif - DBISTATE_DECLARE; - MODULE = DBD::Pg PACKAGE = DBD::Pg + I32 constant(name=Nullch) char *name @@ -58,51 +59,68 @@ OUTPUT: RETVAL -PROTOTYPES: DISABLE - -BOOT: - items = 0; /* avoid 'unused variable' warning */ - DBISTATE_INIT; - /* XXX this interface will change: */ - DBI_IMP_SIZE("DBD::Pg::dr::imp_data_size", sizeof(imp_drh_t)); - DBI_IMP_SIZE("DBD::Pg::db::imp_data_size", sizeof(imp_dbh_t)); - DBI_IMP_SIZE("DBD::Pg::st::imp_data_size", sizeof(imp_sth_t)); - dbd_init(DBIS); +INCLUDE: Pg.xsi # ------------------------------------------------------------ -# driver level interface +# db functions # ------------------------------------------------------------ -MODULE = DBD::Pg PACKAGE = DBD::Pg::dr +MODULE=DBD::Pg PACKAGE = DBD::Pg::db -# disconnect_all renamed and ALIASed to avoid length clash on VMS :-( + +#TODO: make quote(foo, {type=>SQL_INTEGER}) work #rl +#TODO: make quote(foo, {pg_type=>DBD::Pg::PG_INTEGER}) work #rl +#TODO: remove dbd_quote() into thsi function. void -discon_all_(drh) - SV * drh - ALIAS: - disconnect_all = 1 +quote(dbh, to_quote_sv, type_sv=Nullsv) + SV* dbh + SV* to_quote_sv + SV* type_sv + CODE: - D_imp_drh(drh); - ST(0) = dbd_discon_all(drh, imp_drh) ? &sv_yes : &sv_no; + { + char *to_quote; + STRLEN len; + STRLEN retlen=0; + int type_num; + char *quoted; + sql_type_info_t *type_info; + + + if(type_sv && SvOK(type_sv)) { + if SvMAGICAL(type_sv) + mg_get(type_sv); + + type_info = sql_type_data(SvIV(type_sv)); + type_num = type_info->type.pg; + + } else { + /* default to varchar */ + type_num = VARCHAROID; + } + + if (!SvOK(to_quote_sv)) { + quoted = "NULL"; + len = 4; + ST(0) = sv_2mortal(newSVpv(quoted,len)); + } else { + if (SvMAGICAL(to_quote_sv)) + mg_get(to_quote_sv); + + to_quote = SvPV(to_quote_sv, len); + quoted = dbd_quote(to_quote, type_num, len, &retlen); + ST(0) = sv_2mortal(newSVpv(quoted, retlen)); + free (quoted); + } + } # ------------------------------------------------------------ -# database level interface +# database level interface PG specific # ------------------------------------------------------------ MODULE = DBD::Pg PACKAGE = DBD::Pg::db -void -_login(dbh, dbname, username, pwd) - SV * dbh - char * dbname - char * username - char * pwd - CODE: - D_imp_dbh(dbh); - ST(0) = pg_db_login(dbh, imp_dbh, dbname, username, pwd) ? &sv_yes : &sv_no; - - int _ping(dbh) SV * dbh @@ -135,123 +153,6 @@ ST(0) = dbd_db_pg_notifies(dbh, imp_dbh); void -commit(dbh) - SV * dbh - CODE: - D_imp_dbh(dbh); - if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) { - warn("commit ineffective with AutoCommit enabled"); - } - ST(0) = dbd_db_commit(dbh, imp_dbh) ? &sv_yes : &sv_no; - - -void -rollback(dbh) - SV * dbh - CODE: - D_imp_dbh(dbh); - if (DBIc_has(imp_dbh, DBIcf_AutoCommit)) { - warn("rollback ineffective with AutoCommit enabled"); - } - ST(0) = dbd_db_rollback(dbh, imp_dbh) ? &sv_yes : &sv_no; - - -void -disconnect(dbh) - SV * dbh - CODE: - D_imp_dbh(dbh); - if ( !DBIc_ACTIVE(imp_dbh) ) { - XSRETURN_YES; - } - /* pre-disconnect checks and tidy-ups */ - if (DBIc_CACHED_KIDS(imp_dbh)) { - SvREFCNT_dec(DBIc_CACHED_KIDS(imp_dbh)); - DBIc_CACHED_KIDS(imp_dbh) = Nullhv; - } - /* Check for disconnect() being called whilst refs to cursors */ - /* still exists. This possibly needs some more thought. */ - if (DBIc_ACTIVE_KIDS(imp_dbh) && DBIc_WARN(imp_dbh) && !dirty) { - char *plural = (DBIc_ACTIVE_KIDS(imp_dbh)==1) ? "" : "s"; - warn("disconnect(%s) invalidates %d active statement%s. %s", - SvPV(dbh,na), (int)DBIc_ACTIVE_KIDS(imp_dbh), plural, - "Either destroy statement handles or call finish on them before disconnecting."); - } - ST(0) = dbd_db_disconnect(dbh, imp_dbh) ? &sv_yes : &sv_no; - - -void -STORE(dbh, keysv, valuesv) - SV * dbh - SV * keysv - SV * valuesv - CODE: - D_imp_dbh(dbh); - ST(0) = &sv_yes; - if (!dbd_db_STORE_attrib(dbh, imp_dbh, keysv, valuesv)) { - if (!DBIS->set_attr(dbh, keysv, valuesv)) { - ST(0) = &sv_no; - } - } - - -void -FETCH(dbh, keysv) - SV * dbh - SV * keysv - CODE: - D_imp_dbh(dbh); - SV *valuesv = dbd_db_FETCH_attrib(dbh, imp_dbh, keysv); - if (!valuesv) { - valuesv = DBIS->get_attr(dbh, keysv); - } - ST(0) = valuesv; /* dbd_db_FETCH_attrib did sv_2mortal */ - - -void -DESTROY(dbh) - SV * dbh - PPCODE: - D_imp_dbh(dbh); - ST(0) = &sv_yes; - if (!DBIc_IMPSET(imp_dbh)) { /* was never fully set up */ - if (DBIc_WARN(imp_dbh) && !dirty && dbis->debug >= 2) { - warn("Database handle %s DESTROY ignored - never set up", SvPV(dbh,na)); - } - } - else { - /* pre-disconnect checks and tidy-ups */ - if (DBIc_CACHED_KIDS(imp_dbh)) { - SvREFCNT_dec(DBIc_CACHED_KIDS(imp_dbh)); - DBIc_CACHED_KIDS(imp_dbh) = Nullhv; - } - if (DBIc_IADESTROY(imp_dbh)) { /* want's ineffective destroy */ - DBIc_ACTIVE_off(imp_dbh); - } - if (DBIc_ACTIVE(imp_dbh)) { - if (DBIc_WARN(imp_dbh) && (!dirty || dbis->debug >= 3)) { - warn("Database handle destroyed without explicit disconnect"); - } - /* The application has not explicitly disconnected. That's bad. */ - /* To ensure integrity we *must* issue a rollback. This will be */ - /* harmless if the application has issued a commit. If it hasn't */ - /* then it'll ensure integrity. Consider a Ctrl-C killing perl */ - /* between two statements that must be executed as a transaction. */ - /* Perl will call DESTROY on the dbh and, if we don't rollback, */ - /* the server will automatically commit! Bham! Corrupt database! */ - if (!DBIc_has(imp_dbh,DBIcf_AutoCommit)) { - dbd_db_rollback(dbh, imp_dbh); /* ROLLBACK! */ - } - dbd_db_disconnect(dbh, imp_dbh); - } - dbd_db_destroy(dbh, imp_dbh); - } - - -# driver specific functions - - -void lo_open(dbh, lobjId, mode) SV * dbh unsigned int lobjId @@ -392,253 +293,7 @@ # -- end of DBD::Pg::db -# ------------------------------------------------------------ -# statement interface -# ------------------------------------------------------------ -MODULE = DBD::Pg PACKAGE = DBD::Pg::st - -void -_prepare(sth, statement, attribs=Nullsv) - SV * sth - char * statement - SV * attribs - CODE: - { - D_imp_sth(sth); - D_imp_dbh_from_sth; - DBD_ATTRIBS_CHECK("_prepare", sth, attribs); - if (!strncasecmp(statement, "begin", 5) || - !strncasecmp(statement, "end", 4) || - !strncasecmp(statement, "commit", 6) || - !strncasecmp(statement, "abort", 5) || - !strncasecmp(statement, "rollback", 8) ) { - warn("please use DBI functions for transaction handling"); - ST(0) = &sv_no; - } else { - ST(0) = dbd_st_prepare(sth, imp_sth, statement, attribs) ? &sv_yes : &sv_no; - } - } - -void -rows(sth) - SV * sth - CODE: - D_imp_sth(sth); - XST_mIV(0, dbd_st_rows(sth, imp_sth)); - - -void -bind_param(sth, param, value, attribs=Nullsv) - SV * sth - SV * param - SV * value - SV * attribs - CODE: - { - IV sql_type = 0; - D_imp_sth(sth); - if (attribs) { - if (SvNIOK(attribs)) { - sql_type = SvIV(attribs); - attribs = Nullsv; - } - else { - SV **svp; - DBD_ATTRIBS_CHECK("bind_param", sth, attribs); - /* XXX we should perhaps complain if TYPE is not SvNIOK */ - DBD_ATTRIB_GET_IV(attribs, "TYPE", 4, svp, sql_type); - } - } - ST(0) = dbd_bind_ph(sth, imp_sth, param, value, sql_type, attribs, FALSE, 0) ? &sv_yes : &sv_no; - } - - -void -bind_param_inout(sth, param, value_ref, maxlen, attribs=Nullsv) - SV * sth - SV * param - SV * value_ref - IV maxlen - SV * attribs - CODE: - { - IV sql_type = 0; - D_imp_sth(sth); - if (!SvROK(value_ref) || SvTYPE(SvRV(value_ref)) > SVt_PVMG) { - croak("bind_param_inout needs a reference to a scalar value"); - } - if (SvREADONLY(SvRV(value_ref))) { - croak(no_modify); - } - if (attribs) { - if (SvNIOK(attribs)) { - sql_type = SvIV(attribs); - attribs = Nullsv; - } - else { - SV **svp; - DBD_ATTRIBS_CHECK("bind_param", sth, attribs); - DBD_ATTRIB_GET_IV(attribs, "TYPE", 4, svp, sql_type); - } - } - ST(0) = dbd_bind_ph(sth, imp_sth, param, SvRV(value_ref), sql_type, attribs, TRUE, maxlen) ? &sv_yes : &sv_no; - } - - -void -execute(sth, ...) - SV * sth - CODE: - D_imp_sth(sth); - int ret; - if (items > 1) { - /* Handle binding supplied values to placeholders */ - int i; - SV *idx; - imp_sth->all_params_len = 0; /* used for malloc of statement string in case we have placeholders */ - if (items-1 != DBIc_NUM_PARAMS(imp_sth)) { - croak("execute called with %ld bind variables, %d needed", items-1, DBIc_NUM_PARAMS(imp_sth)); - XSRETURN_UNDEF; - } - idx = sv_2mortal(newSViv(0)); - for(i=1; i < items ; ++i) { - sv_setiv(idx, i); - if (!dbd_bind_ph(sth, imp_sth, idx, ST(i), 0, Nullsv, FALSE, 0)) { - XSRETURN_UNDEF; /* dbd_bind_ph already registered error */ - } - } - } - ret = dbd_st_execute(sth, imp_sth); - /* remember that dbd_st_execute must return <= -2 for error */ - if (ret == 0) { /* ok with no rows affected */ - XST_mPV(0, "0E0"); /* (true but zero) */ - } - else if (ret < -1) { /* -1 == unknown number of rows */ - XST_mUNDEF(0); /* <= -2 means error */ - } - else { - XST_mIV(0, ret); /* typically 1, rowcount or -1 */ - } - - -void -fetchrow_arrayref(sth) - SV * sth - ALIAS: - fetch = 1 - CODE: - D_imp_sth(sth); - AV *av = dbd_st_fetch(sth, imp_sth); - ST(0) = (av) ? sv_2mortal(newRV_inc((SV *)av)) : &sv_undef; - - -void -fetchrow_array(sth) - SV * sth - ALIAS: - fetchrow = 1 - PPCODE: - D_imp_sth(sth); - AV *av; - av = dbd_st_fetch(sth, imp_sth); - if (av) { - int num_fields = AvFILL(av)+1; - int i; - EXTEND(sp, num_fields); - for(i=0; i < num_fields; ++i) { - PUSHs(AvARRAY(av)[i]); - } - } - - -void -finish(sth) - SV * sth - CODE: - D_imp_sth(sth); - D_imp_dbh_from_sth; - if (!DBIc_ACTIVE(imp_dbh)) { - /* Either an explicit disconnect() or global destruction */ - /* has disconnected us from the database. Finish is meaningless */ - /* XXX warn */ - XSRETURN_YES; - } - if (!DBIc_ACTIVE(imp_sth)) { - /* No active statement to finish */ - XSRETURN_YES; - } - ST(0) = dbd_st_finish(sth, imp_sth) ? &sv_yes : &sv_no; - - -void -blob_read(sth, field, offset, len, destrv=Nullsv, destoffset=0) - SV * sth - int field - long offset - long len - SV * destrv - long destoffset - CODE: - { - D_imp_sth(sth); - if (!destrv) { - destrv = sv_2mortal(newRV_inc(sv_2mortal(newSViv(0)))); - } - ST(0) = dbd_st_blob_read(sth, imp_sth, field, offset, len, destrv, destoffset) ? SvRV(destrv) : &sv_undef; - } - -void -STORE(sth, keysv, valuesv) - SV * sth - SV * keysv - SV * valuesv - CODE: - D_imp_sth(sth); - ST(0) = &sv_yes; - if (!dbd_st_STORE_attrib(sth, imp_sth, keysv, valuesv)) { - if (!DBIS->set_attr(sth, keysv, valuesv)) { - ST(0) = &sv_no; - } - } - - -# FETCH renamed and ALIASed to avoid case clash on VMS :-( -void -FETCH_attrib(sth, keysv) - SV * sth - SV * keysv - ALIAS: - FETCH = 1 - CODE: - D_imp_sth(sth); - SV *valuesv = dbd_st_FETCH_attrib(sth, imp_sth, keysv); - if (!valuesv) { - valuesv = DBIS->get_attr(sth, keysv); - } - ST(0) = valuesv; /* dbd_st_FETCH_attrib did sv_2mortal */ - - -void -DESTROY(sth) - SV * sth - PPCODE: - D_imp_sth(sth); - ST(0) = &sv_yes; - if (!DBIc_IMPSET(imp_sth)) { /* was never fully set up */ - if (DBIc_WARN(imp_sth) && !dirty && dbis->debug >= 2) { - warn("Statement handle %s DESTROY ignored - never set up", SvPV(sth,na)); - } - } - else { - if (DBIc_IADESTROY(imp_sth)) { /* want's ineffective destroy */ - DBIc_ACTIVE_off(imp_sth); - } - if (DBIc_ACTIVE(imp_sth)) { - dbd_st_finish(sth, imp_sth); - } - dbd_st_destroy(sth, imp_sth); - } # end of Pg.xs diff -uN orig/dbdpg/dbdimp.c dbdpg/dbdimp.c --- orig/dbdpg/dbdimp.c Mon Feb 3 10:20:34 2003 +++ dbdpg/dbdimp.c Mon Feb 10 00:10:57 2003 @@ -21,6 +21,8 @@ */ #include "Pg.h" +#include +#include"types.h" /* XXX DBI should provide a better version of this */ #define IS_DBI_HANDLE(h) (SvROK(h) && SvTYPE(SvRV(h)) == SVt_PVHV && SvRMAGICAL(SvRV(h)) && (SvMAGIC(SvRV(h)))->mg_type == 'P') @@ -28,8 +30,10 @@ DBISTATE_DECLARE; -static void dbd_preparse (imp_sth_t *imp_sth, char *statement); +void pg_error(); +#include "large_object.c" +#include "prescan_stmt.c" void dbd_init (dbistate) @@ -96,42 +100,11 @@ free(err); } -static int -pgtype_bind_ok (dbtype) - int dbtype; -{ - /* basically we support types that can be returned as strings */ - switch(dbtype) { - case 16: /* bool */ - case 17: /* bytea */ - case 18: /* char */ - case 20: /* int8 */ - case 21: /* int2 */ - case 23: /* int4 */ - case 25: /* text */ - case 26: /* oid */ - case 700: /* float4 */ - case 701: /* float8 */ - case 702: /* abstime */ - case 703: /* reltime */ - case 704: /* tinterval */ - case 1042: /* bpchar */ - case 1043: /* varchar */ - case 1082: /* date */ - case 1083: /* time */ - case 1184: /* datetime */ - case 1186: /* timespan */ - case 1296: /* timestamp */ - return 1; - } - return 0; -} - /* ================================================================== */ int -pg_db_login (dbh, imp_dbh, dbname, uid, pwd) +dbd_db_login (dbh, imp_dbh, dbname, uid, pwd) SV *dbh; imp_dbh_t *imp_dbh; char *dbname; @@ -144,6 +117,10 @@ char *src; char *dest; + PGresult *pgres_ret; + char *vstring, *vstart, *vnext; /* Stuff for getting version info */ + + if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "pg_db_login\n"); } /* build connect string */ @@ -189,6 +166,30 @@ return 0; } + /* Quick basic version check -- not robust a'tall TODO: rewrite */ + pgres_ret = PQexec(imp_dbh->conn, "SELECT version()"); + if (pgres_ret && PQresultStatus(pgres_ret) == PGRES_TUPLES_OK) { + vstring = PQgetvalue(pgres_ret, 0,0); /* Tuple 0 ,filed 0 */ + vstart = index(vstring, ' '); + + imp_dbh->version.major = strtol(vstart, &vnext, 10); + imp_dbh->version.minor = strtol(vnext+1, NULL, 10); + imp_dbh->version.ver = strtod(vstart, NULL); + + } else { + imp_dbh->version.major = 0; + imp_dbh->version.minor = 0; + imp_dbh->version.ver = 0.0; + } + PQclear(pgres_ret); + + /* PerlIO_printf(DBILOGFP, "v.ma: %i, v.mi: %i v.ver: %f\n", + imp_dbh->version.major, imp_dbh->version.minor, imp_dbh->version.ver); + + if(imp_dbh->version.ver >= 7.3) + PerlIO_printf(DBILOGFP, "Greater than 7.3\n"); + */ + imp_dbh->init_commit = 1; /* initialize AutoCommit */ imp_dbh->pg_auto_escape = 1; /* initialize pg_auto_escape */ imp_dbh->pg_bool_tf = 0; /* initialize pg_bool_tf */ @@ -316,6 +317,7 @@ } +/* TODO: Tx fix that was done to commit needs to be done here also. #rl */ int dbd_db_rollback (dbh, imp_dbh) SV *dbh; @@ -337,6 +339,8 @@ status = result ? PQresultStatus(result) : -1; PQclear(result); + /*TODO Correct error message. If returning on error + will screw up transaction state? Begin will not get called! */ /* check result */ if (status != PGRES_COMMAND_OK) { pg_error(dbh, status, "rollback failed\n"); @@ -369,7 +373,10 @@ if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_db_disconnect\n"); } /* We assume that disconnect will always work */ - /* since most errors imply already disconnected. */ + /* since most errors imply already disconnected. + * XXX: Um we turn active off, then return 0 on a rollback failing? + * Check to see what happenens -- will we leak memory? :rl + */ DBIc_ACTIVE_off(imp_dbh); if (NULL != imp_dbh->conn) { @@ -510,147 +517,6 @@ } -/* driver specific functins */ - - -int -pg_db_lo_open (dbh, lobjId, mode) - SV *dbh; - unsigned int lobjId; - int mode; -{ - D_imp_dbh(dbh); - return lo_open(imp_dbh->conn, lobjId, mode); -} - - -int -pg_db_lo_close (dbh, fd) - SV *dbh; - int fd; -{ - D_imp_dbh(dbh); - return lo_close(imp_dbh->conn, fd); -} - - -int -pg_db_lo_read (dbh, fd, buf, len) - SV *dbh; - int fd; - char *buf; - int len; -{ - D_imp_dbh(dbh); - return lo_read(imp_dbh->conn, fd, buf, len); -} - - -int -pg_db_lo_write (dbh, fd, buf, len) - SV *dbh; - int fd; - char *buf; - int len; -{ - D_imp_dbh(dbh); - return lo_write(imp_dbh->conn, fd, buf, len); -} - - -int -pg_db_lo_lseek (dbh, fd, offset, whence) - SV *dbh; - int fd; - int offset; - int whence; -{ - D_imp_dbh(dbh); - return lo_lseek(imp_dbh->conn, fd, offset, whence); -} - - -unsigned int -pg_db_lo_creat (dbh, mode) - SV *dbh; - int mode; -{ - D_imp_dbh(dbh); - return lo_creat(imp_dbh->conn, mode); -} - - -int -pg_db_lo_tell (dbh, fd) - SV *dbh; - int fd; -{ - D_imp_dbh(dbh); - return lo_tell(imp_dbh->conn, fd); -} - - -int -pg_db_lo_unlink (dbh, lobjId) - SV *dbh; - unsigned int lobjId; -{ - D_imp_dbh(dbh); - return lo_unlink(imp_dbh->conn, lobjId); -} - - -unsigned int -pg_db_lo_import (dbh, filename) - SV *dbh; - char *filename; -{ - D_imp_dbh(dbh); - return lo_import(imp_dbh->conn, filename); -} - - -int -pg_db_lo_export (dbh, lobjId, filename) - SV *dbh; - unsigned int lobjId; - char *filename; -{ - D_imp_dbh(dbh); - return lo_export(imp_dbh->conn, lobjId, filename); -} - - -int -pg_db_putline (dbh, buffer) - SV *dbh; - char *buffer; -{ - D_imp_dbh(dbh); - return PQputline(imp_dbh->conn, buffer); -} - - -int -pg_db_getline (dbh, buffer, length) - SV *dbh; - char *buffer; - int length; -{ - D_imp_dbh(dbh); - return PQgetline(imp_dbh->conn, buffer, length); -} - - -int -pg_db_endcopy (dbh) - SV *dbh; -{ - D_imp_dbh(dbh); - return PQendcopy(imp_dbh->conn); -} - - /* ================================================================== */ @@ -664,7 +530,13 @@ if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_prepare: statement = >%s<\n", statement); } /* scan statement for '?', ':1' and/or ':foo' style placeholders */ - dbd_preparse(imp_sth, statement); + if((dbd_preparse(sth, imp_sth, statement)) == 0) + return 0; + + if (is_tx_stmt(statement)) { + warn("please use DBI functions for transaction handling"); + return(0); + } /* initialize new statement handle */ imp_sth->result = 0; @@ -675,309 +547,144 @@ } -static void -dbd_preparse (imp_sth, statement) +int +dbd_preparse (sth, imp_sth, statement) + SV *sth; imp_sth_t *imp_sth; - char *statement; + const char *statement; { - bool in_literal = FALSE; - char in_comment = '\0'; - char *src, *start, *dest; - phs_t phs_tpl; - SV *phs_sv; - int idx=0; - char *style="", *laststyle=Nullch; - STRLEN namelen; - - if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_preparse: statement = >%s<\n", statement); } - - /* allocate room for copy of statement with spare capacity */ - /* for editing '?' or ':1' into ':p1'. */ - imp_sth->statement = (char*)safemalloc(strlen(statement) * 3 + 1); - - /* initialise phs ready to be cloned per placeholder */ - memset(&phs_tpl, 0, sizeof(phs_tpl)); - phs_tpl.ftype = 1043; /* VARCHAR */ - - src = statement; - dest = imp_sth->statement; - while(*src) { - - if (in_comment) { - /* SQL-style and C++-style */ - if ((in_comment == '-' || in_comment == '/') && *src == '\n') { - in_comment = '\0'; - } - /* C-style */ - else if (in_comment == '*' && *src == '*' && *(src+1) == '/') { - *dest++ = *src++; /* avoids asterisk-slash-asterisk issues */ - in_comment = '\0'; - } - *dest++ = *src++; - continue; - } - - if (in_literal) { - /* check if literal ends but keep quotes in literal */ - if (*src == in_literal) { - int bs=0; - char *str; - str = src-1; - while (*(str-bs) == '\\') - bs++; - if (!(bs & 1)) - in_literal = 0; - } - *dest++ = *src++; - continue; - } - - /* Look for comments: SQL-style or C++-style or C-style */ - if ((*src == '-' && *(src+1) == '-') || - (*src == '/' && *(src+1) == '/') || - (*src == '/' && *(src+1) == '*')) - { - in_comment = *(src+1); - /* We know *src & the next char are to be copied, so do */ - /* it. In the case of C-style comments, it happens to */ - /* help us avoid slash-asterisk-slash oddities. */ - *dest++ = *src++; - *dest++ = *src++; - continue; - } + static unsigned int prep_stmt_id = 0; + int place_holder_count, stmt_len, status; + int digits, i; + int offset = 0; + D_imp_dbh_from_sth; - /* check if no placeholders */ - if (*src != ':' && *src != '?') { - if (*src == '\'' || *src == '"') { - in_literal = *src; - } - *dest++ = *src++; - continue; - } + ++prep_stmt_id; + digits = 0; + i = prep_stmt_id; + do { + ++digits; + i /=10; + } while (i>0); /* 12*/ + + //PerlIO_printf(DBILOGFP, "Statement: %s \n", statement); + prescan_stmt(statement, &stmt_len, &place_holder_count); + + //PerlIO_printf(DBILOGFP, "Place holders: %i \n", place_holder_count); + /* add space for placeholders candidates */ + stmt_len += calc_ph_space(place_holder_count); + + + offset += strlen ("PREPARE \"DBD::ChurlPg::cached_query \" ("); + offset += digits; /* number of digits in prep_statement_id */ + offset += place_holder_count*strlen("varchar, "); + offset += strlen(") AS"); + + stmt_len += offset; + ++stmt_len; /* for term \0 */ + + //PerlIO_printf(DBILOGFP, "Smt len:%i Offset %i\n", stmt_len, offset); + + Newc(0, imp_sth->statement, stmt_len, char, char); + memset(imp_sth->statement, ' ', offset+1); + if (place_holder_count) { + /* +1 so we can use a 1 based idx (placeholders start from 1)*/ + Newc(0, imp_sth->place_holders, place_holder_count+1, + phs_t**, phs_t*); + } else { + imp_sth->place_holders = 0; + } - /* check for cast operator */ - if (*src == ':' && (*(src-1) == ':' || *(src+1) == ':')) { - *dest++ = *src++; - continue; - } + place_holder_count = rewrite_placeholders(imp_sth, statement, imp_sth->statement+offset,0); + imp_sth->phc = place_holder_count; - /* only here for : or ? outside of a comment or literal and no cast */ + // PerlIO_printf(DBILOGFP, "Rewritten stmt: %s\n", imp_sth->statement+offset); - start = dest; /* save name inc colon */ - *dest++ = *src++; - if (*start == '?') { /* X/Open standard */ - sprintf(start,":p%d", ++idx); /* '?' -> ':p1' (etc) */ - dest = start+strlen(start); - style = "?"; - - } else if (isDIGIT(*src)) { /* ':1' */ - idx = atoi(src); - *dest++ = 'p'; /* ':1'->':p1' */ - if (idx <= 0) { - croak("Placeholder :%d invalid, placeholders must be >= 1", idx); - } - while(isDIGIT(*src)) { - *dest++ = *src++; - } - style = ":1"; + assert(strlen(imp_sth->statement)+1 <= stmt_len); + /* if not dml, no need to continue, As we are not going to + server side prepare this statement TODO: remalloc*/ + if (!is_dml(imp_sth->statement+offset) || imp_dbh->version.ver < 7.3) + return 1; - } else if (isALNUM(*src)) { /* ':foo' */ - while(isALNUM(*src)) { /* includes '_' */ - *dest++ = *src++; - } - style = ":foo"; - } else { /* perhaps ':=' PL/SQL construct */ - continue; - } - *dest = '\0'; /* handy for debugging */ - namelen = (dest-start); - if (laststyle && style != laststyle) { - croak("Can't mix placeholder styles (%s/%s)",style,laststyle); - } - laststyle = style; - if (imp_sth->all_params_hv == NULL) { - imp_sth->all_params_hv = newHV(); - } - phs_tpl.sv = &sv_undef; - phs_sv = newSVpv((char*)&phs_tpl, sizeof(phs_tpl)+namelen+1); - hv_store(imp_sth->all_params_hv, start, namelen, phs_sv, 0); - strcpy( ((phs_t*)(void*)SvPVX(phs_sv))->name, start); - } - *dest = '\0'; - if (imp_sth->all_params_hv) { - DBIc_NUM_PARAMS(imp_sth) = (int)HvKEYS(imp_sth->all_params_hv); - if (dbis->debug >= 2) { PerlIO_printf(DBILOGFP, " dbd_preparse scanned %d distinct placeholders\n", (int)DBIc_NUM_PARAMS(imp_sth)); } - } -} + /* 1 == PREPARE -- TODO: Fix ugly number thing*/ + build_preamble(imp_sth->statement, 1, place_holder_count, prep_stmt_id); + //PerlIO_printf(DBILOGFP, "Rewritten stmt: %s\n", imp_sth->statement); -/* if it LOOKS like a string, this function will determine whether the type needs to be surrounded in single quotes */ -static int pg_sql_needquote (sql_type) - int sql_type; -{ - if (sql_type > 1000 || sql_type == 17 ) { - return 1; + imp_sth->result = PQexec(imp_dbh->conn, imp_sth->statement); + status = imp_sth->result ? PQresultStatus(imp_sth->result) : -1; + if (status != PGRES_COMMAND_OK) { + pg_error(sth,status, PQerrorMessage(imp_dbh->conn)); + return 0; } - return 0; -} - + if (imp_sth->result) + PQclear(imp_sth->result); + /* 2 == EXECUTE -- TODO: Fix ugly number thing & remalloc*/ + build_preamble(imp_sth->statement, 2, place_holder_count, prep_stmt_id); + //PerlIO_printf(DBILOGFP, "Rewritten stmt: %s\n", imp_sth->statement); + imp_sth->server_prepared = 1; -static int -pg_sql_type (imp_sth, name, sql_type) - imp_sth_t *imp_sth; - char *name; - int sql_type; -{ - switch (sql_type) { - case SQL_CHAR: - return 1042; /* bpchar */ - case SQL_NUMERIC: - return 700; /* float4 */ - case SQL_DECIMAL: - return 700; /* float4 */ - case SQL_INTEGER: - return 23; /* int4 */ - case SQL_SMALLINT: - return 21; /* int2 */ - case SQL_FLOAT: - return 700; /* float4 */ - case SQL_REAL: - return 701; /* float8 */ - case SQL_DOUBLE: - return 20; /* int8 */ - case SQL_VARCHAR: - return 1043; /* varchar */ - case SQL_BINARY: - return 17; /* bytea */ - default: - if (DBIc_WARN(imp_sth) && imp_sth && name) { - warn("SQL type %d for '%s' is not fully supported, bound as VARCHAR instead", - sql_type, name); - } - return pg_sql_type(imp_sth, name, SQL_VARCHAR); - } + assert(strlen(imp_sth->statement)+1 <= stmt_len); + return 1; } -static int -sql_pg_type (imp_sth, name, sql_type) - imp_sth_t *imp_sth; - char *name; - int sql_type; -{ - if (dbis->debug >= 1) { - PerlIO_printf(DBILOGFP, "sql_pg_type name '%s' type '%d'\n", name, sql_type ); - } - switch (sql_type) { - case 17: /* bytea */ - return SQL_BINARY; - case 20: /* int8 */ - return SQL_DOUBLE; - case 21: /* int2 */ - return SQL_SMALLINT; - case 23: /* int4 */ - return SQL_INTEGER; - case 700: /* float4 */ - return SQL_NUMERIC; - case 701: /* float8 */ - return SQL_REAL; - case 1042: /* bpchar */ - return SQL_CHAR; - case 1043: /* varchar */ - return SQL_VARCHAR; - case 1082: /* date */ - return SQL_DATE; - case 1083: /* time */ - return SQL_TIME; - case 1296: /* date */ - return SQL_TIMESTAMP; - default: - return sql_type; - } -} +int +deallocate_statement (sth, imp_sth) + SV *sth; + imp_sth_t *imp_sth; +{ + int status, max_len; + char *stmt, *dest, *start; + PGresult *result; + D_imp_dbh_from_sth; + if (NULL == imp_dbh->conn) + return 1; -static int -dbd_rebind_ph (sth, imp_sth, phs) - SV *sth; - imp_sth_t *imp_sth; - phs_t *phs; -{ - STRLEN value_len; + max_len = strlen(imp_sth->statement)+strlen("DEALLOCATE ")+2; + Newc(0,stmt, max_len, char, char); - if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_rebind\n"); } + start = strstr(imp_sth->statement, "\"DBD::ChurlPg::cached_query"); - /* convert to a string ASAP */ - if (!SvPOK(phs->sv) && SvOK(phs->sv)) { - sv_2pv(phs->sv, &na); + if(!start) { + pg_error(sth, -1, "Could not Deallocate statment... Preamble" + "not found"); + return -1; } - if (dbis->debug >= 2) { - char *val = neatsvpv(phs->sv,0); - PerlIO_printf(DBILOGFP, " bind %s <== %.1000s (", phs->name, val); - if (SvOK(phs->sv)) { - PerlIO_printf(DBILOGFP, "size %ld/%ld/%ld, ", (long)SvCUR(phs->sv),(long)SvLEN(phs->sv),phs->maxlen); - } else { - PerlIO_printf(DBILOGFP, "NULL, "); - } - PerlIO_printf(DBILOGFP, "ptype %d, otype %d%s)\n", (int)SvTYPE(phs->sv), phs->ftype, (phs->is_inout) ? ", inout" : ""); - } + sprintf(stmt, "DEALLOCATE "); - /* At the moment we always do sv_setsv() and rebind. */ - /* Later we may optimise this so that more often we can */ - /* just copy the value & length over and not rebind. */ + dest = stmt+11; - if (phs->is_inout) { /* XXX */ - if (SvREADONLY(phs->sv)) { - croak(no_modify); - } - /* phs->sv _is_ the real live variable, it may 'mutate' later */ - /* pre-upgrade high to reduce risk of SvPVX realloc/move */ - (void)SvUPGRADE(phs->sv, SVt_PVNV); - /* ensure room for result, 28 is magic number (see sv_2pv) */ - SvGROW(phs->sv, (phs->maxlen < 28) ? 28 : phs->maxlen+1); - } - else { - /* phs->sv is copy of real variable, upgrade to at least string */ - (void)SvUPGRADE(phs->sv, SVt_PV); - } + *dest++ = *start++; + while ((*dest++ = *start++)) + if ('"' == *(dest-1)) + break; - /* At this point phs->sv must be at least a PV with a valid buffer, */ - /* even if it's undef (null) */ - /* Here we set phs->progv, phs->indp, and value_len. */ - if (SvOK(phs->sv)) { - phs->progv = SvPV(phs->sv, value_len); - phs->indp = 0; - } - else { /* it's null but point to buffer in case it's an out var */ - phs->progv = SvPVX(phs->sv); - phs->indp = -1; - value_len = 0; - } - phs->sv_type = SvTYPE(phs->sv); /* part of mutation check */ - phs->maxlen = SvLEN(phs->sv)-1; /* avail buffer space */ - if (phs->maxlen < 0) { /* can happen with nulls */ - phs->maxlen = 0; - } + *dest = '\0'; - phs->alen = value_len + phs->alen_incnull; + // PerlIO_printf(DBILOGFP, "Rewritten stmt: %s, Max Len: %i, Act Len:%i\n", stmt, max_len, strlen(stmt)); - imp_sth->all_params_len += SvOK(phs->sv) ? phs->alen : 4; /* NULL */ + result = PQexec(imp_dbh->conn, stmt); + Safefree(stmt); - if (dbis->debug >= 3) { - PerlIO_printf(DBILOGFP, " bind %s <== '%.*s' (size %ld/%ld, otype %d, indp %d)\n", - phs->name, - (int)(phs->alen>SvIV(DBIS->neatsvpvlen) ? SvIV(DBIS->neatsvpvlen) : phs->alen), - (phs->progv) ? phs->progv : "", - (long)phs->alen, (long)phs->maxlen, phs->ftype, phs->indp); - } + status = result ? PQresultStatus(result) : -1; + PQclear(result); + if (PGRES_COMMAND_OK != status) { + pg_error(sth,status, PQerrorMessage(imp_dbh->conn)); + return -1; + } return 1; + } + +/* TODO: break this sub up. */ int dbd_bind_ph (sth, imp_sth, ph_namesv, newvalue, sql_type, attribs, is_inout, maxlen) SV *sth; @@ -990,15 +697,24 @@ IV maxlen; { SV **phs_svp; + SV **svp; STRLEN name_len; - char *name; + char *name = Nullch; char namebuf[30]; phs_t *phs; + sql_type_info_t *sql_type_info; + int pg_type, bind_type; + char *value_string; + int value_len; + if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_bind_ph\n"); } - /* check if placeholder was passed as a number */ + if (is_inout) + croak("bind_inout not supported by this driver"); + + /* check if placeholder was passed as a number */ if (SvGMAGICAL(ph_namesv)) { /* eg if from tainted expression */ mg_get(ph_namesv); } @@ -1006,24 +722,25 @@ name = SvPV(ph_namesv, name_len); } if (SvNIOKp(ph_namesv) || (name && isDIGIT(name[0]))) { - sprintf(namebuf, ":p%d", (int)SvIV(ph_namesv)); + sprintf(namebuf, "$%d", (int)SvIV(ph_namesv)); name = namebuf; name_len = strlen(name); + assert(name_len < sizeof(namebuf)); } assert(name != Nullch); - if (SvTYPE(newvalue) > SVt_PVLV) { /* hook for later array logic */ + if (SvTYPE(newvalue) > SVt_PVLV) { /* hook for later array logic */ croak("Can't bind a non-scalar value (%s)", neatsvpv(newvalue,0)); } if (SvROK(newvalue) && !IS_DBI_HANDLE(newvalue)) { /* dbi handle allowed for cursor variables */ croak("Can't bind a reference (%s)", neatsvpv(newvalue,0)); } - if (SvTYPE(newvalue) == SVt_PVLV && is_inout) { /* may allow later */ + if (SvTYPE(newvalue) == SVt_PVLV && is_inout) { /* may allow later */ croak("Can't bind ``lvalue'' mode scalar as inout parameter (currently)"); } - if (dbis->debug >= 2) { + if (dbis->debug >= 2) { PerlIO_printf(DBILOGFP, " bind %s <== %s (type %ld", name, neatsvpv(newvalue,0), (long)sql_type); if (is_inout) { PerlIO_printf(DBILOGFP, ", inout 0x%lx, maxlen %ld", (long)newvalue, (long)maxlen); @@ -1034,81 +751,97 @@ PerlIO_printf(DBILOGFP, ")\n"); } - phs_svp = hv_fetch(imp_sth->all_params_hv, name, name_len, 0); - if (phs_svp == NULL) { - croak("Can't bind unknown placeholder '%s' (%s)", name, neatsvpv(ph_namesv,0)); - } - phs = (phs_t*)(void*)SvPVX(*phs_svp); /* placeholder struct */ - if (phs->sv == &sv_undef) { /* first bind for this placeholder */ - phs->ftype = 1043; /* our default type VARCHAR */ - phs->is_inout = is_inout; - if (is_inout) { - /* phs->sv assigned in the code below */ - ++imp_sth->has_inout_params; - /* build array of phs's so we can deal with out vars fast */ - if (!imp_sth->out_params_av) { - imp_sth->out_params_av = newAV(); + // XXX this is broken: bind_param(1,1,{TYPE=>SQL_INTEGER}); + if (attribs) { + if (sql_type) + croak ("Cannot specify both sql_type and pg_type"); + + if ((svp = hv_fetch((HV*)SvRV(attribs),"pg_type", 7, 0))==NULL) + croak("DBD::ChurlPg only knows about the pg_type attribute"); + + pg_type = SvIV(*svp); + + + if ((sql_type_info = pg_type_data(pg_type))) { + if (!sql_type_info->bind_ok) { + croak("Can't bind %s, pg_type %s not supported" + "by DBD::ChurlPg", + name, sql_type_info->type_name); + } } - av_push(imp_sth->out_params_av, SvREFCNT_inc(*phs_svp)); - } + bind_type = sql_type_info->type_id; - if (attribs) { /* only look for pg_type on first bind of var */ - SV **svp; - /* Setup / Clear attributes as defined by attribs. */ - /* XXX If attribs is EMPTY then reset attribs to default? */ - if ( (svp = hv_fetch((HV*)SvRV(attribs), "pg_type", 7, 0)) != NULL) { - int pg_type = SvIV(*svp); - if (!pgtype_bind_ok(pg_type)) { - croak("Can't bind %s, pg_type %d not supported by DBD::Pg", phs->name, pg_type); - } - if (sql_type) { - croak("Can't specify both TYPE (%d) and pg_type (%d) for %s", sql_type, pg_type, phs->name); - } - phs->ftype = pg_type; - } - } - if (sql_type) { - /* SQL_BINARY (-2) is deprecated. */ - if (sql_type == -2 && DBIc_WARN(imp_sth)) { - warn("Use of SQL type SQL_BINARY (%d) is deprecated. Use { pg_type => DBD::Pg::PG_BYTEA } instead.", sql_type); + } else if (sql_type) { + + if ((sql_type_info = sql_type_data(sql_type))) { + /* always bind as pg_type, because we know we are inserting + into a pg database... It would make no sense to quote + something to sql semantics and break the insert. + */ + bind_type = sql_type_info->type.pg; + } else { + croak("Cannot bind %s unknown sql_type %i", + name, sql_type); } - phs->ftype = pg_sql_type(imp_sth, phs->name, sql_type); - } - } /* was first bind for this placeholder */ - /* check later rebinds for any changes */ - else if (is_inout || phs->is_inout) { - croak("Can't rebind or change param %s in/out mode after first bind (%d => %d)", phs->name, phs->is_inout , is_inout); + } else { + sql_type_info = pg_type_data(VARCHAROID); + if (!sql_type_info) + croak("Default type is bad!!!!???"); + + bind_type = sql_type_info->type_id; } - else if (sql_type && phs->ftype != pg_sql_type(imp_sth, phs->name, sql_type)) { - croak("Can't change TYPE of param %s to %d after initial bind", phs->name, sql_type); + + + /* get the place holder */ + phs_svp = hv_fetch(imp_sth->all_params_hv, name, name_len, 0); + if (phs_svp == NULL) { + croak("Can't bind unknown placeholder '%s' (%s)", + name, neatsvpv(ph_namesv,0)); } + phs = (phs_t*)(void*)SvPVX(*phs_svp); - phs->maxlen = maxlen; /* 0 if not inout */ - if (!is_inout) { /* normal bind to take a (new) copy of current value */ - if (phs->sv == &sv_undef) { /* (first time bind) */ - phs->sv = newSV(0); - } - sv_setsv(phs->sv, newvalue); - } else if (newvalue != phs->sv) { - if (phs->sv) { - SvREFCNT_dec(phs->sv); - } - phs->sv = SvREFCNT_inc(newvalue); /* point to live var */ + if (phs->is_bound && phs->ftype != bind_type) { + croak("Can't change TYPE of param %s to %d after initial bind", + phs->name, sql_type); + } else { + phs->ftype = bind_type; + } + + /* convert to a string ASAP */ + if (!SvPOK(newvalue) && SvOK(newvalue)) { + sv_2pv(newvalue, &na); + } + /* phs->sv is copy of real variable, upgrade to at least string */ + (void)SvUPGRADE(newvalue, SVt_PV); + + + if (!SvOK(newvalue)) { + phs->quoted = strdup("NULL"); + if (NULL == phs->quoted) + croak("No memory"); + phs->quoted_len = strlen(phs->quoted); + } else { + value_string = SvPV(newvalue, value_len); + phs->quoted = sql_type_info->quote( + value_string, value_len, &phs->quoted_len + ); } - return dbd_rebind_ph(sth, imp_sth, phs); + phs->is_bound = 1; + return 1; + } -int +/*TODO: make smaller */ dbd_st_execute (sth, imp_sth) /* <= -2:error, >=0:ok row count, (-1=unknown count) */ SV *sth; imp_sth_t *imp_sth; { - dTHR; + //dTHR; D_imp_dbh_from_sth; ExecStatusType status = -1; @@ -1117,171 +850,54 @@ char *statement; int ret = -2; int num_fields; - int i; - STRLEN len; - bool in_literal = FALSE; - char in_comment = '\0'; - char *src; - char *dest; - char *val; - char namebuf[30]; - phs_t *phs; - SV **svp; + int max_len =0; if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_execute\n"); } - /* - here we get the statement from the statement handle where - it has been stored when creating a blank sth during prepare - svp = hv_fetch((HV *)SvRV(sth), "Statement", 9, FALSE); - statement = SvPV(*svp, na); - */ - if (NULL == imp_dbh->conn) { - pg_error(sth, -1, "execute on disconnected handle"); + pg_error(sth, -1, "execute on disconnected handle"); return -2; - } - - statement = imp_sth->statement; - if (! statement) { - /* are we prepared ? */ + } + + if (! imp_sth->statement) { pg_error(sth, -1, "statement not prepared\n"); return -2; } + max_len = strlen(imp_sth->statement)+1; /* do we have input parameters ? */ if ((int)DBIc_NUM_PARAMS(imp_sth) > 0) { - /* we have to allocate some additional memory for possible escaping quotes and backslashes */ - /* Worst case is all character must be binary-escaped (\\xxx) */ - int max_len = imp_sth->all_params_len * 5 + DBIc_NUM_PARAMS(imp_sth) * 2 + 1; - statement = (char*)safemalloc(strlen(imp_sth->statement) + max_len ); - dest = statement; - src = imp_sth->statement; - /* scan statement for ':p1' style placeholders */ - while(*src) { - - if (in_comment) { - /* SQL-style and C++-style */ - if ((in_comment == '-' || in_comment == '/') && *src == '\n') { - in_comment = '\0'; - } - /* C-style */ - else if (in_comment == '*' && *src == '*' && *(src+1) == '/') { - *dest++ = *src++; /* avoids asterisk-slash-asterisk issues */ - in_comment = '\0'; - } - *dest++ = *src++; - continue; - } - - if (in_literal) { - /* check if literal ends but keep quotes in literal */ - if (*src == in_literal) { - int bs=0; - char *str; - str = src-1; - while (*(str-bs) == '\\') - bs++; - if (!(bs & 1)) - in_literal = 0; + /* How much do we need to malloc to hold resultant string */ + HV *hv = imp_sth->all_params_hv; + SV *sv; + char *key; + I32 retlen; + hv_iterinit(hv); + //PerlIO_printf(DBILOGFP, "b4 max_len: %i\n", max_len); + while( (sv = hv_iternextsv(hv, &key, &retlen)) != NULL ) { + if (sv != &sv_undef) { + phs_t *phs_tpl = (phs_t*)(void*)SvPVX(sv); + if (!phs_tpl->is_bound) { + pg_error(sth, -1, + "Execute called with unbound placeholder"); + return -2; } - *dest++ = *src++; - continue; + max_len += phs_tpl->quoted_len * phs_tpl->count; } + } - /* Look for comments: SQL-style or C++-style or C-style */ - if ((*src == '-' && *(src+1) == '-') || - (*src == '/' && *(src+1) == '/') || - (*src == '/' && *(src+1) == '*')) - { - in_comment = *(src+1); - /* We know *src & the next char are to be copied, so do */ - /* it. In the case of C-style comments, it happens to */ - /* help us avoid slash-asterisk-slash oddities. */ - *dest++ = *src++; - *dest++ = *src++; - continue; - } + Newc(0, statement, max_len, char, char); - /* check if no placeholders */ - if (*src != ':' && *src != '?') { - if (*src == '\'' || *src == '"') { - in_literal = *src; - } - *dest++ = *src++; - continue; - } + /* scan statement for '$1' style placeholders and replace with values*/ + if ((ret = rewrite_execute_stmt(sth, imp_sth, statement, sth)) < 0) + return ret; + } else { + statement = imp_sth->statement; + } - /* check for cast operator */ - if (*src == ':' && (*(src-1) == ':' || *(src+1) == ':')) { - *dest++ = *src++; - continue; - } + assert(strlen(statement)+1 <= max_len); - i = 0; - namebuf[i++] = *src++; /* ':' */ - namebuf[i++] = *src++; /* 'p' */ - - while (isDIGIT(*src) && i < (sizeof(namebuf)-1) ) { - namebuf[i++] = *src++; - } - if ( i == (sizeof(namebuf) - 1)) { - pg_error(sth, -1, "namebuf buffer overrun\n"); - return -2; - } - namebuf[i] = '\0'; - svp = hv_fetch(imp_sth->all_params_hv, namebuf, i, 0); - if (svp == NULL) { - pg_error(sth, -1, "parameter unknown\n"); - return -2; - } - /* get attribute */ - phs = (phs_t*)(void*)SvPVX(*svp); - /* replace undef with NULL */ - if(!SvOK(phs->sv)) { - val = "NULL"; - len = 4; - } else { - val = SvPV(phs->sv, len); - } - /* quote string attribute */ - if(!SvNIOK(phs->sv) && SvOK(phs->sv) && pg_sql_needquote(phs->ftype)) { /* avoid quoting NULL, tpf: bind_param as numeric */ - *dest++ = '\''; - } - while (len--) { - if (imp_dbh->pg_auto_escape) { - /* if the parameter was bound as PG_BYTEA, escape nonprintables */ - if (phs->ftype == 17 && !isPRINT(*val)) { /* escape null character */ - dest+=snprintf(dest, strlen(imp_sth->statement) + max_len + (statement - dest), "\\\\%03o", *((unsigned char *)val)); - val++; - continue; /* do not copy the null */ - } - /* escape quote */ - if (*val == '\'') { - *dest++ = '\''; - } - /* escape backslash */ - if (*val == '\\') { - if (phs->ftype == 17) { /* four backslashes. really. */ - *dest++ = '\\'; - *dest++ = '\\'; - *dest++ = '\\'; - } else { - *dest++ = '\\'; - } - } - } - /* copy attribute to statement */ - *dest++ = *val++; - } - /* quote string attribute */ - if(!SvNIOK(phs->sv) && SvOK(phs->sv) && pg_sql_needquote(phs->ftype)) { /* avoid quoting NULL, tpf: bind_param as numeric */ - *dest++ = '\''; - } - } - *dest = '\0'; - } if (dbis->debug >= 2) { PerlIO_printf(DBILOGFP, "dbd_st_execute: statement = >%s<\n", statement); } @@ -1332,14 +948,23 @@ } + + + + +/*TODO: pg_bool_tf && chob_blanks + +*/ + AV * dbd_st_fetch (sth, imp_sth) SV *sth; imp_sth_t *imp_sth; { - D_imp_dbh_from_sth; + sql_type_info_t *type_info; int num_fields; - int i; + char *value; + int i, pg_type, value_len; AV *av; if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_fetch\n"); } @@ -1347,7 +972,7 @@ /* Check that execute() was executed sucessfully */ if ( !DBIc_ACTIVE(imp_sth) ) { pg_error(sth, 1, "no statement executing\n"); - + return Nullav; } @@ -1366,164 +991,27 @@ if (PQgetisnull(imp_sth->result, imp_sth->cur_tuple, i)) { sv_setsv(sv, &sv_undef); } else { - char *val = (char*)PQgetvalue(imp_sth->result, imp_sth->cur_tuple, i); - int val_len = strlen(val); - int type = PQftype(imp_sth->result, i); /* hopefully these hard coded values will not change */ - if (16 == type && ! imp_dbh->pg_bool_tf) { - *val = (*val == 'f') ? '0' : '1'; /* bool: translate postgres into perl */ - } - if (17 == type) { /* decode \001 -> chr(1), etc, in-place */ - char *p = val; /* points to next available pos */ - char *s = val; /* points to current scanning pos */ - int c1,c2,c3; - while (*s) { - if (*s == '\\') { - if (*(s+1) == '\\') { /* double backslash */ - *p++ = '\\'; - s += 2; - continue; - } - else if ( isdigit(c1=(*(s+1))) && - isdigit(c2=(*(s+2))) && - isdigit(c3=(*(s+3))) ) { - *p++ = (c1 - '0') * 64 + (c2 - '0') * 8 + (c3 - '0'); - s += 4; - continue; - } - } - *p++ = *s++; - } - val_len = (p - val); - } - else if (1042 == type && DBIc_has(imp_sth,DBIcf_ChopBlanks)) { - char *str = val; - while((val_len > 0) && (str[val_len-1] == ' ')) { - val_len--; - } - val[val_len] = '\0'; - } - sv_setpvn(sv, val, val_len); - } - } + value = (char*)PQgetvalue(imp_sth->result, imp_sth->cur_tuple, i); - imp_sth->cur_tuple += 1; + pg_type = PQftype(imp_sth->result, i); + type_info = pg_type_data(pg_type); - return av; -} + if (type_info) + type_info->dequote(value, &value_len); /* dequote in place */ + else + value_len = strlen(value); - -int -dbd_st_blob_read (sth, imp_sth, lobjId, offset, len, destrv, destoffset) - SV *sth; - imp_sth_t *imp_sth; - int lobjId; - long offset; - long len; - SV *destrv; - long destoffset; -{ - D_imp_dbh_from_sth; - int ret, lobj_fd, nbytes, nread; - PGresult* result; - ExecStatusType status; - SV *bufsv; - char *tmp; - - if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_blob_read\n"); } - /* safety check */ - if (lobjId <= 0) { - pg_error(sth, -1, "dbd_st_blob_read: lobjId <= 0"); - return 0; - } - if (offset < 0) { - pg_error(sth, -1, "dbd_st_blob_read: offset < 0"); - return 0; - } - if (len < 0) { - pg_error(sth, -1, "dbd_st_blob_read: len < 0"); - return 0; - } - if (! SvROK(destrv)) { - pg_error(sth, -1, "dbd_st_blob_read: destrv not a reference"); - return 0; - } - if (destoffset < 0) { - pg_error(sth, -1, "dbd_st_blob_read: destoffset < 0"); - return 0; - } - - /* dereference destination and ensure it's writable string */ - bufsv = SvRV(destrv); - if (! destoffset) { - sv_setpvn(bufsv, "", 0); - } - - /* execute begin - result = PQexec(imp_dbh->conn, "begin"); - status = result ? PQresultStatus(result) : -1; - PQclear(result); - if (status != PGRES_COMMAND_OK) { - pg_error(sth, status, PQerrorMessage(imp_dbh->conn)); - return 0; - } - */ - - /* open large object */ - lobj_fd = lo_open(imp_dbh->conn, lobjId, INV_READ); - if (lobj_fd < 0) { - pg_error(sth, -1, PQerrorMessage(imp_dbh->conn)); - return 0; - } - - /* seek on large object */ - if (offset > 0) { - ret = lo_lseek(imp_dbh->conn, lobj_fd, offset, SEEK_SET); - if (ret < 0) { - pg_error(sth, -1, PQerrorMessage(imp_dbh->conn)); - return 0; + sv_setpvn(sv, value, value_len); } } - /* read from large object */ - nread = 0; - SvGROW(bufsv, destoffset + nread + BUFSIZ + 1); - tmp = (SvPVX(bufsv)) + destoffset + nread; - while ((nbytes = lo_read(imp_dbh->conn, lobj_fd, tmp, BUFSIZ)) > 0) { - nread += nbytes; - /* break if user wants only a specified chunk */ - if (len > 0 && nread > len) { - nread = len; - break; - } - SvGROW(bufsv, destoffset + nread + BUFSIZ + 1); - tmp = (SvPVX(bufsv)) + destoffset + nread; - } - - /* terminate string */ - SvCUR_set(bufsv, destoffset + nread); - *SvEND(bufsv) = '\0'; - - /* close large object */ - ret = lo_close(imp_dbh->conn, lobj_fd); - if (ret < 0) { - pg_error(sth, -1, PQerrorMessage(imp_dbh->conn)); - return 0; - } - - /* execute end - result = PQexec(imp_dbh->conn, "end"); - status = result ? PQresultStatus(result) : -1; - PQclear(result); - if (status != PGRES_COMMAND_OK) { - pg_error(sth, status, PQerrorMessage(imp_dbh->conn)); - return 0; - } - */ + imp_sth->cur_tuple += 1; - return nread; + return av; } +/* TODO: test for rows and define rows so that this rows() will be used */ int dbd_st_rows (sth, imp_sth) SV *sth; @@ -1564,15 +1052,21 @@ /* Free off contents of imp_sth */ + if (imp_sth->server_prepared) + if (deallocate_statement(sth, imp_sth) < 1) + warn("Something Ugly Happened. And whatever it was, it caused" + "us not to be able to deallocate the prepared statement. " + "Prolly a tx went bad or something like that"); + Safefree(imp_sth->statement); + if (imp_sth->place_holders) + Safefree(imp_sth->place_holders); + if (imp_sth->result) { PQclear(imp_sth->result); imp_sth->result = 0; } - if (imp_sth->out_params_av) - sv_free((SV*)imp_sth->out_params_av); - if (imp_sth->all_params_hv) { HV *hv = imp_sth->all_params_hv; SV *sv; @@ -1582,7 +1076,8 @@ while( (sv = hv_iternextsv(hv, &key, &retlen)) != NULL ) { if (sv != &sv_undef) { phs_t *phs_tpl = (phs_t*)(void*)SvPVX(sv); - sv_free(phs_tpl->sv); + /* sv_free(phs_tpl->sv); */ + free(phs_tpl->quoted); } } sv_free((SV*)imp_sth->all_params_hv); @@ -1615,6 +1110,9 @@ char *key = SvPV(keysv,kl); int i, sz; SV *retsv = Nullsv; + char *type_name; + sql_type_info_t *type_info; + if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_FETCH\n"); } @@ -1635,10 +1133,9 @@ AV *av = newAV(); retsv = newRV(sv_2mortal((SV*)av)); while(--i >= 0) { - av_store(av, i, newSViv(sql_pg_type( imp_sth, - PQfname(imp_sth->result, i), - PQftype(imp_sth->result, i)))); - } + type_info = sql_type_data(PQftype(imp_sth->result, i)); + av_store(av, i, newSViv( type_info ? type_info->type.sql : 0 ) ); + } } else if (kl==9 && strEQ(key, "PRECISION")) { AV *av = newAV(); retsv = newRV(sv_2mortal((SV*)av)); @@ -1670,259 +1167,14 @@ } } else if (kl==7 && strEQ(key, "pg_type")) { AV *av = newAV(); - char *type_nam; retsv = newRV(sv_2mortal((SV*)av)); + while(--i >= 0) { - switch (PQftype(imp_sth->result, i)) { - case 16: - type_nam = "bool"; - break; - case 17: - type_nam = "bytea"; - break; - case 18: - type_nam = "char"; - break; - case 19: - type_nam = "name"; - break; - case 20: - type_nam = "int8"; - break; - case 21: - type_nam = "int2"; - break; - case 22: - type_nam = "int28"; - break; - case 23: - type_nam = "int4"; - break; - case 24: - type_nam = "regproc"; - break; - case 25: - type_nam = "text"; - break; - case 26: - type_nam = "oid"; - break; - case 27: - type_nam = "tid"; - break; - case 28: - type_nam = "xid"; - break; - case 29: - type_nam = "cid"; - break; - case 30: - type_nam = "oid8"; - break; - case 32: - type_nam = "SET"; - break; - case 210: - type_nam = "smgr"; - break; - case 600: - type_nam = "point"; - break; - case 601: - type_nam = "lseg"; - break; - case 602: - type_nam = "path"; - break; - case 603: - type_nam = "box"; - break; - case 604: - type_nam = "polygon"; - break; - case 605: - type_nam = "filename"; - break; - case 628: - type_nam = "line"; - break; - case 629: - type_nam = "_line"; - break; - case 700: - type_nam = "float4"; - break; - case 701: - type_nam = "float8"; - break; - case 702: - type_nam = "abstime"; - break; - case 703: - type_nam = "reltime"; - break; - case 704: - type_nam = "tinterval"; - break; - case 705: - type_nam = "unknown"; - break; - case 718: - type_nam = "circle"; - break; - case 719: - type_nam = "_circle"; - break; - case 790: - type_nam = "money"; - break; - case 791: - type_nam = "_money"; - break; - case 810: - type_nam = "oidint2"; - break; - case 910: - type_nam = "oidint4"; - break; - case 911: - type_nam = "oidname"; - break; - case 1000: - type_nam = "_bool"; - break; - case 1001: - type_nam = "_bytea"; - break; - case 1002: - type_nam = "_char"; - break; - case 1003: - type_nam = "_name"; - break; - case 1005: - type_nam = "_int2"; - break; - case 1006: - type_nam = "_int28"; - break; - case 1007: - type_nam = "_int4"; - break; - case 1008: - type_nam = "_regproc"; - break; - case 1009: - type_nam = "_text"; - break; - case 1028: - type_nam = "_oid"; - break; - case 1010: - type_nam = "_tid"; - break; - case 1011: - type_nam = "_xid"; - break; - case 1012: - type_nam = "_cid"; - break; - case 1013: - type_nam = "_oid8"; - break; - case 1014: - type_nam = "_lock"; - break; - case 1015: - type_nam = "_stub"; - break; - case 1016: - type_nam = "_ref"; - break; - case 1017: - type_nam = "_point"; - break; - case 1018: - type_nam = "_lseg"; - break; - case 1019: - type_nam = "_path"; - break; - case 1020: - type_nam = "_box"; - break; - case 1021: - type_nam = "_float4"; - break; - case 1022: - type_nam = "_float8"; - break; - case 1023: - type_nam = "_abstime"; - break; - case 1024: - type_nam = "_reltime"; - break; - case 1025: - type_nam = "_tinterval"; - break; - case 1026: - type_nam = "_filename"; - break; - case 1027: - type_nam = "_polygon"; - break; - case 1033: - type_nam = "aclitem"; - break; - case 1034: - type_nam = "_aclitem"; - break; - case 1042: - type_nam = "bpchar"; - break; - case 1043: - type_nam = "varchar"; - break; - case 1082: - type_nam = "date"; - break; - case 1083: - type_nam = "time"; - break; - case 1182: - type_nam = "_date"; - break; - case 1183: - type_nam = "_time"; - break; - case 1184: - type_nam = "datetime"; - break; - case 1185: - type_nam = "_datetime"; - break; - case 1186: - type_nam = "timespan"; - break; - case 1187: - type_nam = "_timespan"; - break; - case 1231: - type_nam = "_numeric"; - break; - case 1296: - type_nam = "timestamp"; - break; - case 1700: - type_nam = "numeric"; - break; - - default: - type_nam = "unknown"; - - } - av_store(av, i, newSVpv(type_nam, 0)); + + type_info = pg_type_data(PQftype(imp_sth->result,i)); + type_name = (type_info) ? type_info->type_name : "unknown"; + av_store(av, i, newSVpv(type_name, 0)); + } } else if (kl==13 && strEQ(key, "pg_oid_status")) { retsv = newSVpv((char *)PQoidStatus(imp_sth->result), 0); diff -uN orig/dbdpg/dbdimp.h dbdpg/dbdimp.h --- orig/dbdpg/dbdimp.h Mon Jan 13 20:05:56 2003 +++ dbdpg/dbdimp.h Sat Feb 8 00:16:37 2003 @@ -25,6 +25,27 @@ int init_commit; /* initialize AutoCommit */ int pg_auto_escape; /* initialize AutoEscape */ int pg_bool_tf; /* do bools return 't'/'f' */ + struct { + int major; + int minor; + double ver; + } version; +}; + + +#define sword signed int +#define sb2 signed short +#define ub2 unsigned short +typedef struct phs_st phs_t; /* scalar placeholder */ + +struct phs_st { /* scalar placeholder EXPERIMENTAL */ + int ftype; /* field type */ + char *quoted; /* Quoted value bound to placeholder*/ + size_t quoted_len; + unsigned int count; + bool is_bound; + + char name[1]; /* struct is malloc'd bigger as needed */ }; /* Define sth implementor data structure */ @@ -38,40 +59,13 @@ /* Input Details */ char *statement; /* sql (see sth_scan) */ HV *all_params_hv; /* all params, keyed by name */ - AV *out_params_av; /* quick access to inout params */ - int pg_pad_empty; /* convert ""->" " when binding */ - int all_params_len; /* length-sum of all params */ - - /* (In/)Out Parameter Details */ - bool has_inout_params; -}; + bool server_prepared; /* Did we prepare this server side?*/ + phs_t **place_holders; + unsigned int phc; -#define sword signed int -#define sb2 signed short -#define ub2 unsigned short - -typedef struct phs_st phs_t; /* scalar placeholder */ - -struct phs_st { /* scalar placeholder EXPERIMENTAL */ - sword ftype; /* external OCI field type */ - - SV *sv; /* the scalar holding the value */ - int sv_type; /* original sv type at time of bind */ - bool is_inout; - - IV maxlen; /* max possible len (=allocated buffer) */ - - /* these will become an array */ - sb2 indp; /* null indicator */ - char *progv; - ub2 arcode; - IV alen; /* effective length ( <= maxlen ) */ - - int alen_incnull; /* 0 or 1 if alen should include null */ - char name[1]; /* struct is malloc'd bigger as needed */ + /*char *orig_statement; */ /*? Origional SQL statement for debug?? ?*/ }; - SV * dbd_db_pg_notifies (SV *dbh, imp_dbh_t *imp_dbh); Common subdirectories: orig/dbdpg/eg and dbdpg/eg Common subdirectories: orig/dbdpg/inc and dbdpg/inc diff -uN orig/dbdpg/large_object.c dbdpg/large_object.c --- orig/dbdpg/large_object.c Wed Dec 31 19:00:00 1969 +++ dbdpg/large_object.c Sat Feb 8 00:04:00 2003 @@ -0,0 +1,249 @@ +int +pg_db_lo_open (dbh, lobjId, mode) + SV *dbh; + unsigned int lobjId; + int mode; +{ + D_imp_dbh(dbh); + return lo_open(imp_dbh->conn, lobjId, mode); +} + + +int +pg_db_lo_close (dbh, fd) + SV *dbh; + int fd; +{ + D_imp_dbh(dbh); + return lo_close(imp_dbh->conn, fd); +} + + +int +pg_db_lo_read (dbh, fd, buf, len) + SV *dbh; + int fd; + char *buf; + int len; +{ + D_imp_dbh(dbh); + return lo_read(imp_dbh->conn, fd, buf, len); +} + + +int +pg_db_lo_write (dbh, fd, buf, len) + SV *dbh; + int fd; + char *buf; + int len; +{ + D_imp_dbh(dbh); + return lo_write(imp_dbh->conn, fd, buf, len); +} + + +int +pg_db_lo_lseek (dbh, fd, offset, whence) + SV *dbh; + int fd; + int offset; + int whence; +{ + D_imp_dbh(dbh); + return lo_lseek(imp_dbh->conn, fd, offset, whence); +} + + +unsigned int +pg_db_lo_creat (dbh, mode) + SV *dbh; + int mode; +{ + D_imp_dbh(dbh); + return lo_creat(imp_dbh->conn, mode); +} + + +int +pg_db_lo_tell (dbh, fd) + SV *dbh; + int fd; +{ + D_imp_dbh(dbh); + return lo_tell(imp_dbh->conn, fd); +} + + +int +pg_db_lo_unlink (dbh, lobjId) + SV *dbh; + unsigned int lobjId; +{ + D_imp_dbh(dbh); + return lo_unlink(imp_dbh->conn, lobjId); +} + + +unsigned int +pg_db_lo_import (dbh, filename) + SV *dbh; + char *filename; +{ + D_imp_dbh(dbh); + return lo_import(imp_dbh->conn, filename); +} + + +int +pg_db_lo_export (dbh, lobjId, filename) + SV *dbh; + unsigned int lobjId; + char *filename; +{ + D_imp_dbh(dbh); + return lo_export(imp_dbh->conn, lobjId, filename); +} + + +int +pg_db_putline (dbh, buffer) + SV *dbh; + char *buffer; +{ + D_imp_dbh(dbh); + return PQputline(imp_dbh->conn, buffer); +} + + +int +pg_db_getline (dbh, buffer, length) + SV *dbh; + char *buffer; + int length; +{ + D_imp_dbh(dbh); + return PQgetline(imp_dbh->conn, buffer, length); +} + + +int +pg_db_endcopy (dbh) + SV *dbh; +{ + D_imp_dbh(dbh); + return PQendcopy(imp_dbh->conn); +} + + +int +dbd_st_blob_read (sth, imp_sth, lobjId, offset, len, destrv, destoffset) + SV *sth; + imp_sth_t *imp_sth; + int lobjId; + long offset; + long len; + SV *destrv; + long destoffset; +{ + D_imp_dbh_from_sth; + int ret, lobj_fd, nbytes, nread; + /* PGresult* result; + ExecStatusType status; */ + SV *bufsv; + char *tmp; + + if (dbis->debug >= 1) { PerlIO_printf(DBILOGFP, "dbd_st_blob_read\n"); } + /* safety check */ + if (lobjId <= 0) { + pg_error(sth, -1, "dbd_st_blob_read: lobjId <= 0"); + return 0; + } + if (offset < 0) { + pg_error(sth, -1, "dbd_st_blob_read: offset < 0"); + return 0; + } + if (len < 0) { + pg_error(sth, -1, "dbd_st_blob_read: len < 0"); + return 0; + } + if (! SvROK(destrv)) { + pg_error(sth, -1, "dbd_st_blob_read: destrv not a reference"); + return 0; + } + if (destoffset < 0) { + pg_error(sth, -1, "dbd_st_blob_read: destoffset < 0"); + return 0; + } + + /* dereference destination and ensure it's writable string */ + bufsv = SvRV(destrv); + if (! destoffset) { + sv_setpvn(bufsv, "", 0); + } + + /* execute begin + result = PQexec(imp_dbh->conn, "begin"); + status = result ? PQresultStatus(result) : -1; + PQclear(result); + if (status != PGRES_COMMAND_OK) { + pg_error(sth, status, PQerrorMessage(imp_dbh->conn)); + return 0; + } + */ + + /* open large object */ + lobj_fd = lo_open(imp_dbh->conn, lobjId, INV_READ); + if (lobj_fd < 0) { + pg_error(sth, -1, PQerrorMessage(imp_dbh->conn)); + return 0; + } + + /* seek on large object */ + if (offset > 0) { + ret = lo_lseek(imp_dbh->conn, lobj_fd, offset, SEEK_SET); + if (ret < 0) { + pg_error(sth, -1, PQerrorMessage(imp_dbh->conn)); + return 0; + } + } + + /* read from large object */ + nread = 0; + SvGROW(bufsv, destoffset + nread + BUFSIZ + 1); + tmp = (SvPVX(bufsv)) + destoffset + nread; + while ((nbytes = lo_read(imp_dbh->conn, lobj_fd, tmp, BUFSIZ)) > 0) { + nread += nbytes; + /* break if user wants only a specified chunk */ + if (len > 0 && nread > len) { + nread = len; + break; + } + SvGROW(bufsv, destoffset + nread + BUFSIZ + 1); + tmp = (SvPVX(bufsv)) + destoffset + nread; + } + + /* terminate string */ + SvCUR_set(bufsv, destoffset + nread); + *SvEND(bufsv) = '\0'; + + /* close large object */ + ret = lo_close(imp_dbh->conn, lobj_fd); + if (ret < 0) { + pg_error(sth, -1, PQerrorMessage(imp_dbh->conn)); + return 0; + } + + /* execute end + result = PQexec(imp_dbh->conn, "end"); + status = result ? PQresultStatus(result) : -1; + PQclear(result); + if (status != PGRES_COMMAND_OK) { + pg_error(sth, status, PQerrorMessage(imp_dbh->conn)); + return 0; + } + */ + + return nread; +} + diff -uN orig/dbdpg/large_object.h dbdpg/large_object.h --- orig/dbdpg/large_object.h Wed Dec 31 19:00:00 1969 +++ dbdpg/large_object.h Sat Feb 8 00:04:07 2003 @@ -0,0 +1,14 @@ +int pg_db_lo_open (); +int pg_db_lo_close (); +int pg_db_lo_read (); +int pg_db_lo_write (); +int pg_db_lo_lseek (); +unsigned int pg_db_lo_creat (); +int pg_db_lo_tell (); +int pg_db_lo_unlink (); +unsigned int pg_db_lo_import (); +int pg_db_lo_export (); +int pg_db_putline (); +int pg_db_getline (); +int pg_db_endcopy (); +int dbd_db_ping (); diff -uN orig/dbdpg/pg_typeOID.h dbdpg/pg_typeOID.h --- orig/dbdpg/pg_typeOID.h Wed Dec 31 19:00:00 1969 +++ dbdpg/pg_typeOID.h Tue Feb 11 19:21:43 2003 @@ -0,0 +1,65 @@ +/* This comes from: + +grep '^#define' /data/dnloads/postgresql-7.3/src/include/catalog/pg_type.h |grep OID >pg_typeOID.h + +*/ + +#define BOOLOID 16 +#define BYTEAOID 17 +#define CHAROID 18 +#define NAMEOID 19 +#define INT8OID 20 +#define INT2OID 21 +#define INT2VECTOROID 22 +#define INT4OID 23 +#define REGPROCOID 24 +#define TEXTOID 25 +#define OIDOID 26 +#define TIDOID 27 +#define XIDOID 28 +#define CIDOID 29 +#define OIDVECTOROID 30 +#define POINTOID 600 +#define LSEGOID 601 +#define PATHOID 602 +#define BOXOID 603 +#define POLYGONOID 604 +#define LINEOID 628 +#define FLOAT4OID 700 +#define FLOAT8OID 701 +#define ABSTIMEOID 702 +#define RELTIMEOID 703 +#define TINTERVALOID 704 +#define UNKNOWNOID 705 +#define CIRCLEOID 718 +#define CASHOID 790 +#define MACADDROID 829 +#define INETOID 869 +#define CIDROID 650 +#define ACLITEMOID 1033 +#define BPCHAROID 1042 +#define VARCHAROID 1043 +#define DATEOID 1082 +#define TIMEOID 1083 +#define TIMESTAMPOID 1114 +#define TIMESTAMPTZOID 1184 +#define INTERVALOID 1186 +#define TIMETZOID 1266 +#define BITOID 1560 +#define VARBITOID 1562 +#define NUMERICOID 1700 +#define REFCURSOROID 1790 +#define REGPROCEDUREOID 2202 +#define REGOPEROID 2203 +#define REGOPERATOROID 2204 +#define REGCLASSOID 2205 +#define REGTYPEOID 2206 +#define RECORDOID 2249 +#define CSTRINGOID 2275 +#define ANYOID 2276 +#define ANYARRAYOID 2277 +#define VOIDOID 2278 +#define TRIGGEROID 2279 +#define LANGUAGE_HANDLEROID 2280 +#define INTERNALOID 2281 +#define OPAQUEOID 2282 diff -uN orig/dbdpg/prescan_stmt.c dbdpg/prescan_stmt.c --- orig/dbdpg/prescan_stmt.c Wed Dec 31 19:00:00 1969 +++ dbdpg/prescan_stmt.c Sat Feb 8 00:04:19 2003 @@ -0,0 +1,456 @@ +/******************* + * pre_scan_stmt() + * returns the length of the statement and + * an estimate of how many place holders it contains. + */ + +void +prescan_stmt (stmt, stmt_len, place_holder_count) + const char *stmt; + int *stmt_len; + int *place_holder_count; +{ + char ch; + int length = 0; + int phc = 0; + + while ((ch = *stmt)) { + if (':' == ch || '?' == ch || '$' == ch) + ++phc; + ++length; + ++stmt; + } + + *stmt_len = length; + *place_holder_count = phc; +} + + + +/******************* + * clc_ph_space() + * givin a place_holder count, retuns the + * string space needed to hold them. + */ + +size_t +calc_ph_space (place_holder_count) + int place_holder_count; +{ + int divisor = 10,i; + int digits = 2; /* 2: 1 for " " 1 for "$" eg: ' $1' */ + size_t total_length = 0 ; + + for (i=1; i<=place_holder_count; ++i) { + if (i%divisor == 0) { /* this could be made more eff. */ + //PerlIO_printf(DBILOGFP, " \tDigits:%i\n", digits); + divisor *=10; + ++digits; + } + total_length += digits; + } + return total_length; +} + + + +/******************* + * is_dml() + * givin a statement/fragment makes a guess as to whether + * it be a DML statement + */ + +int +is_dml (stmt) + const char *stmt; +{ + char token[7]; + + /* skip any leading whitespace */ + while (*stmt && (isSPACE(*stmt) || '\n' == *stmt) ) + ++stmt; + + /* must be the first non-whitespace token */ + /* TODO: Check size of stmt */ + strncpy (token, stmt, 6); + + token[6] = '\0'; + // PerlIO_printf(DBILOGFP, "token: stmt: %s\n", token); + + /* XXX: UPDATE & INSERT are broken. The (varchar) hack does not work + as they actually look at the field type. Until I get a fix for this + we don't prepare them + */ + if ( !strcasecmp(token, "SELECT") + || !strcasecmp(token, "DELETE") + /*|| !strcasecmp(token, "UPDATE") + || !strcasecmp(token, "INSERT")*/ ) + { + //PerlIO_printf(DBILOGFP, "Is DML\n"); + return 1; + } + // PerlIO_printf(DBILOGFP, "Is not DML\n"); + return 0; +} + + + + +/******************* + * is_tx_stmt() + * decides if a statement is a tx type statement + */ + +int +is_tx_stmt (stmt) + const char *stmt; +{ + char token[10]; + + /* skip any leading whitespace */ + while (*stmt && (isSPACE(*stmt) || '\n' == *stmt) ) + ++stmt; + + /* must be the first non-whitespace token */ + /* TODO: Check size of stmt */ + strncpy (token, stmt, 8); + + token[9] = '\0'; + // PerlIO_printf(DBILOGFP, "token: stmt: %s\n", token); + + if ( !strncasecmp(token, "END", 4) + || !strncasecmp(token, "BEGIN", 5) + || !strncasecmp(token, "ABORT", 5) + || !strncasecmp(token, "COMMIT", 6) + || !strncasecmp(token, "ROLLBACK",8) ) + { + //PerlIO_printf(DBILOGFP, "Is DML\n"); + return 1; + } + // PerlIO_printf(DBILOGFP, "Is not DML\n"); + return 0; +} + + + + +/******************* + * scan_placeholders() + * old preparse. this one takes a statement and sets up + * the place holder SV* + */ + +int +rewrite_placeholders (imp_sth, statement, internal, human) + imp_sth_t *imp_sth; + char *statement; + char *internal; + char *human; + +{ + phs_t phs_tpl; + phs_t *phs; + SV *phs_sv; + SV **hv; + char *src, *dest, *style = "\0", *laststyle = Nullch; + int ch, namelen; + int in_comment=0, in_literal=0; + unsigned int place_holder_count =0; + char *ph_name_start; + + memset(&phs_tpl, 0, sizeof(phs_tpl)); + + if(human); /* use it */ + + src = statement; + dest = internal; + + // PerlIO_printf(DBILOGFP, "HERE: stmt: %s\n", src); + while ((ch = *src++)) { + if (in_comment) { + /* SQL-style and C++-style */ + if ((in_comment == '-' || in_comment == '/') && + '\n' == ch) + { + in_comment = '\0'; + + } else if (in_comment == '*' && '*' == ch && + '/' == *src) /* C style */ + { + /* *dest++ = ch; */ + /* avoids asterisk-slash-asterisk issues */ + ch = *src++; + in_comment = '\0'; + } + /* *dest++ = ch; */ + continue; + } + + if (in_literal) { + /* check if literal ends but keep quotes in literal */ + if (ch == in_literal) { + int back_slashes=0; + char *str; + str = src-2; + while (*(str-back_slashes) == '\\') + ++back_slashes; + + /* odd number of '\'s ? */ + if (!(back_slashes & 1)) + in_literal = 0; + } + *dest++ = ch; + continue; + } + + /* Look for comments: SQL-style or C++-style or C-style */ + if (('-' == ch && '-' == *src) || + ('/' == ch && '/' == *src) || + ('/' == ch && '*' == *src)) + { + in_comment = *src; + /* We know *src & the next char are to be copied, so do + it. In the case of C-style comments, it happens to + help us avoid slash-asterisk-slash oddities. */ + /* *dest++ = ch; */ + continue; + } + + + /* collapse whitespace */ + if ('\n' == ch) { + *(src-1) = ' '; + ch = ' '; + } + if (isSPACE(ch) && src-2 > statement && + isSPACE(*(src-2)) ) + { + continue; + } + + /* check if no placeholders */ + if (':' != ch && '?' != ch && '$' != ch) { + if ('\'' == ch || '"' == ch) + in_literal = ch; + else if ('[' == ch) /* ignore arrays ex. foo[1:3] */ + in_literal = ']'; + + *dest++ = ch; + continue; + } + + /* cast */ + if (':' == ch && ':'== *src) { + *dest++ = ch; + *dest++ = *src++; + continue; + } + + if (ch != '?' && !isALNUM(*src)) + continue; + + + sprintf(dest," $%d", ++place_holder_count); + namelen = strlen(dest); + dest += namelen; + + ph_name_start = src-1; + if ('?' == ch) { /* X/Open standard */ + namelen--; /* Leading " " */ + ph_name_start = dest-namelen; + style = "?"; + } else if (isDIGIT(*src)) { /* '(:/$)1' */ + namelen = 1; + while(isDIGIT(*src)) { + ++namelen; + ++src; + } + style = ":1"; + } else if (isALNUM(*src)) { /* ':foo' */ + namelen = 1; + while(isALNUM(*src)){ /* includes '_' */ + ++namelen; + ++src; + } + style = ":foo"; + } + + if (laststyle && style != laststyle) { + croak("Can't mix placeholder styles (%s/%s)", + style,laststyle); + } + laststyle = style; + + + if (imp_sth->all_params_hv == NULL) { + imp_sth->all_params_hv = newHV(); + } + + //PerlIO_printf(DBILOGFP, "phs name start:%s len: %i Index:%i\n", + // ph_name_start,namelen, place_holder_count); + + hv =hv_fetch(imp_sth->all_params_hv,ph_name_start,namelen,0); + + if (NULL == hv) { + phs_sv = newSV(sizeof(phs_tpl)+namelen+1); + Zero(SvPVX(phs_sv), sizeof(phs_tpl)+namelen+1, char); + hv_store( imp_sth->all_params_hv, + ph_name_start,namelen,phs_sv,0); + + memcpy( ((phs_t*)SvPVX(phs_sv))->name, + ph_name_start,namelen); + *(((phs_t*)SvPVX(phs_sv))->name+namelen+1)='\0'; + } else { + phs_sv = *hv; + } + phs = (phs_t *)SvPVX(phs_sv); + phs->count++; /* Number with this name */ + imp_sth->place_holders[place_holder_count] = phs; + } + + if (place_holder_count) { + DBIc_NUM_PARAMS(imp_sth) = place_holder_count; + if (dbis->debug >= 2) { + PerlIO_printf(DBILOGFP, + " dbd_preparse scanned %d" + " placeholders\n", (int)DBIc_NUM_PARAMS(imp_sth)); + } + } + *dest = '\0'; + return place_holder_count; +} + + + + +/******************* + * build_preamble() + * sticks the SQL needed to prepare/execute a statement + * at the head of the statement. + * type: is one of PREPARE or EXECUTE + */ + +void +build_preamble (statement, type, place_holder_count, prep_stmt_id) + char *statement; + /* const char *type; */ + int type; + int place_holder_count; + int prep_stmt_id; +{ + int i; + char *keyword; + + if (1 == type) + keyword = "PREPARE"; + else if (2 == type) + keyword = "EXECUTE"; + else + croak("error"); + + + sprintf(statement, + "%s \"DBD::ChurlPg::cached_query %i\"", keyword, prep_stmt_id); + + //PerlIO_printf(DBILOGFP, "statement: %s\n", statement); + + if (!place_holder_count) { + statement += strlen(statement); + if (1 == type) + memcpy(statement, " AS ",4); + else if (2 == type) + *statement = '\0'; /* chop off sql statement */ + else + croak("error"); + return; + } + + strcat(statement, " ("); + statement += strlen(statement); + + for (i =1; i <= place_holder_count; ++i) { + if (type == 1) + sprintf(statement, "varchar"); + if (type == 2) + sprintf(statement, "$%i", i); + + if (place_holder_count != i) + strcat(statement, ", "); + + statement += strlen(statement); + } + + if (1 == type) + memcpy(statement, ") AS ", 5); /*finish off */ + else if (2 == type) + memcpy(statement, ")\0 ", 2); /*finish off */ + else + croak("error"); +} + + + +/******************* + * rewrite_execute_stmt() + * rewrites the execute statement to include the + * quoted parameters for the placeholders + * + */ + +int +rewrite_execute_stmt(sth, imp_sth, output) + SV* sth; + imp_sth_t *imp_sth; + char *output; +{ + const char *src, *statement; + char *dest; + char *end; + char ch; + phs_t *phs; + unsigned long ph; + bool in_literal = 0; + + src = statement = imp_sth->statement; + dest = output; + while ((ch = *src++)) { + if (in_literal) { + /* check if literal ends but keep quotes in literal */ + if (ch == in_literal) { + int back_slashes=0; + const char *str; + str = src-2; + while (*(str-back_slashes) == '\\') + ++back_slashes; + /* Odd number of '\'s ? */ + if (!(back_slashes & 1)) + in_literal = 0; + } + } + /* check if no placeholders */ + if (('$' != ch) || !isDIGIT(*src)) { + if ('\'' == ch || '"' == ch) { + in_literal = ch; + } + *dest++ = ch; + continue; + } + + ph = strtol(src, &end, 10); + src = end; + + assert(ph <= imp_sth->phc); + phs = imp_sth->place_holders[ph]; + if (!phs) + croak("DBD::Pg Bug -- Invalid Placeholder"); + + memcpy(dest, phs->quoted, phs->quoted_len); + dest += phs->quoted_len; + } + *dest = '\0'; + + return 0; +} + + + diff -uN orig/dbdpg/quote.c dbdpg/quote.c --- orig/dbdpg/quote.c Wed Dec 31 19:00:00 1969 +++ dbdpg/quote.c Sun Feb 9 22:41:07 2003 @@ -0,0 +1,527 @@ +#include "Pg.h" +#include "types.h" +#include + + +// #include"pg_functions.c" + + +/* This section was stolen from libpq */ +#ifndef PQescapeString +size_t +PQescapeString(char *to, const char *from, size_t length) +{ + const char *source = from; + char *target = to; + unsigned int remaining = length; + + while (remaining > 0) + { + switch (*source) + { + case '\\': + *target = '\\'; + target++; + *target = '\\'; + /* target and remaining are updated below. */ + break; + + case '\'': + *target = '\''; + target++; + *target = '\''; + /* target and remaining are updated below. */ + break; + + default: + *target = *source; + /* target and remaining are updated below. */ + } + source++; + target++; + remaining--; + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} +#endif + +#ifndef PQescapeBytea +/* + * PQescapeBytea - converts from binary string to the + * minimal encoding necessary to include the string in an SQL + * INSERT statement with a bytea type column as the target. + * + * The following transformations are applied + * '\0' == ASCII 0 == \\000 + * '\'' == ASCII 39 == \' + * '\\' == ASCII 92 == \\\\ + * anything >= 0x80 ---> \\ooo (where ooo is an octal expression) + */ +unsigned char * +PQescapeBytea(unsigned char *bintext, size_t binlen, size_t *bytealen) +{ + unsigned char *vp; + unsigned char *rp; + unsigned char *result; + size_t i; + size_t len; + + /* + * empty string has 1 char ('\0') + */ + len = 1; + + vp = bintext; + for (i = binlen; i > 0; i--, vp++) + { + if (*vp == 0 || *vp >= 0x80) + len += 5; /* '5' is for '\\ooo' */ + else if (*vp == '\'') + len += 2; + else if (*vp == '\\') + len += 4; + else + len++; + } + + rp = result = (unsigned char *) malloc(len); + if (rp == NULL) + return NULL; + + vp = bintext; + *bytealen = len; + + for (i = binlen; i > 0; i--, vp++) + { + if (*vp == 0 || *vp >= 0x80) + { + (void) sprintf(rp, "\\\\%03o", *vp); + rp += 5; + } + else if (*vp == '\'') + { + rp[0] = '\\'; + rp[1] = '\''; + rp += 2; + } + else if (*vp == '\\') + { + rp[0] = '\\'; + rp[1] = '\\'; + rp[2] = '\\'; + rp[3] = '\\'; + rp += 4; + } + else + *rp++ = *vp; + } + *rp = '\0'; + + return result; +} +#endif /*PQescapeBytea */ + +/* + * PQunescapeBytea - converts the null terminated string representation + * of a bytea, strtext, into binary, filling a buffer. It returns a + * pointer to the buffer which is NULL on error, and the size of the + * buffer in retbuflen. The pointer may subsequently be used as an + * argument to the function free(3). It is the reverse of PQescapeBytea. + * + * The following transformations are reversed: + * '\0' == ASCII 0 == \000 + * '\'' == ASCII 39 == \' + * '\\' == ASCII 92 == \\ + * + * States: + * 0 normal 0->1->2->3->4 + * 1 \ 1->5 + * 2 \0 1->6 + * 3 \00 + * 4 \000 + * 5 \' + * 6 \\ + */ +#ifndef PQunescapeBytea +unsigned char * +PQunescapeBytea(unsigned char *strtext, size_t *retbuflen) +{ + size_t buflen; + unsigned char *buffer, + *sp, + *bp; + unsigned int state = 0; + + if (strtext == NULL) + return NULL; + buflen = strlen(strtext); /* will shrink, also we discover if + * strtext */ + buffer = (unsigned char *) malloc(buflen); /* isn't NULL terminated */ + if (buffer == NULL) + return NULL; + for (bp = buffer, sp = strtext; *sp != '\0'; bp++, sp++) + { + switch (state) + { + case 0: + if (*sp == '\\') + state = 1; + *bp = *sp; + break; + case 1: + if (*sp == '\'') /* state=5 */ + { /* replace \' with 39 */ + bp--; + *bp = '\''; + buflen--; + state = 0; + } + else if (*sp == '\\') /* state=6 */ + { /* replace \\ with 92 */ + bp--; + *bp = '\\'; + buflen--; + state = 0; + } + else + { + if (isdigit(*sp)) + state = 2; + else + state = 0; + *bp = *sp; + } + break; + case 2: + if (isdigit(*sp)) + state = 3; + else + state = 0; + *bp = *sp; + break; + case 3: + if (isdigit(*sp)) /* state=4 */ + { + int v; + + bp -= 3; + sscanf(sp - 2, "%03o", &v); + *bp = v; + buflen -= 3; + state = 0; + } + else + { + *bp = *sp; + state = 0; + } + break; + } + } + buffer = realloc(buffer, buflen); + if (buffer == NULL) + return NULL; + + *retbuflen = buflen; + return buffer; +} +#endif /*PQunescapeBytea */ + + + +char * +dbd_quote (string, pg_type, length, retlen) + char *string; + int pg_type; + size_t length; + size_t *retlen; +{ + sql_type_info_t *type_info; + char *retval; + + type_info = pg_type_data(pg_type); + if(!type_info) + croak("No Type info"); + + retval = type_info->quote(string, length, retlen); + return retval; +} + + +char * +null_quote(string, len, retlen) + void *string; + size_t len; + size_t *retlen; +{ + char *result; + Newc(0,result,len+1,char, char); + strncpy(result,string, len); + *retlen = len; + return result; +} + + +char * +quote_varchar(string, len, retlen) + char *string; + size_t len; + size_t *retlen; +{ + size_t outlen; + char *result; + + + Newc(0,result,len*2+3,char, char); + outlen = PQescapeString(result+1, string, len); + + // TODO: remalloc outlen + *result = '\''; + outlen++; + *(result+outlen)='\''; + outlen++; + *(result+outlen)='\0'; + *retlen = outlen; + return result; +} + +char * +quote_char(string, len, retlen) + void *string; + size_t len; + size_t *retlen; +{ + size_t outlen; + char *result; + + // TODO: ChopBlanks + Newc(0,result,len*2+3,char, char); + outlen = PQescapeString(result+1, string, len); + + // TODO: remalloc outlen + *result = '\''; + outlen++; + *(result+outlen)='\''; + outlen++; + *(result+outlen)='\0'; + *retlen = outlen; + return result; +} + + +char * +quote_sql_binary( string, len, retlen) + void *string; + size_t len; + size_t *retlen; +{ + char *result; + char *dest; + int max_len = 0, i; + + /* +4 == 3 for X''; 1 for \0 */ + max_len = len*2+4; + Newc(0, result, max_len,char,char); + + + dest = result; + memcpy(dest++, "X'",1); + + for (i=0 ; i <= len ; ++i, dest+=2) + sprintf(dest, "%X", *((char*)string++)); + + strcat(dest, "\'"); + + *retlen = strlen(result); + assert(*retlen+1 <= max_len); + return result; +} + + + + +char * +quote_bytea(string, len, retlen) + void* string; + size_t len; + size_t *retlen; +{ + char *result; + size_t resultant_len =0; + char *intermead = "", *dest; + + intermead = PQescapeBytea(string, len, &resultant_len); + Newc(0,result,resultant_len+2,char, char); + + + dest = result; + + memcpy(dest++, "'",1); + strcpy(dest,intermead); + strcat(dest,"\'"); + + free(intermead); + *retlen=strlen(result); + assert(*retlen+1 <= resultant_len+2); + + + return result; +} + + +char * +quote_bool(value, len, retlen) + void *value; + size_t len; + size_t *retlen; +{ + char *result; + long int int_value; + size_t max_len=6; + + if (isDIGIT(*(char*)value)) { + /* For now -- will go away when quote* take SVs */ + int_value = atoi(value); + } else { + int_value = 42; /* Not true, not false. Just is */ + } + Newc(0,result,max_len,char,char); + + if (0 == int_value) + strcpy(result,"FALSE"); + else if (1 == int_value) + strcpy(result,"TRUE"); + else + croak("Error: Bool must be either 1 or 0"); + + *retlen = strlen(result); + assert(*retlen+1 <= max_len); + + return result; +} + + + +char * +quote_integer(value, len, retlen) + void *value; + size_t len; + size_t *retlen; +{ + char *result; + size_t max_len=6; + + Newc(0,result,max_len,char,char); + + if (*((int*)value) == 0) + strcpy(result,"FALSE"); + if (*((int*)value) == 1) + strcpy(result,"TRUE"); + + *retlen = strlen(result); + assert(*retlen+1 <= max_len); + + return result; +} + + + + +void +dequote_char(string, retlen) + char *string; + int *retlen; +{ + //TODO: chop_blanks if requested + *retlen = strlen(string); +} + + +void +dequote_varchar (string, retlen) + char *string; + int *retlen; +{ + *retlen = strlen(string); +} + + + +void +dequote_bytea(string, retlen) + char *string; + int *retlen; +{ + char *s, *p; + int c1,c2,c3; + /* Stolen right from dbdquote. This probably should be cleaned up + & made more robust. Maybe later... + */ + s = string; + p = string; + while (*s) { + if (*s == '\\') { + if (*(s+1) == '\\') { /* double backslash */ + *p++ = '\\'; + s += 2; + continue; + } else if ( isdigit(c1=(*(s+1))) && + isdigit(c2=(*(s+2))) && + isdigit(c3=(*(s+3))) ) + { + *p++ = (c1-'0') * 64 + (c2-'0') * 8 + (c3-'0'); + s += 4; + continue; + } + } + *p++ = *s++; + } + *retlen = (p-string); +} + + + +/* + This one is not used in PG, but since we have a quote_sql_binary, + it might be nice to let people go the other way too. Say when talking + to something that uses SQL_BINARY + */ +void +dequote_sql_binary (string, retlen) + char *string; + int *retlen; +{ + *retlen = strlen(string); +} + + + +void +dequote_bool (string, retlen) + char *string; + int *retlen; +{ + switch(*string){ + case 'f': *string = '0'; break; + case 't': *string = '1'; break; + default: + croak("I do not know how to deal with %c as a bool", + *string); + } + *retlen = 1; +} + + + +void +null_dequote (string, retlen) + void *string; + size_t *retlen; +{ + *retlen = strlen(string); +} + diff -uN orig/dbdpg/quote.h dbdpg/quote.h --- orig/dbdpg/quote.h Wed Dec 31 19:00:00 1969 +++ dbdpg/quote.h Sat Feb 8 00:00:27 2003 @@ -0,0 +1,17 @@ +#ifndef DBDQUOTEH +#define DBDQUOTEH +char * dbd_quote(); +char * null_quote(); +char * quote_varchar(); +char * quote_char(); +char * quote_sql_binary(); +char * quote_bytea(); +char * quote_bool() ; +char * quote_integer() ; +void dequote_char(); +void dequote_varchar(); +void dequote_bytea(); +void dequote_sql_binary(); +void dequote_bool(); +void null_dequote(); +#endif /*DBDQUOTEH*/ Common subdirectories: orig/dbdpg/t and dbdpg/t diff -uN orig/dbdpg/types.c dbdpg/types.c --- orig/dbdpg/types.c Wed Dec 31 19:00:00 1969 +++ dbdpg/types.c Sat Feb 8 16:45:45 2003 @@ -0,0 +1,192 @@ +#include "quote.h" + +#include "Pg.h" +#include "types.h" + + + + +#define TRUE 1 +#define FALSE 0 +/* For quoting/sql type mapping purposes this table only knows about + the types that DBD::Pg knew about before. The other tyeps are just + here for returning the field type. + +TODO: - expand this for use with type_info() + - map all types to closest sql type. + - set up quote functions for remaining types + - autogeneratet this file. +*/ + +static sql_type_info_t pg_types[] = { + {BOOLOID, "bool", TRUE, quote_bool, dequote_bool, {SQL_INTEGER}}, + {BYTEAOID, "bytea", TRUE, quote_bytea, dequote_bytea, {SQL_BINARY}}, + {CHAROID, "char", FALSE, quote_char, dequote_char, {0}}, + {NAMEOID, "name", FALSE, null_quote, null_dequote, {SQL_VARCHAR}}, + {INT8OID, "int8", TRUE, null_quote, null_dequote, {SQL_DOUBLE}}, + {INT2OID, "int2", TRUE, null_quote, null_dequote, {SQL_SMALLINT}}, + {INT2VECTOROID, "int28", FALSE, null_quote, null_dequote, {0}}, + {INT4OID, "int4", 2, null_quote, null_dequote, {SQL_INTEGER}}, + {REGPROCOID, "regproc", FALSE, null_quote, null_dequote, {0}}, + {TEXTOID, "text", TRUE, quote_varchar, dequote_varchar, {SQL_VARCHAR}}, + {OIDOID, "oid", TRUE, null_quote, null_dequote, {SQL_INTEGER}}, + {TIDOID, "tid", TRUE, null_quote, null_dequote, {SQL_INTEGER}}, + {XIDOID, "xid", TRUE, null_quote, null_dequote, {SQL_INTEGER}}, + {CIDOID, "cid", TRUE, null_quote, null_dequote, {SQL_INTEGER}}, + {OIDVECTOROID, "oid8", FALSE, null_quote, null_dequote, {0}}, + {POINTOID, "point", FALSE, null_quote, null_dequote, {0}}, + {LSEGOID, "lseg", FALSE, null_quote, null_dequote, {0}}, + {PATHOID, "path", FALSE, null_quote, null_dequote, {0}}, + {BOXOID, "box", FALSE, null_quote, null_dequote, {0}}, + {POLYGONOID, "polygon", FALSE, null_quote, null_dequote, {0}}, + {LINEOID, "line", FALSE, null_quote, null_dequote, {0}}, + {FLOAT4OID, "float4", TRUE, quote_char, dequote_char, {SQL_NUMERIC}}, + {FLOAT8OID, "float8", TRUE, null_quote,null_dequote, {SQL_REAL}}, + {ABSTIMEOID, "abstime", TRUE, null_quote, null_dequote, {0}}, + {RELTIMEOID, "reltime", TRUE, null_quote, null_dequote, {0}}, + {TINTERVALOID, "tinterval", TRUE, null_quote, null_dequote, {0}}, + {UNKNOWNOID, "unknown", FALSE, null_quote, null_dequote, {0}}, + {CIRCLEOID, "circle", FALSE, null_quote, null_dequote, {0}}, + {CASHOID, "money", TRUE, null_quote, null_dequote, {0}}, + {MACADDROID, "MAC address", TRUE, quote_varchar,dequote_varchar, {0}}, + {INETOID, "IP address", TRUE, null_quote, null_dequote, {0}}, + {CIDROID, "IP - cidr", TRUE, null_quote, null_dequote, {0}}, + {ACLITEMOID, "aclitem", FALSE, null_quote, null_dequote, {0}}, + {BPCHAROID, "bpchar", TRUE, quote_char, dequote_char, {SQL_CHAR}}, + {VARCHAROID, "varchar", TRUE, quote_varchar, dequote_varchar, {SQL_VARCHAR}}, + {DATEOID, "date", TRUE, null_quote, null_dequote, {0}}, + {TIMEOID, "time", TRUE, null_quote, null_dequote, {0}}, + {TIMESTAMPOID, "timestamp", TRUE, null_quote, null_dequote, {0}}, + {TIMESTAMPTZOID, "datetime", TRUE, null_quote, null_dequote, {0}}, + {INTERVALOID, "timespan", TRUE, null_quote, null_dequote, {0}}, + {TIMETZOID, "timestamptz", TRUE, null_quote, null_dequote, {0}}, + {BITOID, "bitstring", TRUE, null_quote, null_dequote, {0}}, + {VARBITOID, "vbitstring", TRUE, null_quote, null_dequote, {0}}, + {NUMERICOID, "numeric", TRUE, null_quote, null_dequote, {SQL_DECIMAL}}, + {REFCURSOROID, "refcursor", FALSE, null_quote, null_dequote, {0}}, + {REGPROCEDUREOID, "regprocedureoid", FALSE, null_quote, null_dequote, {0}}, + {REGOPEROID, "registeredoperator", FALSE, null_quote, null_dequote, {0}}, + {REGOPERATOROID, "registeroperator_args ", FALSE, null_quote, null_dequote, {0}}, + {REGCLASSOID, "regclass", FALSE, null_quote, null_dequote, {0}}, + {REGTYPEOID, "regtype", FALSE, null_quote, null_dequote, {0}}, + {RECORDOID, "record", FALSE, null_quote, null_dequote, {0}}, + {CSTRINGOID, "cstring", FALSE, null_quote, null_dequote, {0}}, + {ANYOID, "any", FALSE, null_quote, null_dequote, {0}}, + {ANYARRAYOID, "anyarray", FALSE, null_quote, null_dequote, {0}}, + {VOIDOID, "void", FALSE, null_quote, null_dequote, {0}}, + {TRIGGEROID, "trigger", FALSE, null_quote, null_dequote, {0}}, + {LANGUAGE_HANDLEROID, "languagehandle", FALSE, null_quote, null_dequote, {0}}, + {INTERNALOID, "internal", FALSE, null_quote, null_dequote, {0}}, + {OPAQUEOID, "opaque", FALSE, null_quote, null_dequote, {0}}, +}; + +sql_type_info_t* +pg_type_data(sql_type) + int sql_type; +{ + switch(sql_type) { + + case BOOLOID: return &pg_types[0]; + case BYTEAOID: return &pg_types[1]; + case CHAROID: return &pg_types[2]; + case NAMEOID: return &pg_types[3]; + case INT8OID: return &pg_types[4]; + case INT2OID: return &pg_types[5]; + case INT2VECTOROID: return &pg_types[6]; + case INT4OID: return &pg_types[7]; + case REGPROCOID: return &pg_types[8]; + case TEXTOID: return &pg_types[9]; + case OIDOID: return &pg_types[10]; + case TIDOID: return &pg_types[11]; + case XIDOID: return &pg_types[12]; + case CIDOID: return &pg_types[13]; + case OIDVECTOROID: return &pg_types[14]; + case POINTOID: return &pg_types[15]; + case LSEGOID: return &pg_types[16]; + case PATHOID: return &pg_types[17]; + case BOXOID: return &pg_types[18]; + case POLYGONOID: return &pg_types[19]; + case LINEOID: return &pg_types[20]; + case FLOAT4OID: return &pg_types[21]; + case FLOAT8OID: return &pg_types[22]; + case ABSTIMEOID: return &pg_types[23]; + case RELTIMEOID: return &pg_types[24]; + case TINTERVALOID: return &pg_types[25]; + case UNKNOWNOID: return &pg_types[26]; + case CIRCLEOID: return &pg_types[27]; + case CASHOID: return &pg_types[28]; + case MACADDROID: return &pg_types[29]; + case INETOID: return &pg_types[30]; + case CIDROID: return &pg_types[31]; + case ACLITEMOID: return &pg_types[32]; + case BPCHAROID: return &pg_types[33]; + case VARCHAROID: return &pg_types[34]; + case DATEOID: return &pg_types[35]; + case TIMEOID: return &pg_types[36]; + case TIMESTAMPOID: return &pg_types[37]; + case TIMESTAMPTZOID: return &pg_types[38]; + case INTERVALOID: return &pg_types[39]; + case TIMETZOID: return &pg_types[40]; + case BITOID: return &pg_types[41]; + case VARBITOID: return &pg_types[42]; + case NUMERICOID: return &pg_types[43]; + case REFCURSOROID: return &pg_types[44]; + case REGPROCEDUREOID: return &pg_types[45]; + case REGOPEROID: return &pg_types[46]; + case REGOPERATOROID: return &pg_types[47]; + case REGCLASSOID: return &pg_types[48]; + case REGTYPEOID: return &pg_types[49]; + case RECORDOID: return &pg_types[50]; + case CSTRINGOID: return &pg_types[51]; + case ANYOID: return &pg_types[52]; + case ANYARRAYOID: return &pg_types[53]; + case VOIDOID: return &pg_types[54]; + case TRIGGEROID: return &pg_types[55]; + case LANGUAGE_HANDLEROID: return &pg_types[56]; + case INTERNALOID: return &pg_types[57]; + case OPAQUEOID: return &pg_types[58]; + + + + default: return NULL; + } +} + + + + +/* This table only knows about the types that dbd_pg knew about before + TODO: Put the rest of the sql types in here with mapping. +*/ +static sql_type_info_t sql_types[] = { + {SQL_VARCHAR, "SQL_VARCHAR", TRUE,quote_varchar, dequote_varchar, {VARCHAROID}}, + {SQL_CHAR, "SQL_CHAR", TRUE, quote_char, dequote_char, {BPCHAROID}}, + {SQL_NUMERIC, "SQL_NUMERIC", TRUE, null_quote, null_dequote, {FLOAT4OID}}, + {SQL_DECIMAL, "SQL_DECIMAL", TRUE, null_quote, null_dequote, {FLOAT4OID}}, + {SQL_INTEGER, "SQL_INTEGER", TRUE, null_quote, null_dequote, {INT4OID}}, + {SQL_SMALLINT, "SQL_SMALLINT", TRUE, null_quote, null_dequote, {INT2OID}}, + {SQL_FLOAT, "SQL_FLOAT", TRUE, null_quote, null_dequote, {FLOAT4OID}}, + {SQL_REAL, "SQL_REAL", TRUE, null_quote, null_dequote, {FLOAT8OID}}, + {SQL_DOUBLE, "SQL_DOUBLE", TRUE, null_quote, null_dequote, {INT8OID}}, + {SQL_BINARY, "SQL_BINARY", TRUE, quote_sql_binary, dequote_sql_binary, {BYTEAOID}}, + +}; + +sql_type_info_t* +sql_type_data(sql_type) + int sql_type; +{ + switch(sql_type) { + case SQL_VARCHAR: return &sql_types[0]; + case SQL_CHAR: return &sql_types[1]; + case SQL_NUMERIC: return &sql_types[2]; + case SQL_DECIMAL: return &sql_types[3]; + case SQL_INTEGER: return &sql_types[4]; + case SQL_SMALLINT: return &sql_types[5]; + case SQL_FLOAT: return &sql_types[6]; + case SQL_REAL: return &sql_types[7]; + case SQL_DOUBLE: return &sql_types[8]; + case SQL_BINARY: return &sql_types[9]; + default: return NULL; + } +} diff -uN orig/dbdpg/types.h dbdpg/types.h --- orig/dbdpg/types.h Wed Dec 31 19:00:00 1969 +++ dbdpg/types.h Sat Feb 8 00:03:50 2003 @@ -0,0 +1,24 @@ +#ifndef DBDPGTYEPSH +#define DBDPGTYEPSH +#include "pg_typeOID.h" + + +/* TODO: Add type_info stuff */ +typedef struct sql_type_info { + int type_id; /* 16 */ + char *type_name; /* bool */ + bool bind_ok; /* 1 */ + char* (*quote)(); + void (*dequote)(); /* 0 if no need to dequote */ + union { + int pg; + int sql; /* closest SQL/PG_WHATEVER Type */ + } type; +} sql_type_info_t; + + +sql_type_info_t* pg_type_data(); +sql_type_info_t* sql_type_data(); + +#endif /*DBDPGTYEPSH */ +