--- sql/pgsql/parser3pgsql.C 2004/01/30 07:30:40 1.17 +++ sql/pgsql/parser3pgsql.C 2007/01/26 10:10:32 1.26 @@ -7,7 +7,7 @@ 2001.07.30 using PgSQL 7.1.2 */ -static const char *RCSId="$Id: parser3pgsql.C,v 1.17 2004/01/30 07:30:40 paf Exp $"; +static const char *RCSId="$Id: parser3pgsql.C,v 1.26 2007/01/26 10:10:32 misha Exp $"; #include "config_includes.h" @@ -22,7 +22,7 @@ static const char *RCSId="$Id: parser3pg // actually writing chunks of that size failed, reduced it twice #define LO_BUFSIZE 0x1000 // from postgres_ext.h -#define InvalidOid ((Oid) 0) +//#define InvalidOid ((Oid) 0) #include "ltdl.h" @@ -58,10 +58,27 @@ static char *lsplit(char **string_ref, c return result; } +static char* rsplit(char* string, char delim) { + if(string) { + char* v=strrchr(string, delim); + if(v) { + *v=0; + return v+1; + } + } + return NULL; +} + +static void toupper_str(char *out, const char *in, size_t size) { + while(size--) + *out++=(char)toupper(*in++); +} + struct Connection { SQL_Driver_services* services; PGconn *conn; + const char* cstrClientCharset; }; /** @@ -89,25 +106,28 @@ public: #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(connection.conn)) /** connect - @param used_only_in_connect_url + @param url format: @b user:pass@host[:port]|[local]/database */ void connect( - char *used_only_in_connect_url, + char *url, SQL_Driver_services& services, void **connection_ref ///< output: Connection* ) { - char *user=used_only_in_connect_url; - char *host=lsplit(user, '@'); + char *user=url; + char *host=rsplit(user, '@'); char *db=lsplit(host, '/'); char *pwd=lsplit(user, ':'); char *port=lsplit(host, ':'); char *options=lsplit(db, '?'); + char *cstrBackwardCompAskServerToTranscode=0; + Connection& connection=*(Connection *)services.malloc(sizeof(Connection)); *connection_ref=&connection; connection.services=&services; + connection.cstrClientCharset=0; connection.conn=PQsetdbLogin( (host&&strcasecmp(host, "local")==0)?NULL/* local Unix domain socket */:host, port, NULL, NULL, db, user, pwd); @@ -118,15 +138,27 @@ public: char *charset=0; char *datestyle=0; + isDefaultTransaction = true; while(options) { if(char *key=lsplit(&options, '&')) { if(*key) { if(char *value=lsplit(key, '=')) { - if(strcasecmp(key, "charset")==0) { - charset=value; + if(strcmp(key, "ClientCharset" ) == 0) { + toupper_str(value, value, strlen(value)); + connection.cstrClientCharset=value; + } else if(strcasecmp(key, "charset")==0) { // left for backward compatibility, consider using ClientCharset + cstrBackwardCompAskServerToTranscode=value; } else if(strcasecmp(key, "datestyle")==0) { datestyle=value; + } else if(strcmp(key, "WithoutDefaultTransaction")==0) { + if(strcmp(value, "1" ) == 0) { + isDefaultTransaction = false; + } else if(strcmp(value, "0" ) == 0) { + isDefaultTransaction = true; + } else { + services._throw("Bad WithoutDefaultTransaction option value. Only 0 or 1 are accepted." /*value*/); + } } else services._throw("unknown connect option" /*key*/); } else @@ -135,26 +167,24 @@ public: } } - if(charset) { + if(connection.cstrClientCharset && cstrBackwardCompAskServerToTranscode) + services._throw("use 'ClientCharset' option only, " + "'charset' option is obsolete and should not be used with new 'ClientCharset' option"); + + if(cstrBackwardCompAskServerToTranscode) { // set CLIENT_ENCODING char statement[MAX_STRING]="set CLIENT_ENCODING="; // win - strncat(statement, charset, MAX_STRING); - - PGresult *res=PQexec(connection.conn, statement); - if(!res) - throwPQerror; - PQclear(res); // throw out the result [don't need but must call] + strncat(statement, cstrBackwardCompAskServerToTranscode, MAX_STRING); + + execute_resultless(connection, statement); } if(datestyle) { // set DATESTYLE char statement[MAX_STRING]="set DATESTYLE="; // ISO,SQL,Postgres,European,NonEuropean=US,German,DEFAULT=ISO strncat(statement, charset, MAX_STRING); - - PGresult *res=PQexec(connection.conn, statement); - if(!res) - throwPQerror; - PQclear(res); // throw out the result [don't need but must call] + + execute_resultless(connection, statement); } begin_transaction(connection); @@ -166,22 +196,10 @@ public: connection.conn=0; } void commit(void *aconnection) { - Connection& connection=*static_cast(aconnection); - - if(PGresult *res=PQexec(connection.conn, "COMMIT")) - PQclear(res); - else - throwPQerror; - begin_transaction(connection); + execute_transaction_cmd(aconnection, "COMMIT"); } void rollback(void *aconnection) { - Connection& connection=*static_cast(aconnection); - - if(PGresult *res=PQexec(connection.conn, "ROLLBACK")) - PQclear(res); - else - throwPQerror; - begin_transaction(connection); + execute_transaction_cmd(aconnection, "ROLLBACK"); } bool ping(void *aconnection) { @@ -196,33 +214,73 @@ public: Connection& connection=*static_cast(aconnection); char *result=(char*)connection.services->malloc_atomic(length*2+1); - char *to=result; - while(length--) { - switch(*from) { - case '\'': // "'" -> "''" - *to++='\''; - break; - case '\\': // "\" -> "\\" - *to++='\\'; - break; - } - *to++=*from++; - } - *to=0; + int err = 0; + PQescapeStringConn (connection.conn, + result, from, length, + &err); return result; - } + } + void query(void *aconnection, - const char *astatement, unsigned long offset, unsigned long limit, + const char *astatement, + size_t placeholders_count, Placeholder* placeholders, + unsigned long offset, unsigned long limit, SQL_Driver_query_event_handlers& handlers) { // _asm int 3; Connection& connection=*static_cast(aconnection); + const char* cstrClientCharset=connection.cstrClientCharset; SQL_Driver_services& services=*connection.services; PGconn *conn=connection.conn; + const char** paramValues; + if(placeholders_count>0){ + //services._throw("bind variables not supported (yet)"); + int binds_size=sizeof(char) * placeholders_count; + paramValues = static_cast(services.malloc_atomic(binds_size)); + for(size_t i=0; i placeholders_count) { + services._throw("bad bind parameter key"); + } + paramValues[atoi(ph.name)-1] = ph.value; + } + } + + // transcode from $request:charset to connect-string?client_charset + if(cstrClientCharset) { + size_t transcoded_statement_size; + services.transcode(astatement, strlen(astatement), + astatement, transcoded_statement_size, + services.request_charset(), + cstrClientCharset); + } + const char *statement=preprocess_statement(connection, astatement, offset, limit); - PGresult *res=PQexec(conn, statement); + PGresult *res; + if(placeholders_count>0){ + res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0); + } else { + res=PQexec(conn, statement); + } if(!res) throwPQerror; @@ -255,10 +313,19 @@ public: for(int i=0; i(aconnection); + execute_resultless(connection, query); + begin_transaction(connection); + } + } + + /** + Executes a query and throws the result. + */ + void execute_resultless(const Connection& connection, const char *query) { + if(PGresult *res=PQexec(connection.conn, query)) + PQclear(res); // throw out the result [don't need but must call] else throwPQerror; } + void begin_transaction(Connection& connection) { + if(isDefaultTransaction) + { + execute_resultless(connection, "BEGIN"); + } + } + const char *preprocess_statement(Connection& connection, const char *astatement, unsigned long offset, unsigned long limit) { PGconn *conn=connection.conn; @@ -412,19 +510,18 @@ private: // private funcs 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; + return lo_rw_method (conn, fd, buf, len, lo_read); } 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 lo_rw_method (conn, fd, buf, len, lo_write); + } + + bool lo_rw_method(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len, int (*lo_func)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len)) { + int size_op; + while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) { + buf+=size_op; + len-=size_op; } return len==0; } @@ -444,6 +541,17 @@ private: // conn client library funcs typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus; typedef PGresult *(*t_PQexec)(PGconn *conn, const char *query); t_PQexec PQexec; +//PQexecParams + typedef PGresult *(*t_PQexecParams)( + PGconn *conn, + const char *query, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); t_PQexecParams PQexecParams; + typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus; typedef int (*t_PQgetlength)(const PGresult *res, int tup_num, @@ -459,6 +567,10 @@ private: // conn client library funcs typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype; + typedef size_t (*t_PQescapeStringConn)(PGconn *conn, + char *to, const char *from, size_t length, + int *error); t_PQescapeStringConn PQescapeStringConn; + 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, const/*paf*/ char *buf, size_t len); t_lo_read lo_read; @@ -498,7 +610,9 @@ private: // conn client library funcs li DLINK(PQclear); DLINK(PQresultStatus); DLINK(PQexec); + DLINK(PQexecParams); DLINK(PQftype); + DLINK(PQescapeStringConn); DLINK(lo_open); DLINK(lo_close); DLINK(lo_read); DLINK(lo_write); DLINK(lo_lseek); DLINK(lo_creat); @@ -508,6 +622,7 @@ private: // conn client library funcs li return 0; } + bool isDefaultTransaction; }; extern "C" SQL_Driver *SQL_DRIVER_CREATE() {