a2014e2008-07-14Stephen R. van den Berg /* * This is the PostgreSQL direct network module for Pike. */ //! This is an interface to the PostgreSQL database //! server. This module is independent of external libraries. //! Note that you @b{do not@} need to have a //! PostgreSQL server running on your host to use this module: you can //! connect to the database over a TCP/IP socket.
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! This module replaces the functionality of the older @[Sql.postgres] //! and @[Postgres.postgres] modules. //!
4e1d1f2008-08-01Stephen R. van den Berg //! This module supports the following features: //! //! - PostgreSQL network protocol version 3, authentication methods //! currently supported are: cleartext and MD5 (recommended). //! //! - Streaming queries which do not buffer the whole resulset in memory. //! //! - Automatic binary transfers to and from the database for most common //! datatypes (amongst others: integer, text and bytea types). //! //! - SQL-injection protection by allowing just one statement per query //! and ignoring anything after the first (unquoted) semicolon in the query. //! //! - COPY support for streaming up- and download. //! //! - Accurate error messages. //! //! - Automatic precompilation of complex queries (session cache). //! //! - Multiple simultaneous queries on the same database connection. //! //! - Cancelling of long running queries by force or by timeout. //! //! - Event driven NOTIFY. //! //! - SSL encrypted connections (optional or forced). //!
a2014e2008-07-14Stephen R. van den Berg //! Refer to the PostgreSQL documentation for further details. //!
4e1d1f2008-08-01Stephen R. van den Berg //! @note //! Multiple simultaneous queries on the same database connection is a //! feature that none of the other database drivers for Pike support. //! So, although it's efficient, its use will make switching database drivers //! difficult. //!
a2014e2008-07-14Stephen R. van den Berg //! @seealso //! @[Sql.Sql], @[Sql.postgres] #pike __REAL_VERSION__
ecbab12008-07-27Stephen R. van den Berg #include "pgsql.h"
a2014e2008-07-14Stephen R. van den Berg 
e22cab2008-07-14Stephen R. van den Berg #define ERROR(X ...) predef::error(X)
a2014e2008-07-14Stephen R. van den Berg  int _nextportal; int _closesent; int _fetchlimit=FETCHLIMIT; private int unnamedportalinuse; private int portalsinflight;
7ac2f12008-07-27Stephen R. van den Berg object _c;
a2014e2008-07-14Stephen R. van den Berg private string SSauthdata,cancelsecret; private int backendpid; private int backendstatus;
13a1202008-07-26Stephen R. van den Berg private mapping(string:mixed) options;
a2014e2008-07-14Stephen R. van den Berg private string lastmessage;
39f78a2008-07-30Stephen R. van den Berg private int clearmessage;
4741cd2008-07-31Stephen R. van den Berg private int earlyclose;
a2014e2008-07-14Stephen R. van den Berg private mapping(string:array(mixed)) notifylist=([]); private mapping(string:string) runtimeparameter;
ecbab12008-07-27Stephen R. van den Berg state _mstate;
a2014e2008-07-14Stephen R. van den Berg private enum querystate {queryidle,inquery,cancelpending,canceled}; private querystate qstate; private mapping(string:mapping(string:mixed)) prepareds=([]);
cb26232008-08-04Stephen R. van den Berg private mapping(string:mixed) tprepared;
a2014e2008-07-14Stephen R. van den Berg private int pstmtcount; private int pportalcount; private int totalhits;
ecbab12008-07-27Stephen R. van den Berg private int cachedepth=STATEMENTCACHEDEPTH; private int timeout=QUERYTIMEOUT; private int portalbuffersize=PORTALBUFFERSIZE;
a2014e2008-07-14Stephen R. van den Berg private int reconnected; // Number of times the connection was reset
cb26232008-08-04Stephen R. van den Berg private int sessionblocked; // Number of times the session blocked on network private int skippeddescribe; // Number of times we skipped Describe phase private int portalsopened; // Number of portals opened int _msgsreceived; // Number of protocol messages received int _bytesreceived; // Number of bytes received int _packetssent; // Number of packets sent int _bytessent; // Number of bytes sent private int warningsdropcount; // Number of uncollected warnings private int prepstmtused; // Number of times prepared statements were used private int warningscollected;
a2014e2008-07-14Stephen R. van den Berg  private string host, database, user, pass; private int port;
cb26232008-08-04Stephen R. van den Berg private object fetchprefix =Regexp("^[ \t\f\r\n]*[Ff][Ee][Tt][Cc][Hh][ \t\f\r\n]");
80dbe82008-08-08Stephen R. van den Berg private object limitpostfix =Regexp("[ \t\f\r\n][Ll][Ii][Mm][Ii][Tt][ \t\f\r\n]+[12][; \t\f\r\n]*$");
ecbab12008-07-27Stephen R. van den Berg Thread.Mutex _querymutex; Thread.Mutex _stealmutex;
a2014e2008-07-14Stephen R. van den Berg  protected string _sprintf(int type, void|mapping flags) { string res=UNDEFINED; switch(type) { case 'O': res=sprintf(DRIVERNAME"://%s@%s:%d/%s pid:%d %s reconnected:%d\n"
17b7532008-07-30Stephen R. van den Berg  "mstate: %O qstate: %O pstmtcount: %d pportalcount: %d prepcache: %d\n" "Last message: %s",
a2014e2008-07-14Stephen R. van den Berg  user,host,port,database,backendpid,status_commit(),reconnected,
17b7532008-07-30Stephen R. van den Berg  _mstate,qstate,pstmtcount,pportalcount,sizeof(prepareds), lastmessage||"");
a2014e2008-07-14Stephen R. van den Berg  break; } return res; } #define BOOLOID 16 #define BYTEAOID 17 #define CHAROID 18 #define INT8OID 20 #define INT2OID 21 #define INT4OID 23 #define TEXTOID 25 #define OIDOID 26 #define XMLOID 142 #define FLOAT4OID 700 #define MACADDROID 829
7c4bd02008-08-09Stephen R. van den Berg #define INETOID 869 /* Force textmode */
a2014e2008-07-14Stephen R. van den Berg #define BPCHAROID 1042 #define VARCHAROID 1043 #define CTIDOID 1247 #define UUIDOID 2950 #define PG_PROTOCOL(m,n) (((m)<<16)|(n))
1fe3992008-07-16Peter Bortas //! @decl void create()
a2014e2008-07-14Stephen R. van den Berg //! @decl void create(string host, void|string database, void|string user,@
13a1202008-07-26Stephen R. van den Berg //! void|string password, void|mapping(string:mixed) options)
a2014e2008-07-14Stephen R. van den Berg //! //! With no arguments, this function initializes (reinitializes if a //! connection had been previously set up) a connection to the //! PostgreSQL backend. Since PostgreSQL requires a database to be //! selected, it will try to connect to the default database. The //! connection may fail however for a variety of reasons, in this case //! the most likely of all is because you don't have enough authority //! to connect to that database. So use of this particular syntax is //! discouraged. //! //! The host argument can have the syntax @expr{"hostname"@} or //! @expr{"hostname:portname"@}. This allows to specify the TCP/IP //! port to connect to. If it is @expr{0@} or @expr{""@}, it will try //! to connect to localhost, default port. //! //! The database argument specifies the database to connect to. If //! @expr{0@} or @expr{""@}, it will try to connect to the specified //! database. //!
cb26232008-08-04Stephen R. van den Berg //! The options argument currently supports at least the following: //! @string //! @value "use_ssl" //! If the database supports and allows SSL connections, the session //! will be SSL encrypted, if not, the connection will fallback //! to plain unencrypted //! @value "force_ssl" //! If the database supports and allows SSL connections, the session //! will be SSL encrypted, if not, the connection will abort //! @value "client_encoding" //! Character encoding for the client side, it defaults to use //! database encoding, e.g.: "SQL_ASCII" //! @value "standard_conforming_strings" //! When on, backslashes in strings must not be escaped any longer, //! @[quote] automatically adjusts quoting strategy accordingly //! @value "escape_string_warning" //! When on, a warning is issued if a backslash (\) appears in an //! ordinary string literal and @[standard_conforming_strings] is off, //! defaults to on //! @endstring //! For the numerous other options please check the PostgreSQL manual.
39fe7f2008-07-26Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @note //! You need to have a database selected before using the sql-object, //! otherwise you'll get exceptions when you try to query it. Also //! notice that this function @b{can@} raise exceptions if the db //! server doesn't respond, if the database doesn't exist or is not //! accessible by you. //! //! @seealso //! @[Postgres.postgres], @[Sql.Sql], @[postgres->select_db] protected void create(void|string _host, void|string _database,
13a1202008-07-26Stephen R. van den Berg  void|string _user, void|string _pass, void|mapping(string:mixed) _options) {
334f372008-08-12Stephen R. van den Berg  pass = _pass; _pass = "CENSORED"; if(pass) String.secure(pass);
a2014e2008-07-14Stephen R. van den Berg  user = _user; database = _database; host = _host || PGSQL_DEFAULT_HOST;
39fe7f2008-07-26Stephen R. van den Berg  options = _options || ([]);
a2014e2008-07-14Stephen R. van den Berg  if(search(host,":")>=0 && sscanf(_host,"%s:%d",host,port)!=2) ERROR("Error in parsing the hostname argument\n"); if(!port) port = PGSQL_DEFAULT_PORT;
ecbab12008-07-27Stephen R. van den Berg  _querymutex=Thread.Mutex(); _stealmutex=Thread.Mutex();
a2014e2008-07-14Stephen R. van den Berg  reconnect(); } //! @decl string error() //! //! This function returns the textual description of the last //! server-related error. Returns @expr{0@} if no error has occurred //! yet. It is not cleared upon reading (can be invoked multiple //! times, will return the same result until a new error occurs). //!
f28c0d2008-08-01Stephen R. van den Berg //! During the execution of a statement, this function accumulates all //! non-error messages (notices, warnings, etc.). If a statement does not //! generate any errors, this function will return all collected messages //! from the last statement. //!
a2014e2008-07-14Stephen R. van den Berg //! To clear the error, pass 1 as argument. //! //! @seealso //! big_query string error(void|int clear) { string s=lastmessage; if(clear) lastmessage=UNDEFINED;
cb26232008-08-04Stephen R. van den Berg  warningscollected=0;
a2014e2008-07-14Stephen R. van den Berg  return s; } //! @decl string host_info() //! //! This function returns a string describing what host are we talking to, //! and how (TCP/IP or UNIX sockets).
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso //! server_info
a2014e2008-07-14Stephen R. van den Berg string host_info() {
6d9aa52008-08-21Stephen R. van den Berg  return sprintf("fd:%d TCP/IP %s:%d PID %d", _c?_c.query_fd():-1,host,port,backendpid);
a2014e2008-07-14Stephen R. van den Berg }
7ac2f12008-07-27Stephen R. van den Berg final private object getsocket(void|int nossl) {
39fe7f2008-07-26Stephen R. van den Berg  object lcon = Stdio.File();
13a1202008-07-26Stephen R. van den Berg  if(!lcon.connect(host,port)) return UNDEFINED;
7ac2f12008-07-27Stephen R. van den Berg 
39fe7f2008-07-26Stephen R. van den Berg  object fcon;
13a1202008-07-26Stephen R. van den Berg #if constant(SSL.sslfile)
7ac2f12008-07-27Stephen R. van den Berg  if(!nossl && (options->use_ssl || options->force_ssl)) {
39fe7f2008-07-26Stephen R. van den Berg  PD("SSLRequest\n");
ecbab12008-07-27Stephen R. van den Berg  { object c=.pgsql_util.PGassist();
7ac2f12008-07-27Stephen R. van den Berg  lcon.write(({c.plugint32(8),c.plugint32(PG_PROTOCOL(1234,5679))})); }
39fe7f2008-07-26Stephen R. van den Berg  switch(lcon.read(1)) { case "S": SSL.context context = SSL.context(); context->random = Crypto.Random.random_string;
ecbab12008-07-27Stephen R. van den Berg  fcon=.pgsql_util.PGconnS(lcon, context);
39fe7f2008-07-26Stephen R. van den Berg  if(fcon) return fcon; default:lcon.close(); if(!lcon.connect(host,port)) return UNDEFINED; case "N": if(options->force_ssl) ERROR("Encryption not supported on connection to %s:%d\n", host,port);
13a1202008-07-26Stephen R. van den Berg  }
39fe7f2008-07-26Stephen R. van den Berg  } #else if(options->force_ssl) ERROR("Encryption library missing, cannot establish connection to %s:%d\n", host,port);
a2014e2008-07-14Stephen R. van den Berg #endif
ecbab12008-07-27Stephen R. van den Berg  fcon=.pgsql_util.PGconn(lcon,this);
39fe7f2008-07-26Stephen R. van den Berg  return fcon;
a2014e2008-07-14Stephen R. van den Berg } //! Cancels the currently running query. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso //! reload
a2014e2008-07-14Stephen R. van den Berg void cancelquery() { if(qstate==inquery) { qstate=cancelpending; object lcon; PD("CancelRequest\n");
7ac2f12008-07-27Stephen R. van den Berg  if(!(lcon=getsocket(1)))
a2014e2008-07-14Stephen R. van den Berg  ERROR("Cancel connect failed\n");
7ac2f12008-07-27Stephen R. van den Berg  lcon.write(({_c.plugint32(16),_c.plugint32(PG_PROTOCOL(1234,5678)), _c.plugint32(backendpid),cancelsecret}));
a2014e2008-07-14Stephen R. van den Berg  lcon.close(); } }
d6f86e2008-08-20Stephen R. van den Berg //! Changes the connection charset. //! //! @[charset] is a PostgreSQL charset name. //! //! @seealso //! @[get_charset], @[create] void set_charset(string charset) { #if 0 // FIXME Do we want to support the "unicode" setting? (see mysql.pike) #endif big_query("SET CLIENT_ENCODING TO :charset",([":charset":charset])); } //! Returns the PostgreSQL name for the current connection charset. //! //! @seealso //! @[set_charset], @[getruntimeparameters] string get_charset() { return runtimeparameter->client_encoding; }
cbe2762008-08-01Stephen R. van den Berg //! Returns the set of currently active runtimeparameters for
cb26232008-08-04Stephen R. van den Berg //! the open session; these are initialised by the options parameter //! during session creation, and then processed and returned by the server. //! //! Common values are: //! @string //! @value "client_encoding" //! Character encoding for the client side, e.g.: "SQL_ASCII" //! @value "server_encoding" //! Character encoding for the server side as determined when the //! database was created, e.g.: "SQL_ASCII" //! @value "DateStyle" //! Date parsing/display, e.g.: "ISO, DMY" //! @value "TimeZone" //! Default timezone used by the database, e.g.: "localtime" //! @value "standard_conforming_strings" //! When on, backslashes in strings must not be escaped any longer //! @value "session_authorization" //! Displays the authorisationrole which the current session runs under //! @value "is_superuser" //! Indicates if the current authorisationrole has database-superuser //! privileges //! @value "integer_datetimes" //! Reports wether the database supports 64-bit-integer dates and times //! @value "server_version" //! Shows the server version, e.g.: "8.3.3" //! @endstring //! //! The values can be changed during a session using SET commands to the //! database. //! For other runtimeparameters check the PostgreSQL documentation.
cbe2762008-08-01Stephen R. van den Berg //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. mapping(string:string) getruntimeparameters() { return runtimeparameter+([]); }
cb26232008-08-04Stephen R. van den Berg //! Returns a mapping with a set of statistics for the current session. //! //! @param warnings_dropped //! Number of warnings/notices generated by the database but not //! collected by the application by using @[error] after the statements //! that generated them. //! //! @param skipped_describe_count //! Number of times the driver skipped asking the database to //! describe the statement parameters because it was already cached. //! //! @param used_prepared_statements //! Numer of times prepared statements were used from cache instead of //! reparsing in the current session. //! //! @param current_prepared_statements //! Cache size of currently prepared statements. //! //! @param current_prepared_statement_hits //! Sum of the number hits on statements in the current statement cache. //! //! @param prepared_portal_count //! Total number of prepared portals generated. //! //! @param prepared_statement_count //! Total number of prepared statements generated. //! //! @param portals_opened_count //! Total number of portals opened, i.e. number of statements issued //! to the database. //! //! @param blocked_count //! Number of times the driver had to (briefly) wait for the database to //! send additional data. //! //! @param bytes_received //! Total number of bytes received from the database so far. //! //! @param messages_received //! Total number of messages received from the database (one SQL-statement //! requires multiple messages to be exchanged). //! //! @param bytes_sent //! Total number of bytes sent to the database so far. //! //! @param packets_sent //! Total number of packets sent to the database (one packet usually //! contains multiple messages). //! //! @param reconnect_count //! Number of times the connection to the database has been lost. //! //! @param portals_in_flight //! Currently still open portals, i.e. running statements. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. mapping(string:mixed) getstatistics() { mapping(string:mixed) stats=([ "warnings_dropped":warningsdropcount, "skipped_describe_count":skippeddescribe, "used_prepared_statements":prepstmtused, "current_prepared_statements":sizeof(prepareds), "current_prepared_statement_hits":totalhits, "prepared_portal_count":pportalcount, "prepared_statement_count":pstmtcount, "portals_opened_count":portalsopened, "blocked_count":sessionblocked, "messages_received":_msgsreceived, "bytes_received":_bytesreceived, "packets_sent":_packetssent, "bytes_sent":_bytessent, "reconnect_count":reconnected, "portals_in_flight":portalsinflight, ]); return stats; }
a2014e2008-07-14Stephen R. van den Berg //! Returns the old cachedepth, sets the new cachedepth for prepared //! statements automatic caching. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. int setcachedepth(void|int newdepth) { int olddepth=cachedepth;
488ce72008-07-24Stephen R. van den Berg  if(!zero_type(newdepth) && newdepth>=0)
a2014e2008-07-14Stephen R. van den Berg  cachedepth=newdepth; return olddepth; } //! Returns the old timeout, sets the new timeout for long running //! queries. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. int settimeout(void|int newtimeout) { int oldtimeout=timeout;
488ce72008-07-24Stephen R. van den Berg  if(!zero_type(newtimeout) && newtimeout>0)
a2014e2008-07-14Stephen R. van den Berg  timeout=newtimeout; return oldtimeout; } //! Returns the old portalbuffersize, sets the new portalbuffersize //! for buffering partially concurrent queries. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. int setportalbuffersize(void|int newportalbuffersize) { int oldportalbuffersize=portalbuffersize;
488ce72008-07-24Stephen R. van den Berg  if(!zero_type(newportalbuffersize) && newportalbuffersize>0)
a2014e2008-07-14Stephen R. van den Berg  portalbuffersize=newportalbuffersize; return oldportalbuffersize; } //! Returns the old fetchlimit, sets the new fetchlimit to interleave //! queries. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. int setfetchlimit(void|int newfetchlimit) { int oldfetchlimit=_fetchlimit;
488ce72008-07-24Stephen R. van den Berg  if(!zero_type(newfetchlimit) && newfetchlimit>=0)
a2014e2008-07-14Stephen R. van den Berg  _fetchlimit=newfetchlimit; return oldfetchlimit; } final private string glob2reg(string glob) { if (!glob||!sizeof(glob)) return "%"; return replace(glob,({"*","?","\\","%","_"}),({"%","_","\\\\","\\%","\\_"})); } final private string addnlifpresent(void|string msg) { return msg?msg+"\n":""; } final private string pinpointerror(void|string query,void|string offset) { if(!query) return ""; int k=(int)offset; if(k<=0) return MARKSTART+query+MARKEND; return MARKSTART+(k>1?query[..k-2]:"")+MARKERROR+query[k-1..]+MARKEND; }
6d9aa52008-08-21Stephen R. van den Berg final private string lastmsgnl() {
316bb92008-08-22Stephen R. van den Berg  return lastmessage?lastmessage+"\n":"";
6d9aa52008-08-21Stephen R. van den Berg }
49a3282008-07-25Stephen R. van den Berg final int _decodemsg(void|state waitforstate) {
a2014e2008-07-14Stephen R. van den Berg #ifdef DEBUG { array line; #ifdef DEBUGMORE line=backtrace(); #endif PD("Waiting for state %O %O\n",waitforstate,line&&line[sizeof(line)-2]); } #endif
ecbab12008-07-27Stephen R. van den Berg  while(_mstate!=waitforstate) { if(_mstate!=unauthenticated) {
a2014e2008-07-14Stephen R. van den Berg  if(qstate==cancelpending) qstate=canceled,sendclose();
7ac2f12008-07-27Stephen R. van den Berg  if(_c.flushed && qstate==inquery && !_c.bpeek(0)) {
a2014e2008-07-14Stephen R. van den Berg  int tcurr=time(); int told=tcurr+timeout;
cb26232008-08-04Stephen R. van den Berg  sessionblocked++;
7ac2f12008-07-27Stephen R. van den Berg  while(!_c.bpeek(told-tcurr))
a2014e2008-07-14Stephen R. van den Berg  if((tcurr=time())-told>=timeout) { sendclose();cancelquery(); break; } } }
7ac2f12008-07-27Stephen R. van den Berg  int msgtype=_c.getbyte(); int msglen=_c.getint32();
cb26232008-08-04Stephen R. van den Berg  _msgsreceived++; _bytesreceived+=1+msglen;
a2014e2008-07-14Stephen R. van den Berg  enum errortype { noerror=0, protocolerror, protocolunsupported }; errortype errtype=noerror; switch(msgtype) {
cb26232008-08-04Stephen R. van den Berg  void storetiming() { tprepared->trun=gethrtime()-tprepared->trunstart; m_delete(tprepared,"trunstart"); tprepared = UNDEFINED; };
a2014e2008-07-14Stephen R. van den Berg  void getcols() {
7ac2f12008-07-27Stephen R. van den Berg  int bintext=_c.getbyte();
a2014e2008-07-14Stephen R. van den Berg  array a;
7ac2f12008-07-27Stephen R. van den Berg  int cols=_c.getint16();
a2014e2008-07-14Stephen R. van den Berg  msglen-=4+1+2+2*cols; foreach(a=allocate(cols,([]));;mapping m)
7ac2f12008-07-27Stephen R. van den Berg  m->type=_c.getint16(); if(_c.portal) { // Discard column info, and make it line oriented
a2014e2008-07-14Stephen R. van den Berg  a=({(["type":bintext?BYTEAOID:TEXTOID,"name":"line"])});
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_datarowdesc=a;
a2014e2008-07-14Stephen R. van den Berg  }
ecbab12008-07-27Stephen R. van den Berg  _mstate=gotrowdescription;
a2014e2008-07-14Stephen R. van den Berg  }; array(string) getstrings() { string s; if(msglen<1) errtype=protocolerror;
7ac2f12008-07-27Stephen R. van den Berg  s=_c.getstring(msglen);
a2014e2008-07-14Stephen R. van den Berg  if(s[--msglen]) errtype=protocolerror; if(!msglen) return ({}); s=s[..msglen-1];msglen=0; return s/"\0"; };
b133b02008-08-06Stephen R. van den Berg  mapping(string:string) getresponse() { mapping(string:string) msgresponse=([]);
a2014e2008-07-14Stephen R. van den Berg  msglen-=4; foreach(getstrings();;string f) if(sizeof(f)) msgresponse[f[..0]]=f[1..]; PD("%O\n",msgresponse);
b133b02008-08-06Stephen R. van den Berg  return msgresponse;
a2014e2008-07-14Stephen R. van den Berg  }; case 'R':PD("Authentication\n"); { string sendpass; int authtype; msglen-=4+4;
7ac2f12008-07-27Stephen R. van den Berg  switch(authtype=_c.getint32()) {
a2014e2008-07-14Stephen R. van den Berg  case 0:PD("Ok\n");
ecbab12008-07-27Stephen R. van den Berg  _mstate=authenticated;
a2014e2008-07-14Stephen R. van den Berg  break; case 2:PD("KerberosV5\n"); errtype=protocolunsupported; break; case 3:PD("ClearTextPassword\n"); sendpass=pass; break; case 4:PD("CryptPassword\n"); if(msglen<2) errtype=protocolerror;
f28c0d2008-08-01Stephen R. van den Berg  sendpass=_c.getstring(msglen);msglen=0; // salt errtype=protocolunsupported; // Pike lacks function that takes salt
a2014e2008-07-14Stephen R. van den Berg  break; case 5:PD("MD5Password\n"); if(msglen<4) errtype=protocolerror; #if constant(Crypto.MD5.hash) #define md5hex(x) String.string2hex(Crypto.MD5.hash(x)) sendpass=md5hex(pass+user);
7ac2f12008-07-27Stephen R. van den Berg  sendpass="md5"+md5hex(sendpass+_c.getstring(msglen));
a2014e2008-07-14Stephen R. van den Berg #else
7ac2f12008-07-27Stephen R. van den Berg  _c.getstring(msglen);
a2014e2008-07-14Stephen R. van den Berg  errtype=protocolunsupported; #endif msglen=0; break; case 6:PD("SCMCredential\n"); errtype=protocolunsupported; break; case 7:PD("GSS\n"); errtype=protocolunsupported; break; case 9:PD("SSPI\n"); errtype=protocolunsupported; break; case 8:PD("GSSContinue\n"); errtype=protocolunsupported; if(msglen<1) errtype=protocolerror;
7ac2f12008-07-27Stephen R. van den Berg  SSauthdata=_c.getstring(msglen);msglen=0;
a2014e2008-07-14Stephen R. van den Berg  break; default:PD("Unknown Authentication Method %c\n",authtype); errtype=protocolunsupported; break; } switch(errtype) { case noerror:
ecbab12008-07-27Stephen R. van den Berg  if(_mstate==unauthenticated)
7ac2f12008-07-27Stephen R. van den Berg  _c.sendcmd(({"p",_c.plugint32(4+sizeof(sendpass)+1),
b212b52008-07-26Stephen R. van den Berg  sendpass,"\0"}),1);
a2014e2008-07-14Stephen R. van den Berg  break; default: case protocolunsupported: ERROR("Unsupported authenticationmethod %c\n",authtype); break; } break; } case 'K':PD("BackendKeyData\n");
7ac2f12008-07-27Stephen R. van den Berg  msglen-=4+4;backendpid=_c.getint32();cancelsecret=_c.getstring(msglen);
a2014e2008-07-14Stephen R. van den Berg  msglen=0; break; case 'S':PD("ParameterStatus\n"); msglen-=4; { array(string) ts=getstrings(); if(sizeof(ts)==2) { runtimeparameter[ts[0]]=ts[1]; PD("%s=%s\n",ts[0],ts[1]); } else errtype=protocolerror; } break; case 'Z':PD("ReadyForQuery\n"); msglen-=4+1;
7ac2f12008-07-27Stephen R. van den Berg  backendstatus=_c.getbyte();
ecbab12008-07-27Stephen R. van den Berg  _mstate=readyforquery;
a2014e2008-07-14Stephen R. van den Berg  qstate=queryidle; _closesent=0; break; case '1':PD("ParseComplete\n"); msglen-=4;
ecbab12008-07-27Stephen R. van den Berg  _mstate=parsecomplete;
a2014e2008-07-14Stephen R. van den Berg  break; case 't': PD("ParameterDescription (for %s)\n",
7ac2f12008-07-27Stephen R. van den Berg  _c.portal?_c.portal->_portalname:"DISCARDED");
a2014e2008-07-14Stephen R. van den Berg  { array a;
7ac2f12008-07-27Stephen R. van den Berg  int cols=_c.getint16();
a2014e2008-07-14Stephen R. van den Berg  msglen-=4+2+4*cols; foreach(a=allocate(cols);int i;)
7ac2f12008-07-27Stephen R. van den Berg  a[i]=_c.getint32();
a2014e2008-07-14Stephen R. van den Berg #ifdef DEBUGMORE PD("%O\n",a); #endif
7ac2f12008-07-27Stephen R. van den Berg  if(_c.portal) _c.portal->_datatypeoid=a;
ecbab12008-07-27Stephen R. van den Berg  _mstate=gotparameterdescription;
a2014e2008-07-14Stephen R. van den Berg  break; } case 'T': PD("RowDescription (for %s)\n",
7ac2f12008-07-27Stephen R. van den Berg  _c.portal?_c.portal->_portalname:"DISCARDED");
a2014e2008-07-14Stephen R. van den Berg  msglen-=4+2; { array a;
7ac2f12008-07-27Stephen R. van den Berg  foreach(a=allocate(_c.getint16());int i;) {
488ce72008-07-24Stephen R. van den Berg  string s;
7ac2f12008-07-27Stephen R. van den Berg  msglen-=sizeof(s=_c.getstring())+1;
a2014e2008-07-14Stephen R. van den Berg  mapping(string:mixed) res=(["name":s]); msglen-=4+2+4+2+4+2;
7ac2f12008-07-27Stephen R. van den Berg  res->tableoid=_c.getint32()||UNDEFINED; res->tablecolattr=_c.getint16()||UNDEFINED; res->type=_c.getint32(); { int len=_c.getint16();
a2014e2008-07-14Stephen R. van den Berg  res->length=len>=0?len:"variable"; }
7ac2f12008-07-27Stephen R. van den Berg  res->atttypmod=_c.getint32();res->formatcode=_c.getint16();
a2014e2008-07-14Stephen R. van den Berg  a[i]=res; } #ifdef DEBUGMORE PD("%O\n",a); #endif
7ac2f12008-07-27Stephen R. van den Berg  if(_c.portal) _c.portal->_datarowdesc=a;
ecbab12008-07-27Stephen R. van den Berg  _mstate=gotrowdescription;
a2014e2008-07-14Stephen R. van den Berg  break; } case 'n':PD("NoData\n"); msglen-=4;
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_datarowdesc=({});
ecbab12008-07-27Stephen R. van den Berg  _mstate=gotrowdescription;
a2014e2008-07-14Stephen R. van den Berg  break; case '2':PD("BindComplete\n"); msglen-=4;
ecbab12008-07-27Stephen R. van den Berg  _mstate=bindcomplete;
a2014e2008-07-14Stephen R. van den Berg  break; case 'D':PD("DataRow\n");
488ce72008-07-24Stephen R. van den Berg  msglen-=4;
7ac2f12008-07-27Stephen R. van den Berg  if(_c.portal) {
cb26232008-08-04Stephen R. van den Berg  if(tprepared) storetiming();
49a3282008-07-25Stephen R. van den Berg #ifdef USEPGsql
7ac2f12008-07-27Stephen R. van den Berg  _c.decodedatarow(msglen);msglen=0;
49a3282008-07-25Stephen R. van den Berg #else
488ce72008-07-24Stephen R. van den Berg  array a, datarowdesc;
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_bytesreceived+=msglen; datarowdesc=_c.portal->_datarowdesc; int cols=_c.getint16();
b156722008-08-25Stephen R. van den Berg  int atext = _c.portal->_alltext; // cache locally for speed
488ce72008-07-24Stephen R. van den Berg  a=allocate(cols,UNDEFINED); msglen-=2+4*cols; foreach(a;int i;) {
7ac2f12008-07-27Stephen R. van den Berg  int collen=_c.getint32();
488ce72008-07-24Stephen R. van den Berg  if(collen>0) { msglen-=collen; mixed value;
a2014e2008-07-14Stephen R. van den Berg  switch(datarowdesc[i]->type) {
7ac2f12008-07-27Stephen R. van den Berg  default:value=_c.getstring(collen);
a2014e2008-07-14Stephen R. van den Berg  break;
f6f9722008-08-26Stephen R. van den Berg  case CHAROID:value=atext?_c.getstring(1):_c.getbyte();
aaa4562008-08-21Stephen R. van den Berg  break;
7ac2f12008-07-27Stephen R. van den Berg  case BOOLOID:value=_c.getbyte();
b156722008-08-25Stephen R. van den Berg  if(atext)
aaa4562008-08-21Stephen R. van den Berg  value=value?"t":"f";
a2014e2008-07-14Stephen R. van den Berg  break;
7ac2f12008-07-27Stephen R. van den Berg  case INT8OID:value=_c.getint64();
a2014e2008-07-14Stephen R. van den Berg  break;
b156722008-08-25Stephen R. van den Berg  case FLOAT4OID: value=_c.getstring(collen); if(!atext) value=(float)value;
a2014e2008-07-14Stephen R. van den Berg  break;
7ac2f12008-07-27Stephen R. van den Berg  case INT2OID:value=_c.getint16();
a2014e2008-07-14Stephen R. van den Berg  break; case OIDOID:
7ac2f12008-07-27Stephen R. van den Berg  case INT4OID:value=_c.getint32();
a2014e2008-07-14Stephen R. van den Berg  }
aaa4562008-08-21Stephen R. van den Berg  if(atext&&!stringp(value)) value=(string)value;
a2014e2008-07-14Stephen R. van den Berg  a[i]=value;
488ce72008-07-24Stephen R. van den Berg  } else if(!collen) a[i]="";
a2014e2008-07-14Stephen R. van den Berg  }
49a3282008-07-25Stephen R. van den Berg  a=({a});
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_datarows+=a; _c.portal->_inflight-=sizeof(a); #endif
a2014e2008-07-14Stephen R. van den Berg  }
488ce72008-07-24Stephen R. van den Berg  else
7ac2f12008-07-27Stephen R. van den Berg  _c.getstring(msglen),msglen=0;
ecbab12008-07-27Stephen R. van den Berg  _mstate=dataready;
a2014e2008-07-14Stephen R. van den Berg  break; case 's':PD("PortalSuspended\n"); msglen-=4;
ecbab12008-07-27Stephen R. van den Berg  _mstate=portalsuspended;
a2014e2008-07-14Stephen R. van den Berg  break; case 'C':PD("CommandComplete\n"); { msglen-=4; if(msglen<1) errtype=protocolerror;
7ac2f12008-07-27Stephen R. van den Berg  string s=_c.getstring(msglen-1);
cb26232008-08-04Stephen R. van den Berg  if(_c.portal) { if(tprepared) storetiming();
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_statuscmdcomplete=s;
cb26232008-08-04Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  PD("%s\n",s);
7ac2f12008-07-27Stephen R. van den Berg  if(_c.getbyte())
a2014e2008-07-14Stephen R. van den Berg  errtype=protocolerror; msglen=0;
ecbab12008-07-27Stephen R. van den Berg  _mstate=commandcomplete;
a2014e2008-07-14Stephen R. van den Berg  break; } case 'I':PD("EmptyQueryResponse\n"); msglen-=4;
ecbab12008-07-27Stephen R. van den Berg  _mstate=commandcomplete;
a2014e2008-07-14Stephen R. van den Berg  break; case '3':PD("CloseComplete\n"); msglen-=4; _closesent=0; break; case 'd':PD("CopyData\n");
cb26232008-08-04Stephen R. van den Berg  if(tprepared) storetiming();
a2014e2008-07-14Stephen R. van den Berg  msglen-=4; if(msglen<0) errtype=protocolerror;
7ac2f12008-07-27Stephen R. van den Berg  if(_c.portal) { _c.portal->_bytesreceived+=msglen; _c.portal->_datarows+=({({_c.getstring(msglen)})});
a2014e2008-07-14Stephen R. van den Berg  } msglen=0;
ecbab12008-07-27Stephen R. van den Berg  _mstate=dataready;
a2014e2008-07-14Stephen R. van den Berg  break; case 'H':PD("CopyOutResponse\n"); getcols();
7ac2f12008-07-27Stephen R. van den Berg  if(_c.portal) _c.portal->_fetchlimit=0; // disables further Executes
a2014e2008-07-14Stephen R. van den Berg  break; case 'G':PD("CopyInResponse\n"); getcols();
ecbab12008-07-27Stephen R. van den Berg  _mstate=copyinresponse;
a2014e2008-07-14Stephen R. van den Berg  break; case 'c':PD("CopyDone\n"); msglen-=4; break; case 'E':PD("ErrorResponse\n");
b133b02008-08-06Stephen R. van den Berg  { mapping(string:string) msgresponse; msgresponse=getresponse();
cb26232008-08-04Stephen R. van den Berg  warningsdropcount+=warningscollected; warningscollected=0;
a2014e2008-07-14Stephen R. van den Berg  switch(msgresponse->C) {
17b7532008-07-30Stephen R. van den Berg #define USERERROR(msg) throw(({msg, backtrace()[..<1]}))
a2014e2008-07-14Stephen R. van den Berg  case "P0001": lastmessage=sprintf("%s: %s",msgresponse->S,msgresponse->M);
17b7532008-07-30Stephen R. van den Berg  USERERROR(lastmessage
7ac2f12008-07-27Stephen R. van den Berg  +"\n"+pinpointerror(_c.portal->query,msgresponse->P));
a2014e2008-07-14Stephen R. van den Berg  break; default: lastmessage=sprintf("%s %s:%s %s\n (%s:%s:%s)\n%s%s%s%s\n%s", msgresponse->S,msgresponse->C,msgresponse->P||"",msgresponse->M, msgresponse->F||"",msgresponse->R||"",msgresponse->L||"", addnlifpresent(msgresponse->D),addnlifpresent(msgresponse->H),
7ac2f12008-07-27Stephen R. van den Berg  pinpointerror(_c.portal&&_c.portal->query,msgresponse->P),
a2014e2008-07-14Stephen R. van den Berg  pinpointerror(msgresponse->q,msgresponse->p), addnlifpresent(msgresponse->W)); switch(msgresponse->S) { case "PANIC":werror(lastmessage); }
17b7532008-07-30Stephen R. van den Berg  USERERROR(lastmessage);
a2014e2008-07-14Stephen R. van den Berg  } break;
b133b02008-08-06Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  case 'N':PD("NoticeResponse\n");
b133b02008-08-06Stephen R. van den Berg  { mapping(string:string) msgresponse; msgresponse=getresponse();
cb26232008-08-04Stephen R. van den Berg  if(clearmessage) { warningsdropcount+=warningscollected; clearmessage=warningscollected=0; lastmessage=UNDEFINED; } warningscollected++;
39f78a2008-07-30Stephen R. van den Berg  lastmessage=sprintf("%s%s %s: %s",
6d9aa52008-08-21Stephen R. van den Berg  lastmsgnl(),msgresponse->S,msgresponse->C,msgresponse->M);
a2014e2008-07-14Stephen R. van den Berg  break;
b133b02008-08-06Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  case 'A':PD("NotificationResponse\n"); { msglen-=4+4;
7ac2f12008-07-27Stephen R. van den Berg  int pid=_c.getint32();
a2014e2008-07-14Stephen R. van den Berg  string condition,extrainfo=UNDEFINED; { array(string) ts=getstrings(); switch(sizeof(ts)) { case 0:errtype=protocolerror; break; default:errtype=protocolerror; case 2:extrainfo=ts[1]; case 1:condition=ts[0]; } } PD("%d %s\n%s\n",pid,condition,extrainfo);
b133b02008-08-06Stephen R. van den Berg  runcallback(pid,condition,extrainfo);
a2014e2008-07-14Stephen R. van den Berg  break; }
c015742008-08-21Stephen R. van den Berg  default: if(msgtype!=-1) { PD("Unknown message received %c\n",msgtype); msglen-=4;PD("%O\n",_c.getstring(msglen));msglen=0; errtype=protocolunsupported; } else {
6d9aa52008-08-21Stephen R. van den Berg  string msg=lastmsgnl(); if(!reconnect(1)) { sleep(RECONNECTDELAY); if(!reconnect(1)) { sleep(RECONNECTBACKOFF); reconnect(1); } } ERROR("%s%sConnection lost to database %s@%s:%d/%s %d\n", msg,lastmsgnl(),user,host,port,database,backendpid);
c015742008-08-21Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  break; } if(msglen) errtype=protocolerror; switch(errtype) { case protocolunsupported: ERROR("Unsupported servermessage received %c\n",msgtype); break; case protocolerror:
6d9aa52008-08-21Stephen R. van den Berg  string msg=lastmsgnl(); lastmessage=UNDEFINED;
a2014e2008-07-14Stephen R. van den Berg  reconnect(1);
6d9aa52008-08-21Stephen R. van den Berg  ERROR("%s%sProtocol error with database %s\n", msg,lastmsgnl(),host_info());
a2014e2008-07-14Stephen R. van den Berg  break; case noerror: break; } if(zero_type(waitforstate)) break; }
ecbab12008-07-27Stephen R. van den Berg  PD("Found state %O\n",_mstate); return _mstate;
a2014e2008-07-14Stephen R. van den Berg }
0e0cfa2008-07-25Henrik Grubbström (Grubba) #ifndef UNBUFFEREDIO
a2014e2008-07-14Stephen R. van den Berg private int read_cb(mixed foo, string d) {
7ac2f12008-07-27Stephen R. van den Berg  _c.unread(d);
49a3282008-07-25Stephen R. van den Berg  do _decodemsg();
7ac2f12008-07-27Stephen R. van den Berg  while(_c.bpeek(0)==1);
a2014e2008-07-14Stephen R. van den Berg  return 0; }
0e0cfa2008-07-25Henrik Grubbström (Grubba) #endif
a2014e2008-07-14Stephen R. van den Berg 
cb26232008-08-04Stephen R. van den Berg //! Closes the connection to the database, any running queries are //! terminated instantly. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. void close() {
d432822008-07-31Stephen R. van den Berg  cancelquery();
7ac2f12008-07-27Stephen R. van den Berg  if(_c) _c.sendterminate();
a2014e2008-07-14Stephen R. van den Berg }
cb26232008-08-04Stephen R. van den Berg void destroy() { close(); }
6d9aa52008-08-21Stephen R. van den Berg private int reconnect(void|int force) {
c015742008-08-21Stephen R. van den Berg  Thread.MutexKey connectmtxkey;
7ac2f12008-07-27Stephen R. van den Berg  if(_c) {
a2014e2008-07-14Stephen R. van den Berg  reconnected++;
cb26232008-08-04Stephen R. van den Berg  prepstmtused=0;
a2014e2008-07-14Stephen R. van den Berg #ifdef DEBUG ERROR("While debugging, reconnects are forbidden\n"); exit(1); #endif if(!force)
7ac2f12008-07-27Stephen R. van den Berg  _c.sendterminate();
6d9aa52008-08-21Stephen R. van den Berg  _c.close(); _c=0;
cb26232008-08-04Stephen R. van den Berg  foreach(prepareds;;mapping tp) m_delete(tp,"preparedname");
c015742008-08-21Stephen R. van den Berg  if(!(connectmtxkey = _stealmutex.trylock(2)))
6d9aa52008-08-21Stephen R. van den Berg  return 0; // Recursive reconnect, bailing out } if(!(_c=getsocket())) { string msg=sprintf("Couldn't connect to database on %s:%d\n",host,port); if(force) { lastmessage=lastmsgnl()+msg; return 0; } else ERROR(msg);
a2014e2008-07-14Stephen R. van den Berg  } _closesent=0;
ecbab12008-07-27Stephen R. van den Berg  _mstate=unauthenticated;
a2014e2008-07-14Stephen R. van den Berg  qstate=queryidle; runtimeparameter=([]);
7ac2f12008-07-27Stephen R. van den Berg  array(string) plugbuf=({"",_c.plugint32(PG_PROTOCOL(3,0))});
a2014e2008-07-14Stephen R. van den Berg  if(user) plugbuf+=({"user\0",user,"\0"}); if(database) plugbuf+=({"database\0",database,"\0"});
b156722008-08-25Stephen R. van den Berg  foreach(options-(<"use_ssl","force_ssl">);
aaa4562008-08-21Stephen R. van den Berg  string name;mixed value)
cb26232008-08-04Stephen R. van den Berg  plugbuf+=({name,"\0",(string)value,"\0"});
a2014e2008-07-14Stephen R. van den Berg  plugbuf+=({"\0"}); int len=4; foreach(plugbuf;;string s) len+=sizeof(s);
7ac2f12008-07-27Stephen R. van den Berg  plugbuf[0]=_c.plugint32(len);
4741cd2008-07-31Stephen R. van den Berg  _c.write(plugbuf);
a2014e2008-07-14Stephen R. van den Berg  PD("%O\n",plugbuf);
6d9aa52008-08-21Stephen R. van den Berg  { mixed err=catch(_decodemsg(readyforquery)); if(err) if(force) throw(err); else return 0; }
a2014e2008-07-14Stephen R. van den Berg  PD("%O\n",runtimeparameter);
6d9aa52008-08-21Stephen R. van den Berg  if(force) { lastmessage=lastmsgnl()+"Reconnected to database "+host_info();
b133b02008-08-06Stephen R. van den Berg  runcallback(backendpid,"_reconnect","");
6d9aa52008-08-21Stephen R. van den Berg  } return 1;
a2014e2008-07-14Stephen R. van den Berg } //! @decl void reload() //! //! Resets the connection to the database. Can be used for //! a variety of reasons, for example to detect the status of a connection.
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso //! cancelquery
a2014e2008-07-14Stephen R. van den Berg void reload(void|int special) { mixed err; int didsync; if(err = catch {
4741cd2008-07-31Stephen R. van den Berg  sendclose(1);
a2014e2008-07-14Stephen R. van den Berg  PD("Portalsinflight: %d\n",portalsinflight); if(!portalsinflight) {
4741cd2008-07-31Stephen R. van den Berg  if(!earlyclose) { PD("Sync\n"); _c.sendcmd(({"S",_c.plugint32(4)}),2); }
a2014e2008-07-14Stephen R. van den Berg  didsync=1;
488ce72008-07-24Stephen R. van den Berg  if(!special) {
49a3282008-07-25Stephen R. van den Berg  _decodemsg(readyforquery);
cb26232008-08-04Stephen R. van den Berg  foreach(prepareds;;mapping tp) { m_delete(tp,"datatypeoid"); m_delete(tp,"datarowdesc");
488ce72008-07-24Stephen R. van den Berg  } }
a2014e2008-07-14Stephen R. van den Berg  }
4741cd2008-07-31Stephen R. van den Berg  earlyclose=0;
a2014e2008-07-14Stephen R. van den Berg  }) {
4741cd2008-07-31Stephen R. van den Berg  earlyclose=0;
a2014e2008-07-14Stephen R. van den Berg  PD("%O\n",err);
6d9aa52008-08-21Stephen R. van den Berg  if(!reconnect(1)) ERROR(lastmessage);
a2014e2008-07-14Stephen R. van den Berg  } else if(didsync && special==2)
49a3282008-07-25Stephen R. van den Berg  _decodemsg(readyforquery);
a2014e2008-07-14Stephen R. van den Berg #ifndef UNBUFFEREDIO
7ac2f12008-07-27Stephen R. van den Berg  _c.set_read_callback(read_cb);
a2014e2008-07-14Stephen R. van den Berg #endif } //! @decl void select_db(string dbname) //! //! This function allows you to connect to a database. Due to //! restrictions of the Postgres frontend-backend protocol, you always //! have to be connected to a database, so in fact this function just //! allows you to connect to a different database on the same server. //! //! @note //! This function @b{can@} raise exceptions if something goes wrong //! (backend process not running, not enough permissions..) //! //! @seealso //! create void select_db(string dbname) { database=dbname; reconnect();
b133b02008-08-06Stephen R. van den Berg  reconnected=0;
a2014e2008-07-14Stephen R. van den Berg } //! With PostgreSQL you can LISTEN to NOTIFY events. //! This function allows you to detect and handle such events. //! //! @param condition //! Name of the notification event we're listening //! to. A special case is the empty string, which matches all events, //! and can be used as fallback function which is called only when the
b133b02008-08-06Stephen R. van den Berg //! specific condition is not handled. Another special case is //! @[_reconnect] which gets called whenever the connection unexpectedly //! drops and reconnects to the database.
a2014e2008-07-14Stephen R. van den Berg //! //! @param notify_cb //! Function to be called on receiving a notification-event of //! condition @[condition]. //! The callback function is invoked with
9b77ae2008-07-16Peter Bortas //! @expr{void notify_cb(pid,condition,extrainfo, .. args);@}
a2014e2008-07-14Stephen R. van den Berg //! @[pid] is the process id of the database session that originated //! the event. @[condition] contains the current condition. //! @[extrainfo] contains optional extra information specified by //! the database. //! The rest of the arguments to @[notify_cb] are passed //! verbatim from @[args]. //! The callback function must return no value.
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @param selfnotify //! Normally notify events generated by your own session are ignored. //! If you want to receive those as well, set @[selfnotify] to one.
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @param args //! Extra arguments to pass to @[notify_cb].
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. void set_notify_callback(string condition, void|function(int,string,string,mixed ...:void) notify_cb,void|int selfnotify, mixed ... args) { if(!notify_cb) m_delete(notifylist,condition); else { array old=notifylist[condition]; if(!old) old=({notify_cb}); if(selfnotify||args) old+=({selfnotify}); if(args) old+=args; notifylist[condition]=old; } }
b133b02008-08-06Stephen R. van den Berg final private void runcallback(int pid,string condition,string extrainfo) { array cb; if((cb=notifylist[condition]||notifylist[""]) && (pid!=backendpid || sizeof(cb)>1 && cb[1])) cb[0](pid,condition,extrainfo,@cb[2..]); }
a2014e2008-07-14Stephen R. van den Berg //! This function quotes magic characters inside strings embedded in a //! textual query. Quoting must not be done for parameters passed in //! bindings. //! //! @seealso
cb26232008-08-04Stephen R. van den Berg //! big_query, quotebinary, create
a2014e2008-07-14Stephen R. van den Berg string quote(string s) { string r=runtimeparameter->standard_conforming_strings; if(r && r=="on") return replace(s, "'", "''"); return replace(s, ({ "'", "\\" }), ({ "''", "\\\\" }) ); }
cb26232008-08-04Stephen R. van den Berg //! This function quotes magic characters inside binaries (bytea) embedded in a //! textual query. Quoting must not be done for parameters passed in //! bindings. //! //! @seealso //! big_query, quote //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. string quotebinary(string s) { return replace(s, ({ "'", "\\", "\0" }), ({ "''", "\\\\", "\\000" }) ); }
a2014e2008-07-14Stephen R. van den Berg //! This function creates a new database with the given name (assuming we //! have enough permissions to do this). //! //! @seealso //! drop_db void create_db(string db) { big_query("CREATE DATABASE :db",([":db":db])); } //! This function destroys a database and all the data it contains (assuming
cb26232008-08-04Stephen R. van den Berg //! we have enough permissions to do so). It is not possible to delete //! the database you're currently connected to. You can connect to database //! @[template1] to avoid connecting to any live database.
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso //! create_db void drop_db(string db) { big_query("DROP DATABASE :db",([":db":db])); } //! This function returns a string describing the server we are //! talking to. It has the form @expr{"servername/serverversion"@} //! (like the HTTP protocol description) and is most useful in //! conjunction with the generic SQL-server module.
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso //! host_info
a2014e2008-07-14Stephen R. van den Berg string server_info () { return DRIVERNAME"/"+(runtimeparameter->server_version||"unknown"); } //! Lists all the databases available on the server. //! If glob is specified, lists only those databases matching it. array(string) list_dbs (void|string glob) { array row,ret=({}); object res=big_query("SELECT d.datname " "FROM pg_database d " "WHERE d.datname ILIKE :glob " "ORDER BY d.datname", ([":glob":glob2reg(glob)])); while(row=res->fetch_row()) ret+=({row[0]}); return ret; } //! Returns an array containing the names of all the tables and views in the //! path in the currently selected database. //! If a glob is specified, it will return only those tables with matching //! names. array(string) list_tables (void|string glob) {
7ac2f12008-07-27Stephen R. van den Berg  array row,ret=({}); // This query might not work on PostgreSQL 7.4 object res=big_query( // due to missing schemasupport
a2014e2008-07-14Stephen R. van den Berg  "SELECT CASE WHEN 'public'=n.nspname THEN '' ELSE n.nspname||'.' END " " ||c.relname AS name "
7ac2f12008-07-27Stephen R. van den Berg  "FROM pgcatalog.pgclass c " " LEFT JOIN pgcatalog.pg_namespace n ON n.oid=c.relnamespace " "WHERE c.relkind IN ('r','v') AND n.nspname<>'pgcatalog' " " AND n.nspname !~ '^pg_toast' AND pgcatalog.pg_table_is_visible(c.oid) "
a2014e2008-07-14Stephen R. van den Berg  " AND c.relname ILIKE :glob " " ORDER BY 1", ([":glob":glob2reg(glob)])); while(row=res->fetch_row()) ret+=({row[0]}); return ret; } //! Returns a mapping, indexed on the column name, of mappings describing //! the attributes of a table of the current database. //! If a glob is specified, will return descriptions only of the columns //! matching it. //! //! The currently defined fields are: //! //! @mapping //! @member int "is_shared" //! //! @member string "owner" //! Tableowner //! //! @member string "length" //! Size of the columndatatype //! //! @member string "text" //! A textual description of the internal (to the server) type-name //! //! @member mixed "default" //! Default value for the column //! //! @member mixed "schema" //! Schema the table belongs to //! //! @member mixed "table" //! Name of the table //! //! @member mixed "kind" //! Type of table //! //! @member mixed "has_index" //! If the table has any indices //! //! @member mixed "has_primarykey" //! If the table has a primary key //! //! @member mixed "rowcount" //! Estimated rowcount of the table //! //! @member mixed "pagecount" //! Estimated pagecount of the table //! //! @endmapping //! //! Setting wild to * will include system columns in the list. //! array(mapping(string:mixed)) list_fields(void|string table, void|string wild) { array row, ret=({}); string schema=UNDEFINED; sscanf(table||"*", "%s.%s", schema, table); object res = big_query( "SELECT a.attname, a.atttypid, t.typname, a.attlen, " " c.relhasindex, c.relhaspkey, c.reltuples, c.relpages, " " c.relisshared, t.typdefault, " " n.nspname, c.relname, " " CASE c.relkind " " WHEN 'r' THEN 'table' " " WHEN 'v' THEN 'view' " " WHEN 'i' THEN 'index' " " WHEN 'S' THEN 'sequence' " " WHEN 's' THEN 'special' "
7ac2f12008-07-27Stephen R. van den Berg  " WHEN 't' THEN 'toastable' " // pun intended :-)
a2014e2008-07-14Stephen R. van den Berg  " WHEN 'c' THEN 'composite' " " ELSE c.relkind::TEXT END AS relkind, " " r.rolname "
7ac2f12008-07-27Stephen R. van den Berg  "FROM pgcatalog.pgclass c " " LEFT JOIN pgcatalog.pg_namespace n ON n.oid=c.relnamespace " " JOIN pgcatalog.pg_roles r ON r.oid=c.relowner " " JOIN pgcatalog.pg_attribute a ON c.oid=a.attrelid " " JOIN pgcatalog.pg_type t ON a.atttypid=t.oid "
a2014e2008-07-14Stephen R. van den Berg  "WHERE c.relname ILIKE :table AND " " (n.nspname ILIKE :schema OR " " :schema IS NULL "
7ac2f12008-07-27Stephen R. van den Berg  " AND n.nspname<>'pgcatalog' AND n.nspname !~ '^pg_toast') "
a2014e2008-07-14Stephen R. van den Berg  " AND a.attname ILIKE :wild " " AND (a.attnum>0 OR '*'=:realwild) " "ORDER BY n.nspname,c.relname,a.attnum,a.attname", ([":schema":glob2reg(schema),":table":glob2reg(table), ":wild":glob2reg(wild),":realwild":wild])); array colnames=res->fetch_fields(); { mapping(string:string) renames=([ "attname":"name", "nspname":"schema", "relname":"table", "rolname":"owner", "typname":"type", "attlen":"length", "typdefault":"default", "relisshared":"is_shared", "atttypid":"typeoid", "relkind":"kind", "relhasindex":"has_index", "relhaspkey":"has_primarykey", "reltuples":"rowcount", "relpages":"pagecount", ]); foreach(colnames;int i;mapping m) { string nf,field=m->name; if(nf=renames[field]) field=nf; colnames[i]=field; } } #define delifzero(m,field) if(!(m)[field]) m_delete(m,field) while(row=res->fetch_row()) { mapping m=mkmapping(colnames,row); delifzero(m,"is_shared"); delifzero(m,"has_index"); delifzero(m,"has_primarykey"); delifzero(m,"default"); ret+=({m}); } return ret; } private int oidformat(int oid) { switch(oid) { case BOOLOID: case BYTEAOID: case CHAROID: case INT8OID: case INT2OID: case INT4OID: case TEXTOID: case OIDOID: case XMLOID: case MACADDROID: case BPCHAROID: case VARCHAROID: case CTIDOID: case UUIDOID: return 1; //binary }
7ac2f12008-07-27Stephen R. van den Berg  return 0; // text
a2014e2008-07-14Stephen R. van den Berg }
49a3282008-07-25Stephen R. van den Berg final void _sendexecute(int fetchlimit) {
7ac2f12008-07-27Stephen R. van den Berg  string portalname=_c.portal->_portalname;
a2014e2008-07-14Stephen R. van den Berg  PD("Execute portal %s fetchlimit %d\n",portalname,fetchlimit);
7ac2f12008-07-27Stephen R. van den Berg  _c.sendcmd(({"E",_c.plugint32(4+sizeof(portalname)+1+4),portalname,
4741cd2008-07-31Stephen R. van den Berg  "\0",_c.plugint32(fetchlimit)}),!!fetchlimit); if(!fetchlimit) {
80dbe82008-08-08Stephen R. van den Berg  _c.portal->_fetchlimit=0; // disables further Executes
4741cd2008-07-31Stephen R. van den Berg  earlyclose=1; if(sizeof(portalname)) { PD("Close portal %s & Sync\n",portalname); _c.sendcmd(({"C",_c.plugint32(4+1+sizeof(portalname)+1), "P",portalname,"\0"})); } _c.sendcmd(({"S",_c.plugint32(4)}),2); }
80dbe82008-08-08Stephen R. van den Berg  else _c.portal->_inflight+=fetchlimit;
a2014e2008-07-14Stephen R. van den Berg }
4741cd2008-07-31Stephen R. van den Berg final private void sendclose(void|int hold) {
a2014e2008-07-14Stephen R. van den Berg  string portalname;
7ac2f12008-07-27Stephen R. van den Berg  if(_c.portal && (portalname=_c.portal->_portalname)) { _c.portal->_portalname = UNDEFINED; _c.setportal();
b133b02008-08-06Stephen R. van den Berg  portalsinflight--;
a2014e2008-07-14Stephen R. van den Berg #ifdef DEBUGMORE PD("Closetrace %O\n",backtrace()); #endif
b212b52008-07-26Stephen R. van den Berg  if(!sizeof(portalname)) unnamedportalinuse--;
4741cd2008-07-31Stephen R. van den Berg  if(sizeof(portalname)) { if(!earlyclose) { PD("Close portal %s\n",portalname); _c.sendcmd(({"C",_c.plugint32(4+1+sizeof(portalname)+1), "P",portalname,"\0"}),!hold||portalsinflight?1:0); } _closesent=1; }
a2014e2008-07-14Stephen R. van den Berg  } } final private string trbackendst(int c) { switch(c) { case 'I':return "idle"; case 'T':return "intransaction"; case 'E':return "infailedtransaction"; } return "unknown"; } //! Returns the current commitstatus of the connection. Returns either one of: //! unknown //! idle //! intransaction //! infailedtransaction //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. final string status_commit() { return trbackendst(backendstatus); } //! This is the only provided interface which allows you to query the //! database. If you wish to use the simpler "query" function, you need to //! use the @[Sql.Sql] generic SQL-object. //! //! It returns a pgsql_result object (which conforms to the //! @[Sql.sql_result] standard interface for accessing data). I //! recommend using @[query()] for simpler queries (because it is //! easier to handle, but stores all the result in memory), and //! @[big_query()] for queries you expect to return huge amounts of //! data (it's harder to handle, but fetches results on demand). //!
80dbe82008-08-08Stephen R. van den Berg //! Bindings are supported natively straight through the network. //! Special bindings supported are: ":_cache", to force caching or //! not caching for the query at hand depending on the mappingvalue. //!
a2014e2008-07-14Stephen R. van den Berg //! @note //! This function @b{can@} raise exceptions. //!
17b7532008-07-30Stephen R. van den Berg //! @note //! This function does not support multiple queries in one querystring. //! I.e. it allows for but does not require a trailing semicolon, but it //! simply ignores any commands after the first semicolon. This can be //! viewed as a limited protection against SQL-injection attacks. //!
a2014e2008-07-14Stephen R. van den Berg //! @seealso
f28c0d2008-08-01Stephen R. van den Berg //! @[Sql.Sql], @[Sql.sql_result], @[Sql.pgsql_util.pgsql_result]
b156722008-08-25Stephen R. van den Berg object big_query(string q,void|mapping(string|int:mixed) bindings, void|int _alltyped) {
a2014e2008-07-14Stephen R. van den Berg  string preparedname=""; string portalname="";
80dbe82008-08-08Stephen R. van den Berg  int forcecache=-1;
a2014e2008-07-14Stephen R. van den Berg  if(stringp(q) && String.width(q)>8) q=string_to_utf8(q); array(string|int) paramValues; if(bindings) { int pi=0,rep=0; paramValues=allocate(sizeof(bindings)); array(string) from=allocate(sizeof(bindings)); array(string) to=allocate(sizeof(bindings)); foreach(bindings; mixed name; mixed value) {
7ac2f12008-07-27Stephen R. van den Berg  if(stringp(name)) { // Throws if mapping key is empty string
a2014e2008-07-14Stephen R. van den Berg  if(name[0]!=':') name=":"+name;
7ac2f12008-07-27Stephen R. van den Berg  if(name[1]=='_') { // Special option parameter
80dbe82008-08-08Stephen R. van den Berg  switch(name) { case ":_cache":forcecache=(int)value; break; }
a2014e2008-07-14Stephen R. van den Berg  continue; } } from[rep]=name; string rval; if(multisetp(value)) { rval=sizeof(value) ? indices(value)[0] : ""; } else { if(zero_type(value))
7ac2f12008-07-27Stephen R. van den Berg  paramValues[pi++]=UNDEFINED; // NULL
a2014e2008-07-14Stephen R. van den Berg  else { if(stringp(value) && String.width(value)>8) value=string_to_utf8(value); paramValues[pi++]=value; } rval="$"+(string)pi; } to[rep++]=rval; } if(rep--) q=replace(q,from[..rep],to[..rep]); paramValues= pi ? paramValues[..pi-1] : ({}); } else paramValues = ({});
cb26232008-08-04Stephen R. van den Berg  mapping(string:mixed) tp;
a2014e2008-07-14Stephen R. van den Berg  int tstart;
80dbe82008-08-08Stephen R. van den Berg  if(forcecache==1 || forcecache!=0 && sizeof(q)>=MINPREPARELENGTH) {
cb26232008-08-04Stephen R. van den Berg  if(tp=prepareds[q]) { if(tp->preparedname) prepstmtused++, preparedname=tp->preparedname; else if((tstart=tp->trun) && tp->tparse*FACTORPLAN>=tstart)
a2014e2008-07-14Stephen R. van den Berg  preparedname=PREPSTMTPREFIX+(string)pstmtcount++; } else { if(totalhits>=cachedepth) { array(string) plugbuf=({});
cb26232008-08-04Stephen R. van den Berg  foreach(prepareds;string ind;tp) { int oldhits=tp->hits; totalhits-=oldhits-(tp->hits=oldhits>>1);
a2014e2008-07-14Stephen R. van den Berg  if(oldhits<=1) {
cb26232008-08-04Stephen R. van den Berg  string oldprep=tp->preparedname;
a2014e2008-07-14Stephen R. van den Berg  if(oldprep) { PD("Close statement %s\n",oldprep);
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({"C",_c.plugint32(4+1+sizeof(oldprep)+1),
a2014e2008-07-14Stephen R. van den Berg  "S",oldprep,"\0"}); } m_delete(prepareds,ind); } } if(sizeof(plugbuf))
7ac2f12008-07-27Stephen R. van den Berg  _c.sendcmd(plugbuf,1); // close expireds
a2014e2008-07-14Stephen R. van den Berg  PD("%O\n",plugbuf); }
cb26232008-08-04Stephen R. van den Berg  prepareds[q]=tp=([]);
a2014e2008-07-14Stephen R. van den Berg  } tstart=gethrtime();
7ac2f12008-07-27Stephen R. van den Berg  } // pgsql_result autoassigns to portal
cb26232008-08-04Stephen R. van den Berg  else tp=UNDEFINED;
b156722008-08-25Stephen R. van den Berg  .pgsql_util.pgsql_result(this,q,_fetchlimit,portalbuffersize,_alltyped);
a2014e2008-07-14Stephen R. van den Berg  if(unnamedportalinuse) portalname=PORTALPREFIX+(string)pportalcount++; else unnamedportalinuse++;
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_portalname=portalname;
a2014e2008-07-14Stephen R. van den Berg  qstate=inquery;
cb26232008-08-04Stephen R. van den Berg  portalsinflight++; portalsopened++;
39f78a2008-07-30Stephen R. van den Berg  clearmessage=1;
a2014e2008-07-14Stephen R. van den Berg  mixed err; if(err = catch {
cb26232008-08-04Stephen R. van den Berg  if(!sizeof(preparedname) || !tp || !tp->preparedname) {
a2014e2008-07-14Stephen R. van den Berg  PD("Parse statement %s\n",preparedname);
13a1202008-07-26Stephen R. van den Berg  // Even though the protocol doesn't require the Parse command to be // followed by a flush, it makes a VERY noticeable difference in // performance if it is omitted; seems like a flaw in the PostgreSQL
cb26232008-08-04Stephen R. van den Berg  // server v8.3.3
7ac2f12008-07-27Stephen R. van den Berg  _c.sendcmd(({"P",_c.plugint32(4+sizeof(preparedname)+1+sizeof(q)+1+2),
4741cd2008-07-31Stephen R. van den Berg  preparedname,"\0",q,"\0",_c.plugint16(0)}),3);
a2014e2008-07-14Stephen R. van den Berg  PD("Query: %O\n",q);
4741cd2008-07-31Stephen R. van den Berg  } // sends Parameter- and RowDescription for 'S'
cb26232008-08-04Stephen R. van den Berg  if(!tp || !tp->datatypeoid) {
488ce72008-07-24Stephen R. van den Berg  PD("Describe statement %s\n",preparedname);
7ac2f12008-07-27Stephen R. van den Berg  _c.sendcmd(({"D",_c.plugint32(4+1+sizeof(preparedname)+1),
488ce72008-07-24Stephen R. van den Berg  "S",preparedname,"\0"}),1); } else {
cb26232008-08-04Stephen R. van den Berg  skippeddescribe++; _c.portal->_datatypeoid=tp->datatypeoid; _c.portal->_datarowdesc=tp->datarowdesc;
488ce72008-07-24Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  { array(string) plugbuf=({"B",UNDEFINED}); int len=4+sizeof(portalname)+1+sizeof(preparedname)+1 +2+sizeof(paramValues)*(2+4)+2+2; plugbuf+=({portalname,"\0",preparedname,"\0",
7ac2f12008-07-27Stephen R. van den Berg  _c.plugint16(sizeof(paramValues))});
cb26232008-08-04Stephen R. van den Berg  if(!tp || !tp->datatypeoid) {
49a3282008-07-25Stephen R. van den Berg  _decodemsg(gotparameterdescription);
cb26232008-08-04Stephen R. van den Berg  if(tp) tp->datatypeoid=_c.portal->_datatypeoid;
488ce72008-07-24Stephen R. van den Berg  }
7ac2f12008-07-27Stephen R. van den Berg  array dtoid=_c.portal->_datatypeoid;
a2014e2008-07-14Stephen R. van den Berg  foreach(dtoid;;int textbin)
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugint16(oidformat(textbin))}); plugbuf+=({_c.plugint16(sizeof(paramValues))});
a2014e2008-07-14Stephen R. van den Berg  foreach(paramValues;int i;mixed value) { if(zero_type(value))
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugint32(-1)}); // NULL
a2014e2008-07-14Stephen R. van den Berg  else switch(dtoid[i]) { default: { int k; len+=k=sizeof(value=(string)value);
4741cd2008-07-31Stephen R. van den Berg  plugbuf+=({_c.plugint32(k),value}); break;
a2014e2008-07-14Stephen R. van den Berg  }
7ac2f12008-07-27Stephen R. van den Berg  case BOOLOID:plugbuf+=({_c.plugint32(1)});len++;
a2014e2008-07-14Stephen R. van den Berg  switch(stringp(value)?value[0]:value) {
4741cd2008-07-31Stephen R. van den Berg  case 'o':case 'O': _c.plugbyte(stringp(value)&&sizeof(value)>1
a2014e2008-07-14Stephen R. van den Berg  &&(value[1]=='n'||value[1]=='N'));
4741cd2008-07-31Stephen R. van den Berg  break;
a2014e2008-07-14Stephen R. van den Berg  case 0:case 'f':case 'F':case 'n':case 'N':
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugbyte(0)});
a2014e2008-07-14Stephen R. van den Berg  break; default:
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugbyte(1)});
a2014e2008-07-14Stephen R. van den Berg  break; } break;
7ac2f12008-07-27Stephen R. van den Berg  case CHAROID:plugbuf+=({_c.plugint32(1)});len++;
4741cd2008-07-31Stephen R. van den Berg  if(intp(value)) plugbuf+=({_c.plugbyte(value)}); else { value=(string)value; if(sizeof(value)!=1) ERROR("\"char\" types must be 1 byte wide, got %d\n", sizeof(value)); plugbuf+=({value}); }
a2014e2008-07-14Stephen R. van den Berg  break; case INT8OID:len+=8;
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugint32(8),_c.plugint64((int)value)});
a2014e2008-07-14Stephen R. van den Berg  break;
80dbe82008-08-08Stephen R. van den Berg  case OIDOID:
a2014e2008-07-14Stephen R. van den Berg  case INT4OID:len+=4;
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugint32(4),_c.plugint32((int)value)});
a2014e2008-07-14Stephen R. van den Berg  break; case INT2OID:len+=2;
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugint32(2),_c.plugint16((int)value)});
a2014e2008-07-14Stephen R. van den Berg  break; } }
cb26232008-08-04Stephen R. van den Berg  if(!tp || !tp->datarowdesc) { if(tp && fetchprefix->match(q)) // Don't cache FETCH m_delete(prepareds,q),tp=0;
49a3282008-07-25Stephen R. van den Berg  _decodemsg(gotrowdescription);
cb26232008-08-04Stephen R. van den Berg  if(tp) tp->datarowdesc=_c.portal->_datarowdesc;
488ce72008-07-24Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  { array a;int i;
7ac2f12008-07-27Stephen R. van den Berg  len+=(i=sizeof(a=_c.portal->_datarowdesc))*2; plugbuf+=({_c.plugint16(i)});
a2014e2008-07-14Stephen R. van den Berg  foreach(a;;mapping col)
7ac2f12008-07-27Stephen R. van den Berg  plugbuf+=({_c.plugint16(oidformat(col->type))});
a2014e2008-07-14Stephen R. van den Berg  }
7ac2f12008-07-27Stephen R. van den Berg  plugbuf[1]=_c.plugint32(len);
a2014e2008-07-14Stephen R. van den Berg  PD("Bind portal %s statement %s\n",portalname,preparedname);
7ac2f12008-07-27Stephen R. van den Berg  _c.sendcmd(plugbuf);
a2014e2008-07-14Stephen R. van den Berg #ifdef DEBUGMORE PD("%O\n",plugbuf); #endif }
7ac2f12008-07-27Stephen R. van den Berg  _c.portal->_statuscmdcomplete=UNDEFINED;
80dbe82008-08-08Stephen R. van den Berg  _sendexecute(_fetchlimit && !limitpostfix->match(q) // Optimisation for LIMIT 1 && FETCHLIMITLONGRUN);
cb26232008-08-04Stephen R. van den Berg  if(tp) {
49a3282008-07-25Stephen R. van den Berg  _decodemsg(bindcomplete);
a2014e2008-07-14Stephen R. van den Berg  int tend=gethrtime(); if(tend==tstart) m_delete(prepareds,q); else {
cb26232008-08-04Stephen R. van den Berg  tp->hits++;
a2014e2008-07-14Stephen R. van den Berg  totalhits++;
cb26232008-08-04Stephen R. van den Berg  if(!tp->preparedname) {
a2014e2008-07-14Stephen R. van den Berg  if(sizeof(preparedname))
cb26232008-08-04Stephen R. van den Berg  tp->preparedname=preparedname;
a2014e2008-07-14Stephen R. van den Berg  tstart=tend-tstart;
cb26232008-08-04Stephen R. van den Berg  if(!tp->tparse || tp->tparse>tstart) tp->tparse=tstart;
a2014e2008-07-14Stephen R. van den Berg  }
cb26232008-08-04Stephen R. van den Berg  tp->trunstart=tend;
a2014e2008-07-14Stephen R. van den Berg  }
cb26232008-08-04Stephen R. van den Berg  tprepared=tp;
a2014e2008-07-14Stephen R. van den Berg  } }) { PD("%O\n",err); reload(1); backendstatus=UNDEFINED; throw(err); }
ecbab12008-07-27Stephen R. van den Berg  { object tportal=_c.portal; // Make copy, because it might dislodge
7ac2f12008-07-27Stephen R. van den Berg  tportal->fetch_row(1); // upon initial fetch_row()
a2014e2008-07-14Stephen R. van den Berg  return tportal; } } //! This is an alias for @[big_query()], since @[big_query()] already supports //! streaming of multiple simultaneous queries through the same connection. //! //! @seealso //! @[big_query], @[Sql.Sql], @[Sql.sql_result] object streaming_query(string q,void|mapping(string|int:mixed) bindings) { return big_query(q,bindings); }
b156722008-08-25Stephen R. van den Berg  //! This function returns an object that allows streaming and typed //! results. //! //! @seealso //! @[big_query], @[Sql.Sql], @[Sql.sql_result] object big_typed_query(string q,void|mapping(string|int:mixed) bindings) { return big_query(q,bindings,1); }