--- parser3/src/sql/pgsql/Attic/parser3pgsql.C 2001/07/31 12:44:36 1.4 +++ parser3/src/sql/pgsql/Attic/parser3pgsql.C 2001/09/05 08:57:43 1.13 @@ -7,18 +7,22 @@ 2001.07.30 using PgSQL 7.1.2 */ -static const char *RCSId="$Id: parser3pgsql.C,v 1.4 2001/07/31 12:44:36 parser Exp $"; +static const char *RCSId="$Id: parser3pgsql.C,v 1.13 2001/09/05 08:57:43 parser Exp $"; #include "config_includes.h" #include "pa_sql_driver.h" #include -#include +#include -// #include +// OIDOID from catalog/pg_type.h #define OIDOID 26 -#define LO_BUFSIZE 8192 +// LO_BUFSIZE from interfaces\libpq\fe-lobj.c = 8192 (0x2000) +// actually writing chunks of that size failed, reduced it twice +#define LO_BUFSIZE 0x1000 +// from postgres_ext.h +#define InvalidOid ((Oid) 0) #include "ltdl.h" @@ -63,12 +67,12 @@ public: return dlopen_file_spec? dlink(dlopen_file_spec):"client library column is empty"; } + + #define throwPQerror services._throw(PQerrorMessage(conn)) + /** connect @param used_only_in_connect_url format: @b user:pass@host[:port]|[local]/database - 3.23.22b - Currently the only option for @b character_set_name is cp1251_koi8. - WARNING: must be used only to connect, for buffer doesn't live long */ void connect( char *used_only_in_connect_url, @@ -81,38 +85,34 @@ public: char *pwd=lsplit(user, ':'); char *port=lsplit(host, ':'); -// _asm int 3; PGconn *conn=PQsetdbLogin( strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port, NULL, NULL, db, user, pwd); if(!conn) services._throw("PQsetdbLogin failed"); if(PQstatus(conn)!=CONNECTION_OK) - services._throw(PQerrorMessage(conn)); + throwPQerror; *(PGconn **)connection=conn; begin_transaction(services, conn); } - void disconnect(SQL_Driver_services&, void *connection) { -// _asm int 3; + void disconnect(void *connection) { PQfinish((PGconn *)connection); } void commit(SQL_Driver_services& services, void *connection) { -// _asm int 3; PGconn *conn=(PGconn *)connection; if(PGresult *res=PQexec(conn, "COMMIT")) PQclear(res); else - services._throw(PQerrorMessage(conn)); + throwPQerror; begin_transaction(services, conn); } void rollback(SQL_Driver_services& services, void *connection) { -// _asm int 3; PGconn *conn=(PGconn *)connection; if(PGresult *res=PQexec(conn, "ROLLBACK")) PQclear(res); else - services._throw(PQerrorMessage(conn)); + throwPQerror; begin_transaction(services, conn); } @@ -124,11 +124,6 @@ public: SQL_Driver_services&, void *connection, char *to, const char *from, unsigned int length) { /* - 3.23.22b - You must allocate the to buffer to be at least length*2+1 bytes long. - (In the worse case, each character may need to be encoded as using two bytes, - and you need room for the terminating null byte.) - it's already UNTAINT_TIMES_BIGGER */ unsigned int result=length; @@ -149,49 +144,40 @@ public: SQL_Driver_services& services, void *connection, const char *astatement, unsigned long offset, unsigned long limit, SQL_Driver_query_event_handlers& handlers) { +// _asm int 3; PGconn *conn=(PGconn *)connection; + #define PQclear_throw(msg) { \ + PQclear(res); \ + services._throw(msg); \ + } + #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(conn)) - const char *statement; - if(offset || limit) { - size_t statement_size=strlen(astatement); - char *statement_limited=(char *)services.malloc( - statement_size+MAX_NUMBER*2+15/* limit # offset #*/+1); - char *cur=statement_limited; - memcpy(cur, astatement, statement_size); cur+=statement_size; - if(limit) - cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit); - if(offset) - cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset); - statement=statement_limited; - } else - statement=astatement; + const char *statement=preprocess_statement(services, conn, + astatement, offset, limit); PGresult *res=PQexec(conn, statement); if(!res) - services._throw(PQerrorMessage(conn)); + throwPQerror; switch(PQresultStatus(res)) { case PGRES_EMPTY_QUERY: - PQclear(res); - services._throw("no query"); + PQclear_throw("no query"); break; case PGRES_COMMAND_OK: // empty result: insert|delete|update|... + PQclear(res); return; case PGRES_TUPLES_OK: break; default: - PQclear(res); - services._throw("unknown PQexec error"); + PQclear_throwPQerror; break; } int column_count=PQnfields(res); - if(!column_count) { - PQclear(res); - services._throw("result contains no columns"); - } + if(!column_count) + PQclear_throw("result contains no columns"); for(int i=0; i=0) { // seek to end if(lo_lseek(conn, fd, 0, SEEK_END)<0) - PQclear_n_throw; + PQclear_throwPQerror; // get size int size_tell=lo_tell(conn, fd); if(size_tell<0) - PQclear_n_throw; + PQclear_throwPQerror; // seek to begin if(lo_lseek(conn, fd, 0, SEEK_SET)<0) - PQclear_n_throw; + PQclear_throwPQerror; size=(size_t)size_tell; if(size) { // read ptr=services.malloc(size); - char *buf=(char *)ptr; - int countdown=size_tell; - int size_read; - while(countdown && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, countdown)))>0) { - buf+=size_read; - countdown-=size_read; - } - if(countdown) { - PQclear(res); - services._throw("lo_read can not read all of object bytes"); - } + if(!lo_read_ex(conn, fd, (const char *)ptr, size_tell)) + PQclear_throw("lo_read can not read all bytes of object"); } else ptr=0; if(lo_close(conn, fd)<0) - PQclear_n_throw; + PQclear_throwPQerror; } else - PQclear_n_throw; + PQclear_throwPQerror; } else { - // normal column, read it as ASCII string + // normal column, read it normally size=(size_t)PQgetlength(res, r, i); if(size) { ptr=services.malloc(size); @@ -276,7 +247,101 @@ private: // private funcs if(PGresult *res=PQexec(conn, "BEGIN")) PQclear(res); else - services._throw(PQerrorMessage(conn)); + throwPQerror; + } + + const char *preprocess_statement(SQL_Driver_services& services, PGconn *conn, + const char *astatement, unsigned long offset, unsigned long limit) { + size_t statement_size=strlen(astatement); + + char *result=(char *)services.malloc(statement_size + +MAX_NUMBER*2+15 // limit # offset # + +MAX_STRING // in case of short 'strings' + +1); + // offset & limit -> suffixes + const char *o; + if(offset || limit) { + char *cur=result; + memcpy(cur, astatement, statement_size); cur+=statement_size; + if(limit) + cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit); + if(offset) + cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset); + o=result; + } else + o=astatement; + + // /**xxx**/'literal' -> oid + char *n=result; + while(*o) { + if( + o[0]=='/' && + o[1]=='*' && + o[2]=='*') { // name start + o+=3; + while(*o) + if( + o[0]=='*' && + o[1]=='*' && + o[2]=='/' && + o[3]=='\'') { // name end + o+=4; + Oid oid=lo_creat(conn, INV_READ|INV_WRITE); + if(oid==InvalidOid) + throwPQerror; + int fd=lo_open(conn, oid, INV_WRITE); + if(fd>=0) { + const char *start=o; + bool escaped=false; + while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) { + escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\''); + if(escaped) { + // write pending, skip "\" or "'" + if(!lo_write_ex(conn, fd, start, o-start)) + services._throw("lo_write could not write all bytes of object (1)"); + start=++o; + } else + o++; + } + if(!lo_write_ex(conn, fd, start, o-start)) + services._throw("lo_write can not write all bytes of object (2)"); + if(lo_close(conn, fd)<0) + throwPQerror; + } else + throwPQerror; + if(*o) + o++; // skip "'" + + n+=snprintf(n, MAX_NUMBER, "%u", oid); + break; + } else + o++; // /**skip**/'xxx' + } else + *n++=*o++; + } + *n=0; + + return result; + } + +private: // lo_read/write exchancements + + bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) { + int size_read; + while(len && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, len)))>0) { + buf+=size_read; + len-=size_read; + } + return len==0; + } + + bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) { + int size_written; + while(len && (size_written=lo_write(conn, fd, buf, min(LO_BUFSIZE, len)))>0) { + buf+=size_written; + len-=size_written; + } + return len==0; } private: // conn client library funcs @@ -311,8 +376,8 @@ private: // conn client library funcs typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open; typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close; - typedef int (*t_lo_read)(PGconn *conn, int fd, char *buf, size_t len); t_lo_read lo_read; - typedef int (*t_lo_write)(PGconn *conn, int fd, char *buf, size_t len); t_lo_write lo_write; + typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read; + typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write; typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek; typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat; typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell; @@ -358,6 +423,6 @@ private: // conn client library funcs li }; -extern "C" SQL_Driver *create() { +extern "C" SQL_Driver *SQL_DRIVER_CREATE_FUNC_NAME() { return new PgSQL_Driver(); } \ No newline at end of file