Branch: Tag:

2017-11-21

2017-11-21 01:26:52 by Stephen R. van den Berg <srb@cuci.nl>

pgsql: Create proxy object to avoid circular references.

17:   //! PostgreSQL network protocol version 3, authentication methods   //! currently supported are: cleartext, md5 and scram (recommended).   //! @item + //! Optional asynchronous query interface through callbacks. + //! @item   //! Streaming queries which do not buffer the whole resultset in memory.   //! @item   //! Automatic binary transfers to and from the database for most common
52:   //! difficult.   //!   //! @seealso - //! @[Sql.Sql], @[Sql.postgres], @url{http://www.postgresql.org/docs/@} + //! @[Sql.Sql], @[Sql.postgres], + //! @url{https://www.postgresql.org/docs/current/static/@}      #pike __REAL_VERSION__   #require constant(Thread.Thread)
61:      #define ERROR(X ...) predef::error(X)    - final int _fetchlimit=FETCHLIMIT; - final Thread.Mutex _unnamedportalmux; - private Thread.Mutex unnamedstatement; - private Thread.MutexKey termlock; - private Thread.ResourceCountKey backendreg; - final Thread.ResourceCount _portalsinflight, _statementsinflight; - final int _wasparallelisable; - final int _intransaction; + private .pgsql_util.proxy proxy;    - private .pgsql_util.conxion c; - private string cancelsecret; - private int backendpid, backendstatus; - final mapping(string:mixed) _options; - private array(string) lastmessage=({}); - private int clearmessage; - private mapping(string:array(mixed)) notifylist=([]); - final mapping(string:string) _runtimeparameter; - final mapping(string:mapping(string:mixed)) _prepareds=([]); +    private int pstmtcount;   private int ptstmtcount; // Periodically one would like to reset these    // but checking when this is safe to do    // probably is more costly than the gain - final int _pportalcount; - private int totalhits; - private int cachedepth=STATEMENTCACHEDEPTH; - private int timeout=QUERYTIMEOUT; - private int portalbuffersize=PORTALBUFFERSIZE; +    #ifdef PG_STATS   private int skippeddescribe; // Number of times we skipped Describe phase   private int portalsopened; // Number of portals opened   private int prepstmtused; // Number of times we used prepared statements   #endif - final int _msgsreceived; // Number of protocol messages received - final int _bytesreceived; // Number of bytes received - private int warningsdropcount; // Number of uncollected warnings - private int warningscollected; - private int invalidatecache; - private Thread.Queue qportals; - final mixed _delayederror; - private function (:void) readyforquery_cb; + private int cachedepth = STATEMENTCACHEDEPTH; + private int portalbuffersize = PORTALBUFFERSIZE; + private int timeout = QUERYTIMEOUT;    - final string _host; - final int _port; - private string database, user, pass; - private Crypto.SCRAM SASLcontext; - private Thread.Condition waitforauthready; - final Thread.Mutex _shortmux; - final int _readyforquerycount; -  +    protected string _sprintf(int type) { -  string res=UNDEFINED; +  string res;    switch(type) {    case 'O': -  res=sprintf(DRIVERNAME"(%s@%s:%d/%s,%d,%d)", -  user,_host,_port,database,c?->socket&&c->socket->query_fd(),backendpid); +  res = sprintf(DRIVERNAME"(%s@%s:%d/%s,%d,%d)", +  proxy.user, proxy.host, proxy.port, proxy.database, +  proxy.c?->socket && proxy.c->socket->query_fd(), proxy.backendpid);    break;    }    return res;   }    - //! With no arguments, this function initialises (reinitialises if a - //! connection has been set up previously) a connection to the + //! With no arguments, this function initialises 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
156:   //! Send queries to and retrieve results from the database using text   //! instead of the, generally more efficient, default native binary method.   //! Turning this on will allow multiple statements per query separated - //! by semicolons. + //! by semicolons (not recommended).   //! @member int "sync_parse"   //! Set it to zero to turn synchronous parsing off for statements.   //! Setting this to off can cause surprises because statements could
184:   //! For the numerous other options please check the PostgreSQL manual.   //!   //! @note - //! You need to have a database selected before using the sql-object, + //! 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 to you.   //! -  + //! @note + //! It is possible that the exception from a failed connect + //! will not be triggered on this call (because the connect + //! proceeds asynchronously in the background), but on the first + //! attempt to actually use the database instead. + //!   //! @seealso   //! @[Postgres.postgres], @[Sql.Sql], @[select_db()], - //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+search_path@} + //! @url{https://www.postgresql.org/docs/current/static/runtime-config-client.html@}   protected void create(void|string host, void|string database,    void|string user, void|string pass,    void|mapping(string:mixed) options) { -  this::pass = pass && pass != "" ? Standards.IDNA.to_ascii(pass) : pass; +  string spass = pass && pass != "" ? Standards.IDNA.to_ascii(pass) : pass;    if(pass) {    String.secure(pass);    pass = "CENSORED";    } -  this::user = user && user != "" ? Standards.IDNA.to_ascii(user, 1) : user; -  this::database = database; -  _options = options || ([]); -  -  if(!host) host = PGSQL_DEFAULT_HOST; -  if(has_value(host,":") && sscanf(host,"%s:%d",host,_port)!=2) -  ERROR("Error in parsing the hostname argument\n"); -  this::_host = host; -  -  if(!_port) -  _port = PGSQL_DEFAULT_PORT; -  backendreg = .pgsql_util.register_backend(); -  _shortmux=Thread.Mutex(); -  PD("Connect\n"); -  waitforauthready = Thread.Condition(); -  qportals=Thread.Queue(); -  _readyforquerycount = 1; -  qportals->write(1); -  if (!(c = .pgsql_util.conxion(this, qportals, 0))) -  ERROR("Couldn't connect to database on %s:%d\n",_host,_port); -  _runtimeparameter = ([]); -  _unnamedportalmux = Thread.Mutex(); -  unnamedstatement = Thread.Mutex(); -  readyforquery_cb = connect_cb; -  _portalsinflight = Thread.ResourceCount(); -  _statementsinflight = Thread.ResourceCount(); -  _wasparallelisable = 0; +  proxy = .pgsql_util.proxy(host, database, +  user && user != "" ? Standards.IDNA.to_ascii(user, 1) : user, +  spass, options || ([]));   }      //! @returns
251:   //! @[big_query()]   /*semi*/final string error(void|int clear) {    throwdelayederror(this); -  string s=lastmessage*"\n"; -  if(clear) -  lastmessage=({}); -  warningscollected=0; -  return sizeof(s) && s; +  return proxy.geterror(clear);   }      //! This function returns a string describing what host are we talking to, - //! and how (TCP/IP or UNIX sockets). + //! and how (TCP/IP or UNIX socket).   //!   //! @seealso   //! @[server_info()]   /*semi*/final string host_info() { -  return sprintf("fd:%d TCP/IP %s:%d PID %d", -  c?c->socket->query_fd():-1,_host,_port,backendpid); +  return proxy.host_info();   }      //! Returns true if the connection seems to be open.
276:   //! sent over the connection.   //!   //! For a more reliable check of whether the connection - //! is alive, please use @[ping()]. + //! is alive, please use @[ping()] instead.   //!   //! @seealso   //! @[ping()]   /*semi*/final int is_open() { -  catch { -  return c->socket->is_open(); -  }; -  return 0; +  return proxy.is_open();   }      //! Check whether the connection is alive.
302:   //! @[is_open()]   /*semi*/final int ping() {    waitauthready(); -  return is_open() && !catch(c->start()->sendcmd(FLUSHSEND)) ? 0 : -1; +  return is_open() && !catch(proxy.c->start()->sendcmd(FLUSHSEND)) ? 0 : -1;   }      //! Cancels all currently running queries in this session.
313:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final void cancelquery() { -  PD("CancelRequest\n"); -  .pgsql_util.conxion lcon = .pgsql_util.conxion(this, 0, 2); -  lcon->add_int32(16)->add_int32(PG_PROTOCOL(1234,5678)) -  ->add_int32(backendpid)->add(cancelsecret)->sendcmd(FLUSHSEND); -  destruct(lcon); // Destruct explicitly to avoid delayed close - #ifdef PG_DEBUGMORE -  PD("Closetrace %O\n",backtrace()); - #endif +  proxy.cancelquery();   }      //! Changes the connection charset. When set to @expr{"UTF8"@}, the query,
331:   //!   //! @seealso   //! @[get_charset()], @[create()], - //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=character+sets@} + //! @url{https://www.postgresql.org/docs/current/static/multibyte.html@}   /*semi*/final void set_charset(string charset) {    if(charset) -  big_query(sprintf("SET CLIENT_ENCODING TO '%s'",quote(charset))); +  big_query(sprintf("SET CLIENT_ENCODING TO '%s'", quote(charset)));   }      //! @returns
342:   //!   //! @seealso   //! @[set_charset()], @[getruntimeparameters()], - //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=character+sets@} + //! @url{https://www.postgresql.org/docs/current/static/multibyte.html@}   /*semi*/final string get_charset() { -  waitauthready(); -  return _runtimeparameter[CLIENT_ENCODING]; +  return getruntimeparameters()[CLIENT_ENCODING];   }      //! @returns
382:   //! For other runtimeparameters check the PostgreSQL documentation.   //!   //! @seealso - //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+search_path@} + //! @url{https://www.postgresql.org/docs/current/static/runtime-config-client.html@}   //!   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final mapping(string:string) getruntimeparameters() {    waitauthready(); -  return _runtimeparameter+([]); +  return proxy.runtimeparameter + ([]);   }      //! @returns
428:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final mapping(string:mixed) getstatistics() { -  mapping(string:mixed) stats=([ -  "warnings_dropped":warningsdropcount, -  "current_prepared_statements":sizeof(_prepareds), -  "current_prepared_statement_hits":totalhits, +  mapping(string:mixed) stats = ([ +  "warnings_dropped":proxy.warningsdropcount, +  "current_prepared_statements":sizeof(proxy.prepareds), +  "current_prepared_statement_hits":proxy.totalhits,    "prepared_statement_count":pstmtcount,   #ifdef PG_STATS    "used_prepared_statements":prepstmtused,    "skipped_describe_count":skippeddescribe,    "portals_opened_count":portalsopened,   #endif -  "messages_received":_msgsreceived, -  "bytes_received":_bytesreceived, +  "messages_received":proxy.msgsreceived, +  "bytes_received":proxy.bytesreceived,    ]);    return stats;   }
453:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final int setcachedepth(void|int newdepth) { -  int olddepth=cachedepth; -  if(!undefinedp(newdepth) && newdepth>=0) -  cachedepth=newdepth; +  int olddepth = cachedepth; +  if (!undefinedp(newdepth) && newdepth >= 0) +  cachedepth = newdepth;    return olddepth;   }   
468:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final int settimeout(void|int newtimeout) { -  int oldtimeout=timeout; -  if(!undefinedp(newtimeout) && newtimeout>0) -  timeout=newtimeout; +  int oldtimeout = timeout; +  if (!undefinedp(newtimeout) && newtimeout > 0) +  timeout = newtimeout;    return oldtimeout;   }   
483:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final int setportalbuffersize(void|int newportalbuffersize) { -  int oldportalbuffersize=portalbuffersize; -  if(!undefinedp(newportalbuffersize) && newportalbuffersize>0) -  portalbuffersize=newportalbuffersize; +  int oldportalbuffersize = portalbuffersize; +  if (!undefinedp(newportalbuffersize) && newportalbuffersize>0) +  portalbuffersize = newportalbuffersize;    return oldportalbuffersize;   }   
498:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final int setfetchlimit(void|int newfetchlimit) { -  int oldfetchlimit=_fetchlimit; -  if(!undefinedp(newfetchlimit) && newfetchlimit>=0) -  _fetchlimit=newfetchlimit; +  int oldfetchlimit = proxy._fetchlimit; +  if (!undefinedp(newfetchlimit) && newfetchlimit >= 0) +  proxy._fetchlimit = newfetchlimit;    return oldfetchlimit;   }      private string glob2reg(string glob) { -  if(!glob||!sizeof(glob)) +  if (!glob || !sizeof(glob))    return "%"; -  return replace(glob,({"*","?","\\","%","_"}),({"%","_","\\\\","\\%","\\_"})); +  return replace(glob, ({"*", "?", "\\", "%", "_"}), +  ({"%", "_", "\\\\", "\\%", "\\_"}));   }    - private string a2nls(array(string) msg) { -  return msg*"\n"+"\n"; - } -  - 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; - } -  - private void connect_cb() { -  PD("%O\n",_runtimeparameter); - } -  - private array(string) showbindings(.pgsql_util.sql_result portal) { -  array(string) msgs=({}); -  array from; -  if(portal && (from = portal._params)) { -  array to,paramValues; -  [from,to,paramValues] = from; -  if(sizeof(paramValues)) { -  string val; -  int i; -  string fmt=sprintf("%%%ds %%3s %%.61s",max(@map(from,sizeof))); -  foreach(paramValues;i;val) -  msgs+=({sprintf(fmt,from[i],to[i],sprintf("%O",val))}); -  } -  } -  return msgs; - } -  - private void preplastmessage(mapping(string:string) msgresponse) { -  lastmessage=({ -  sprintf("%s %s:%s %s\n (%s:%s:%s)", -  msgresponse.S,msgresponse.C,msgresponse.P||"", -  msgresponse.M,msgresponse.F||"",msgresponse.R||"", -  msgresponse.L||"")}); - } -  +    private void waitauthready() { -  if(waitforauthready) { +  if (proxy.waitforauthready) {    PD("%d Wait for auth ready %O\n", -  c?->socket&&c->socket->query_fd(),backtrace()[-2]); -  Thread.MutexKey lock=_shortmux->lock(); -  catch(PT(waitforauthready->wait(lock))); -  lock=0; -  PD("%d Wait for auth ready released.\n",c?->socket&&c->socket->query_fd()); +  proxy.c?->socket && proxy.c->socket->query_fd(), backtrace()[-2]); +  Thread.MutexKey lock = proxy.shortmux->lock(); +  catch(PT(proxy.waitforauthready->wait(lock))); +  lock = 0; +  PD("%d Wait for auth ready released.\n", +  proxy.c?->socket && proxy.c->socket->query_fd());    }   }    - private inline mixed callout(function(mixed ...:void) f, -  float|int delay,mixed ... args) { -  return .pgsql_util.local_backend->call_out(f,delay,@args); - } -  - private int|.pgsql_util.sql_result portal; // state information procmessage - #ifdef PG_DEBUG - private string datarowdebug; - private int datarowdebugcount; - #endif -  - final void _processloop(.pgsql_util.conxion ci) { -  (c=ci)->socket->set_id(procmessage); -  cancelsecret=0; -  portal=0; -  { -  Stdio.Buffer plugbuffer=Stdio.Buffer()->add_int32(PG_PROTOCOL(3,0)); -  if(user) -  plugbuffer->add("user\0",user,0); -  if(database) -  plugbuffer->add("database\0",database,0); -  foreach(_options-.pgsql_util.censoroptions; string name; mixed value) -  plugbuffer->add(name,0,(string)value,0); -  plugbuffer->add_int8(0); -  PD("%O\n",(string)plugbuffer); -  object cs; -  if (catch(cs = ci->start())) { -  destruct(waitforauthready); -  unnamedstatement=0; -  termlock=0; -  return; -  } else { -  CHAIN(cs)->add_hstring(plugbuffer, 4, 4); -  cs->sendcmd(SENDOUT); -  } -  } // Do not flush at this point, PostgreSQL 9.4 disapproves -  procmessage(); - } -  - private void procmessage() { -  mixed err; -  int terminating=0; -  err = catch { -  .pgsql_util.conxion ci=c; // cache value FIXME sensible? -  .pgsql_util.conxiin cr=ci->i; // cache value FIXME sensible? - #ifdef PG_DEBUG -  PD("Processloop\n"); -  - #ifdef PG_DEBUGMORE -  void showportalstack(string label) { -  PD(sprintf(">>>>>>>>>>> Portalstack %s: %O\n", label, portal)); -  foreach(qportals->peek_array();;int|.pgsql_util.sql_result qp) -  PD(" =========== Portal: %O\n",qp); -  PD("<<<<<<<<<<<<<< Portalstack end\n"); -  }; - #endif -  void showportal(int msgtype) { -  if(objectp(portal)) -  PD("%d<%O %d %c switch portal\n", -  ci->socket->query_fd(),portal._portalname,++ci->queueinidx,msgtype); -  else if(portal>0) -  PD("%d<Sync %d %d %c portal\n", -  ci->socket->query_fd(),++ci->queueinidx,portal,msgtype); -  }; - #endif -  int msgisfatal(mapping(string:string) msgresponse) { -  if (!terminating) // Run the callback once per lost connection -  runcallback(backendpid,"_lost",""); -  return (has_prefix(msgresponse.C, "53") -  || has_prefix(msgresponse.C, "3D") -  || has_prefix(msgresponse.C, "57P")) && MAGICTERMINATE; -  }; -  for(;;) { -  err=catch { - #ifdef PG_DEBUG -  if(!portal && datarowdebug) { -  PD("%s rows %d\n",datarowdebug,datarowdebugcount); -  datarowdebug=0; datarowdebugcount=0; -  } - #endif - #ifdef PG_DEBUGMORE -  showportalstack("LOOPTOP"); - #endif -  if(!sizeof(cr)) { // Preliminary check, fast path -  Thread.MutexKey lock=cr->fillreadmux->lock(); -  if(!sizeof(cr)) { // Check for real -  if(!cr->fillread) { -  lock=0; -  throw(MAGICTERMINATE); // Force proper termination -  } -  cr->procmsg=1; -  lock=0; -  return; // Terminate thread, wait for callback -  } -  lock=0; -  } -  int msgtype=cr->read_int8(); -  if(!portal) { -  portal=qportals->try_read(); - #ifdef PG_DEBUG -  showportal(msgtype); - #endif -  } -  int msglen=cr->read_int32(); -  _msgsreceived++; -  _bytesreceived+=1+msglen; -  int errtype=NOERROR; -  PD("%d<",ci->socket->query_fd()); -  switch(msgtype) { -  array getcols() { -  int bintext=cr->read_int8(); -  int cols=cr->read_int16(); - #ifdef PG_DEBUG -  array a; -  msglen-=4+1+2+2*cols; -  foreach(a=allocate(cols,([]));;mapping m) -  m.type=cr->read_int16(); - #else -  cr->consume(cols<<1); - #endif // Discard column info, and make it line oriented -  return ({ ({(["name":"line"])}), ({bintext?BYTEAOID:TEXTOID}) }); -  }; -  array(string) reads() { - #ifdef PG_DEBUG -  if(msglen<1) -  errtype=PROTOCOLERROR; - #endif -  array ret=({}),aw=({0}); -  do { -  string w=cr->read_cstring(); -  msglen-=sizeof(w)+1; aw[0]=w; ret+=aw; -  } while(msglen); -  return ret; -  }; -  mapping(string:string) getresponse() { -  mapping(string:string) msgresponse=([]); -  msglen-=4; -  foreach(reads();;string f) -  if(sizeof(f)) -  msgresponse[f[..0]]=f[1..]; -  PD("%O\n",msgresponse); -  return msgresponse; -  }; -  case 'R': { -  void authresponse(string|array msg) { -  object cs = ci->start(); -  CHAIN(cs)->add_int8('p')->add_hstring(msg, 4, 4); -  cs->sendcmd(SENDOUT); // No flushing, PostgreSQL 9.4 disapproves -  }; -  PD("Authentication "); -  msglen-=4+4; -  int authtype, k; -  switch(authtype = cr->read_int32()) { -  case 0: -  PD("Ok\n"); -  if (SASLcontext) { -  PD("Authentication validation still in progress\n"); -  errtype = PROTOCOLUNSUPPORTED; -  } else -  cancelsecret=""; -  break; -  case 2: -  PD("KerberosV5\n"); -  errtype=PROTOCOLUNSUPPORTED; -  break; -  case 3: -  PD("ClearTextPassword\n"); -  authresponse(({pass, 0})); -  break; -  case 4: -  PD("CryptPassword\n"); -  errtype=PROTOCOLUNSUPPORTED; -  break; -  case 5: -  PD("MD5Password\n"); - #ifdef PG_DEBUG -  if(msglen<4) -  errtype=PROTOCOLERROR; - #endif - #define md5hex(x) String.string2hex(Crypto.MD5.hash(x)) -  authresponse(({"md5", -  md5hex(md5hex(pass + user) + cr->read(msglen)), 0})); - #ifdef PG_DEBUG -  msglen=0; - #endif -  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; -  cr->read(msglen); // SSauthdata - #ifdef PG_DEBUG -  if(msglen<1) -  errtype=PROTOCOLERROR; -  msglen=0; - #endif -  break; -  case 10: { -  string word; -  PD("AuthenticationSASL\n"); -  k = 0; -  while (sizeof(word = cr->read_cstring())) { -  switch (word) { -  case "SCRAM-SHA-256": -  k = 1; -  } - #ifdef PG_DEBUG -  msglen -= sizeof(word) + 1; -  if (msglen < 1) -  break; - #endif -  } -  if (k) { -  SASLcontext = Crypto.SCRAM(Crypto.SHA256); -  word = SASLcontext.client_1(); -  authresponse(({ -  "SCRAM-SHA-256", 0, sprintf("%4c", sizeof(word)), word -  })); -  } else -  errtype = PROTOCOLUNSUPPORTED; - #ifdef PG_DEBUG -  if(msglen != 1) -  errtype=PROTOCOLERROR; -  msglen=0; - #endif -  break; -  } -  case 11: { -  PD("AuthenticationSASLContinue\n"); -  string response; -  if (response -  = SASLcontext.client_2(cr->read(msglen), pass)) -  authresponse(response); -  else -  errtype = PROTOCOLERROR; - #ifdef PG_DEBUG -  msglen = 0; - #endif -  break; -  } -  case 12: -  PD("AuthenticationSASLFinal\n"); -  if (SASLcontext.client_3(cr->read(msglen))) -  SASLcontext = 0; // Clears context and approves server -  else -  errtype = PROTOCOLERROR; - #ifdef PG_DEBUG -  msglen=0; - #endif -  break; -  default: -  PD("Unknown Authentication Method %c\n",authtype); -  errtype=PROTOCOLUNSUPPORTED; -  break; -  } -  switch(errtype) { -  default: -  case PROTOCOLUNSUPPORTED: -  ERROR("Unsupported authenticationmethod %c\n",authtype); -  case NOERROR: -  break; -  } -  break; -  } -  case 'K': -  msglen-=4+4;backendpid=cr->read_int32(); -  cancelsecret=cr->read(msglen); - #ifdef PG_DEBUG -  PD("BackendKeyData %O\n",cancelsecret); -  msglen=0; - #endif -  break; -  case 'S': { -  PD("ParameterStatus "); -  msglen-=4; -  array(string) ts=reads(); - #ifdef PG_DEBUG -  if(sizeof(ts)==2) { - #endif -  _runtimeparameter[ts[0]]=ts[1]; - #ifdef PG_DEBUG -  PD("%O=%O\n",ts[0],ts[1]); -  } else -  errtype=PROTOCOLERROR; - #endif -  break; -  } -  case '3': - #ifdef PG_DEBUG -  PD("CloseComplete\n"); -  msglen-=4; - #endif -  break; -  case 'Z': { -  backendstatus=cr->read_int8(); - #ifdef PG_DEBUG -  msglen-=4+1; -  PD("ReadyForQuery %c\n",backendstatus); - #endif - #ifdef PG_DEBUGMORE -  showportalstack("READYFORQUERY"); - #endif -  int keeplooking; -  do -  for(keeplooking = 0; objectp(portal); portal = qportals->read()) { - #ifdef PG_DEBUG -  showportal(msgtype); - #endif -  if (backendstatus == 'I' && _intransaction -  && portal->transtype != TRANSEND) -  keeplooking = 1; -  portal->_purgeportal(); -  } -  while (keeplooking && (portal = qportals->read())); -  if (backendstatus == 'I') -  _intransaction = 0; -  foreach(qportals->peek_array();;.pgsql_util.sql_result qp) { -  if(objectp(qp) && qp._synctransact && qp._synctransact<=portal) { -  PD("Checking portal %O %d<=%d\n", -  qp._portalname,qp._synctransact,portal); -  qp->_purgeportal(); -  } -  } -  portal=0; - #ifdef PG_DEBUGMORE -  showportalstack("AFTER READYFORQUERY"); - #endif -  _readyforquerycount--; -  if(readyforquery_cb) -  readyforquery_cb(),readyforquery_cb=0; -  destruct(waitforauthready); -  break; -  } -  case '1': - #ifdef PG_DEBUG -  PD("ParseComplete portal %O\n", portal); -  msglen-=4; - #endif -  break; -  case 't': { -  array a; - #ifdef PG_DEBUG -  int cols=cr->read_int16(); -  PD("%O ParameterDescription %d values\n",portal._query,cols); -  msglen-=4+2+4*cols; -  a=cr->read_ints(cols,4); - #else -  a=cr->read_ints(cr->read_int16(),4); - #endif - #ifdef PG_DEBUGMORE -  PD("%O\n",a); - #endif -  if(portal._tprepared) -  portal._tprepared.datatypeoid=a; -  Thread.Thread(portal->_preparebind,a); -  break; -  } -  case 'T': { -  array a,at; -  int cols=cr->read_int16(); - #ifdef PG_DEBUG -  PD("RowDescription %d columns %O\n",cols,portal._query); -  msglen-=4+2; - #endif -  at=allocate(cols); -  foreach(a=allocate(cols);int i;) -  { -  string s=cr->read_cstring(); -  mapping(string:mixed) res=(["name":s]); - #ifdef PG_DEBUG -  msglen-=sizeof(s)+1+4+2+4+2+4+2; -  res.tableoid=cr->read_int32()||UNDEFINED; -  res.tablecolattr=cr->read_int16()||UNDEFINED; - #else -  cr->consume(6); - #endif -  at[i]=cr->read_int32(); - #ifdef PG_DEBUG -  res.type=at[i]; -  { -  int len=cr->read_sint(2); -  res.length=len>=0?len:"variable"; -  } -  res.atttypmod=cr->read_int32(); -  /* formatcode contains just a zero when Bind has not been issued -  * yet, but the content is irrelevant because it's determined -  * at query time -  */ -  res.formatcode=cr->read_int16(); - #else -  cr->consume(8); - #endif -  a[i]=res; -  } - #ifdef PG_DEBUGMORE -  PD("%O\n",a); - #endif -  if(portal._forcetext) -  portal->_setrowdesc(a,at); // Do not consume queued portal -  else { -  portal->_processrowdesc(a,at); -  portal=0; -  } -  break; -  } -  case 'n': { - #ifdef PG_DEBUG -  msglen-=4; -  PD("NoData %O\n",portal._query); - #endif -  portal._fetchlimit=0; // disables subsequent Executes -  portal -  ->_processrowdesc(.pgsql_util.emptyarray,.pgsql_util.emptyarray); -  portal=0; -  break; -  } -  case 'H': -  portal->_processrowdesc(@getcols()); -  PD("CopyOutResponse %O\n",portal._query); -  break; -  case '2': { -  mapping tp; - #ifdef PG_DEBUG -  msglen-=4; -  PD("%O BindComplete\n",portal._portalname); - #endif -  if (tp=portal._tprepared) { -  int tend = gethrtime(); -  int tstart = tp.trun; -  if (tend == tstart) -  m_delete(_prepareds,portal._query); -  else { -  tp.hits++; -  totalhits++; -  if (!tp.preparedname) { -  if (sizeof(portal._preparedname)) { -  PD("Finalising stored statement %s\n", portal._preparedname); -  tp.preparedname = portal._preparedname; -  } -  tstart = tend - tstart; -  if (!tp.tparse || tp.tparse>tstart) -  tp.tparse = tstart; -  } -  tp.trunstart = tend; -  } -  } -  break; -  } -  case 'D': -  msglen-=4; - #ifdef PG_DEBUG - #ifdef PG_DEBUGMORE -  PD("%O DataRow %d bytes\n",portal._portalname,msglen); - #endif -  datarowdebugcount++; -  if (!datarowdebug) -  datarowdebug = sprintf( -  "%O DataRow %d bytes",portal._portalname,msglen); - #endif - #ifdef PG_DEBUG -  msglen= - #endif -  portal->_decodedata(msglen,_runtimeparameter[CLIENT_ENCODING]); -  break; -  case 's': - #ifdef PG_DEBUG -  PD("%O PortalSuspended\n",portal._portalname); -  msglen-=4; - #endif -  portal=0; -  break; -  case 'C': { -  msglen-=4; - #ifdef PG_DEBUG -  if(msglen<1) -  errtype=PROTOCOLERROR; - #endif -  string s=cr->read(msglen-1); -  portal->_storetiming(); -  PD("%O CommandComplete %O\n",portal._portalname,s); - #ifdef PG_DEBUG -  if(cr->read_int8()) -  errtype=PROTOCOLERROR; -  msglen=0; - #else -  cr->consume(1); - #endif - #ifdef PG_DEBUGMORE -  showportalstack("COMMANDCOMPLETE"); - #endif -  portal->_releasesession(s); -  portal=0; -  break; -  } -  case 'I': - #ifdef PG_DEBUG -  PD("EmptyQueryResponse %O\n",portal._portalname); -  msglen-=4; - #endif - #ifdef PG_DEBUGMORE -  showportalstack("EMPTYQUERYRESPONSE"); - #endif -  portal->_releasesession(); -  portal=0; -  break; -  case 'd': -  PD("%O CopyData\n",portal._portalname); -  portal->_storetiming(); -  msglen-=4; - #ifdef PG_DEBUG -  if(msglen<0) -  errtype=PROTOCOLERROR; - #endif -  portal->_processdataready(({cr->read(msglen)}),msglen); - #ifdef PG_DEBUG -  msglen=0; - #endif -  break; -  case 'G': -  portal->_releasestatement(); -  portal->_setrowdesc(@getcols()); -  PD("%O CopyInResponse\n",portal._portalname); -  portal._state=COPYINPROGRESS; -  break; -  case 'c': - #ifdef PG_DEBUG -  PD("%O CopyDone\n",portal._portalname); -  msglen-=4; - #endif -  portal=0; -  break; -  case 'E': { - #ifdef PG_DEBUGMORE -  showportalstack("ERRORRESPONSE"); - #endif -  if (_portalsinflight->drained() && !_readyforquerycount) -  sendsync(); -  PD("%O ErrorResponse %O\n", -  objectp(portal)&&(portal._portalname||portal._preparedname), -  objectp(portal)&&portal._query); -  mapping(string:string) msgresponse; -  msgresponse=getresponse(); -  warningsdropcount+=warningscollected; -  warningscollected=0; -  switch(msgresponse.C) { -  case "P0001": -  lastmessage=({sprintf("%s: %s",msgresponse.S,msgresponse.M)}); -  USERERROR(a2nls(lastmessage -  +({pinpointerror(portal._query,msgresponse.P)}) -  +showbindings(portal))); -  case "53000":case "53100":case "53200":case "53300":case "53400": -  case "57P01":case "57P02":case "57P03":case "57P04":case "3D000": -  preplastmessage(msgresponse); -  PD(a2nls(lastmessage)); throw(msgisfatal(msgresponse)); -  case "08P01":case "42P05": -  errtype=PROTOCOLERROR; -  case "XX000":case "42883":case "42P01": -  invalidatecache=1; -  default: -  preplastmessage(msgresponse); -  if(msgresponse.D) -  lastmessage+=({msgresponse.D}); -  if(msgresponse.H) -  lastmessage+=({msgresponse.H}); -  lastmessage+=({ -  pinpointerror(objectp(portal)&&portal._query,msgresponse.P)+ -  pinpointerror(msgresponse.q,msgresponse.p)}); -  if(msgresponse.W) -  lastmessage+=({msgresponse.W}); -  if(objectp(portal)) -  lastmessage+=showbindings(portal); -  switch(msgresponse.S) { -  case "PANIC":werror(a2nls(lastmessage)); -  } -  case "25P02": // Preserve last error message -  USERERROR(a2nls(lastmessage)); // Implicitly closed portal -  } -  break; -  } -  case 'N': { -  PD("NoticeResponse\n"); -  mapping(string:string) msgresponse; -  msgresponse=getresponse(); -  if(clearmessage) { -  warningsdropcount+=warningscollected; -  clearmessage=warningscollected=0; -  lastmessage=({}); -  } -  warningscollected++; -  lastmessage=({sprintf("%s %s: %s", -  msgresponse.S,msgresponse.C,msgresponse.M)}); -  int val; -  if (val = msgisfatal(msgresponse)) { // Some warnings are fatal -  preplastmessage(msgresponse); -  PD(a2nls(lastmessage));throw(val); -  } -  break; -  } -  case 'A': { -  PD("NotificationResponse\n"); -  msglen-=4+4; -  int pid=cr->read_int32(); -  string condition,extrainfo=UNDEFINED; -  { -  array(string) ts=reads(); -  switch(sizeof(ts)) { - #if PG_DEBUG -  case 0: -  errtype=PROTOCOLERROR; -  break; -  default: -  errtype=PROTOCOLERROR; - #endif -  case 2: -  extrainfo=ts[1]; -  case 1: -  condition=ts[0]; -  } -  } -  PD("%d %s\n%s\n",pid,condition,extrainfo); -  runcallback(pid,condition,extrainfo); -  break; -  } -  default: -  if(msgtype!=-1) { -  string s; -  PD("Unknown message received %c\n",msgtype); -  s=cr->read(msglen-=4);PD("%O\n",s); - #ifdef PG_DEBUG -  msglen=0; - #endif -  errtype=PROTOCOLUNSUPPORTED; -  } else { -  lastmessage+=({ -  sprintf("Connection lost to database %s@%s:%d/%s %d\n", -  user,_host,_port,database,backendpid)}); -  runcallback(backendpid, "_lost", ""); -  if(!waitforauthready) -  throw(0); -  USERERROR(a2nls(lastmessage)); -  } -  break; -  } - #ifdef PG_DEBUG -  if(msglen) -  errtype=PROTOCOLERROR; - #endif -  { -  string msg; -  switch(errtype) { -  case PROTOCOLUNSUPPORTED: -  msg=sprintf("Unsupported servermessage received %c\n",msgtype); -  break; -  case PROTOCOLERROR: -  msg=sprintf("Protocol error with database %s",host_info()); -  break; -  case NOERROR: -  continue; // Normal production loop -  } -  ERROR(a2nls(lastmessage+=({msg}))); -  } -  }; // We only get here if there is an error -  if(err==MAGICTERMINATE) { // Announce connection termination to server -  catch { -  object cs = ci->start(); -  CHAIN(cs)->add("X\0\0\0\4"); -  cs->sendcmd(SENDOUT); -  }; -  terminating=1; -  err=0; -  } else if(stringp(err)) { -  .pgsql_util.sql_result or; -  if(!objectp(or=portal)) -  or=this; -  if(!or._delayederror) -  or._delayederror=err; - #ifdef PG_DEBUGMORE -  showportalstack("THROWN"); - #endif -  if(objectp(portal)) -  portal->_releasesession(); -  portal=0; -  if(!waitforauthready) -  continue; // Only continue if authentication did not fail -  } -  break; -  } -  PD("Closing database processloop %s\n", err ? describe_backtrace(err) : ""); -  _delayederror=err; -  if (objectp(portal)) { - #ifdef PG_DEBUG -  showportal(0); - #endif -  portal->_purgeportal(); -  } -  destruct(waitforauthready); -  termlock=0; -  if(err && !stringp(err)) -  throw(err); -  }; -  catch { -  unnamedstatement = 0; -  termlock = 0; -  }; -  if (err) { -  PD("Terminating processloop due to %s\n", describe_backtrace(err)); -  _delayederror = err; -  } -  destruct(waitforauthready); -  destruct(c); - } -  +    //! Closes the connection to the database, any running queries are   //! terminated instantly.   //!   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final void close() { -  throwdelayederror(this); -  Thread.MutexKey lock; -  if (qportals && qportals->size()) -  catch(cancelquery()); -  if (unnamedstatement) -  termlock = unnamedstatement->lock(1); -  if (c) // Prevent trivial backtraces -  c->close(); -  if (unnamedstatement) -  lock = unnamedstatement->lock(1); -  if (c) -  destruct(c); -  lock = 0; -  destruct(waitforauthready); +  proxy.close();   }      protected void destroy() { -  string errstring; -  mixed err = catch(close()); -  backendreg = 0; -  /* -  * Flush out any asynchronously reported errors to stderr; because we are -  * inside a destructor, throwing an error will not work anymore. -  * Warnings will be silently discarded at this point. -  */ -  lastmessage = filter(lastmessage, lambda(string val) { -  return has_prefix(val, "ERROR ") || has_prefix(val, "FATAL "); }); -  if (err || (err = catch(errstring = error(1)))) -  werror(describe_backtrace(err)); -  else if (errstring && sizeof(errstring)) -  werror("%s\n", errstring); // Add missing terminating newline +  destruct(proxy);   }      //! For PostgreSQL this function performs the same function as @[resync()].
1341:   }      private void resync_cb() { -  switch(backendstatus) { +  switch (proxy.backendstatus) {    case 'T':case 'E': -  foreach(_prepareds;;mapping tp) { +  foreach (proxy.prepareds; ; mapping tp) {    m_delete(tp,"datatypeoid");    m_delete(tp,"datarowdesc");    m_delete(tp,"datarowtypes");
1352:    }   }    - private void sendsync() { -  _readyforquerycount++; -  c->start()->sendcmd(SYNCSEND); - } -  +    //! Resyncs the database session; typically used to make sure the session is   //! not still in a dangling transaction.   //!
1378:   //! This function is PostgreSQL-specific.   /*semi*/final void resync() {    mixed err; -  if(is_open()) { +  if (is_open()) {    err = catch {    PD("Statementsinflight: %d Portalsinflight: %d\n", -  _statementsinflight, _portalsinflight); -  if(!waitforauthready) { -  readyforquery_cb=resync_cb; -  sendsync(); +  proxy.statementsinflight, proxy.portalsinflight); +  if(!proxy.waitforauthready) { +  proxy.readyforquery_cb = resync_cb; +  proxy.sendsync();    }    return;    }; -  PD("%O\n",err); +  PD("%O\n", err);    } -  if(sizeof(lastmessage)) -  ERROR(a2nls(lastmessage)); +  if (sizeof(proxy.lastmessage)) +  ERROR(proxy.a2nls(proxy.lastmessage));   }      //! Due to restrictions of the Postgres frontend-backend protocol, you always
1403:   //! @seealso   //! @[create()]   /*semi*/final void select_db(string dbname) { -  if (database != dbname) +  if (proxy.database != dbname)    ERROR("Cannot switch databases from %O to %O"    " in an already established connection\n", -  database, dbname); +  proxy.database, dbname);   }      //! With PostgreSQL you can LISTEN to NOTIFY events.
1443:   //! @note   //! This function is PostgreSQL-specific.   /*semi*/final void set_notify_callback(string condition, -  void|function(int,string,string,mixed ...:void) notify_cb,void|int selfnotify, +  void|function(int,string,string,mixed ...:void) notify_cb, void|int selfnotify,    mixed ... args) { -  if(!notify_cb) -  m_delete(notifylist,condition); +  if (!notify_cb) +  m_delete(proxy.notifylist, condition);    else { -  array old=notifylist[condition]; -  if(!old) -  old=({notify_cb}); -  if(selfnotify||args) -  old+=({selfnotify}); -  if(args) -  old+=args; -  notifylist[condition]=old; +  array old = proxy.notifylist[condition]; +  if (!old) +  old = ({notify_cb}); +  if (selfnotify || args) +  old += ({selfnotify}); +  if (args) +  old += args; +  proxy.notifylist[condition] = old;    }   }    - private void runcallback(int pid,string condition,string extrainfo) { -  array cb; -  if((cb=notifylist[condition]||notifylist[""]) -  && (pid!=backendpid || sizeof(cb)>1 && cb[1])) -  callout(cb[0],0,pid,condition,extrainfo,@cb[2..]); - } -  +    //! @returns   //! The given string, but escapes/quotes all contained magic characters   //! according to the quoting rules of the current session for non-binary
1478:   //! @[big_query()], @[quotebinary()], @[create()]   /*semi*/final string quote(string s) {    waitauthready(); -  string r=_runtimeparameter.standard_conforming_strings; -  if(r && r=="on") +  string r = proxy.runtimeparameter.standard_conforming_strings; +  if (r && r == "on")    return replace(s, "'", "''");    return replace(s, ({ "'", "\\" }), ({ "''", "\\\\" }) );   }
1509:   //! @seealso   //! @[drop_db()]   /*semi*/final void create_db(string db) { -  big_query(sprintf("CREATE DATABASE %s",db)); +  big_query(sprintf("CREATE DATABASE %s", db));   }      //! This function destroys a database and all the data it contains (assuming
1523:   //! @seealso   //! @[create_db()]   /*semi*/final void drop_db(string db) { -  big_query(sprintf("DROP DATABASE %s",db)); +  big_query(sprintf("DROP DATABASE %s", db));   }      //! @returns
1536:   //! @[host_info()]   /*semi*/final string server_info () {    waitauthready(); -  return DRIVERNAME"/"+(_runtimeparameter.server_version||"unknown"); +  return DRIVERNAME"/" + (proxy.runtimeparameter.server_version || "unknown");   }      //! @returns
1545:   //! @param glob   //! If specified, list only those databases matching it.   /*semi*/final array(string) list_dbs (void|string glob) { -  array row,ret=({}); +  array row, ret = .pgsql_util.emptyarray;    .pgsql_util.sql_result 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]}); +  while(row = res->fetch_row()) +  ret += ({row[0]});    return ret;   }   
1563:   //! @param glob   //! If specified, list only the tables with matching names.   /*semi*/final array(string) list_tables (void|string glob) { -  array row,ret=({}); // This query might not work on PostgreSQL 7.4 -  .pgsql_util.sql_result res=big_query( // due to missing schemasupport +  array row, ret = .pgsql_util.emptyarray; +  .pgsql_util.sql_result res = big_query( // due to missing schemasupport +  // This query might not work on PostgreSQL 7.4    "SELECT CASE WHEN 'public'=n.nspname THEN '' ELSE n.nspname||'.' END "    " ||c.relname AS name "    "FROM pg_catalog.pg_class c "
1574:    " AND c.relname ILIKE :glob "    " ORDER BY 1",    ([":glob":glob2reg(glob)])); -  while(row=res->fetch_row()) -  ret+=({row[0]}); +  while(row = res->fetch_row()) +  ret += ({row[0]});    return ret;   }   
1621:   //! Setting it to @expr{*@} will include system columns in the list.   /*semi*/final array(mapping(string:mixed)) list_fields(void|string table,    void|string glob) { -  array row, ret=({}); -  string schema=UNDEFINED; +  array row, ret = .pgsql_util.emptyarray; +  string schema;       sscanf(table||"*", "%s.%s", schema, table);   
1672:       array colnames=res->fetch_fields();    { -  mapping(string:string) renames=([ +  mapping(string:string) renames = ([    "attname":"name",    "nspname":"schema",    "relname":"table",
1687:    "relhaspkey":"has_primarykey",    "reltuples":"rowcount",    ]); -  foreach(colnames;int i;mapping m) { -  string nf,field=m.name; +  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) + #define delifzero(m, field) if(!(m)[field]) m_delete(m, field)       while(row=res->fetch_row()) { -  mapping m=mkmapping(colnames,row); +  mapping m=mkmapping(colnames, row);    delifzero(m,"is_shared");    delifzero(m,"has_index");    delifzero(m,"has_primarykey");    delifzero(m,"default"); -  ret+=({m}); +  ret += ({m});    }    return ret;   }
1728:   //! @note   //! This function is PostgreSQL-specific.   final string status_commit() { -  return trbackendst(backendstatus); +  return trbackendst(proxy.backendstatus);   }      private inline void closestatement( -  .pgsql_util.bufcon|.pgsql_util.conxsess plugbuffer,string oldprep) { -  .pgsql_util.closestatement(plugbuffer,oldprep); +  .pgsql_util.bufcon|.pgsql_util.conxsess plugbuffer, string oldprep) { +  .pgsql_util.closestatement(plugbuffer, oldprep);   }      private inline string int2hex(int i) {
1744:    .pgsql_util.throwdelayederror(parent);   }    - //! This is the only provided interface which allows you to query the - //! database. If you wish to use the simpler @[Sql.Sql()->query()] function, - //! you need to use the @[Sql.Sql] generic SQL-object. + //! This is the only provided direct interface which allows you to query the + //! database. A simpler synchronous interface can be used through @[query()].   //!   //! Bindings are supported natively straight across the network.   //! Special bindings supported are:
1778:   //! @returns   //! A @[Sql.pgsql_util.sql_result] object (which conforms to the   //! @[Sql.sql_result] standard interface for accessing data). It is - //! recommended to use @[Sql.Sql()->query()] for simpler queries (because + //! recommended to use @[query()] for simpler queries (because   //! it is easier to handle, but stores all the result in memory), and - //! @[Sql.Sql()->big_query()] for queries you expect to return huge amounts of + //! @[big_query()] for queries you expect to return huge amounts of   //! data (it's harder to handle, but fetches results on demand).   //!   //! @note
1798:   //! simply ignores any commands after the first unquoted semicolon. This can   //! be viewed as a limited protection against SQL-injection attacks.   //! To make it support multiple queries in one querystring, use the - //! @ref{:_text@} option. + //! @ref{:_text@} option (not recommended).   //!   //! @seealso   //! @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result], - //! @[Sql.Sql()->query()], @[Sql.pgsql_util.sql_result] + //! @[query()], @[Sql.pgsql_util.sql_result]   /*semi*/final .pgsql_util.sql_result big_query(string q,    void|mapping(string|int:mixed) bindings,    void|int _alltyped) {    throwdelayederror(this); -  string preparedname=""; -  int forcecache=-1, forcetext=_options.text_query; -  int syncparse=zero_type(_options.sync_parse)?-1:_options.sync_parse; -  if(waitforauthready) +  string preparedname = ""; +  mapping(string:mixed) options = proxy.options; +  .pgsql_util.conxion c = proxy.c; +  int forcecache = -1, forcetext = options.text_query; +  int syncparse = zero_type(options.sync_parse) +  ? -1 : options.sync_parse; +  if (proxy.waitforauthready)    waitauthready(); -  string cenc=_runtimeparameter[CLIENT_ENCODING]; +  string cenc = proxy.runtimeparameter[CLIENT_ENCODING];    switch(cenc) {    case UTF8CHARSET: -  q=string_to_utf8(q); +  q = string_to_utf8(q);    break;    default: -  if(String.width(q)>8) -  ERROR("Don't know how to convert %O to %s encoding\n",q,cenc); +  if (String.width(q) > 8) +  ERROR("Don't know how to convert %O to %s encoding\n", q, cenc);    }    array(string|int) paramValues;    array from; -  if(bindings) { -  if(forcetext) -  q = .sql_util.emulate_bindings(q, bindings, this), paramValues=({}); +  if (bindings) { +  if (forcetext) +  q = .sql_util.emulate_bindings(q, bindings, this), +  paramValues = .pgsql_util.emptyarray;    else { -  int pi=0,rep=0; -  paramValues=allocate(sizeof(bindings)); -  from=allocate(sizeof(bindings)); -  array(string) to=allocate(sizeof(bindings)); -  foreach(bindings; mixed name; mixed value) { -  if(stringp(name)) { // Throws if mapping key is empty string -  if(name[0]!=':') -  name=":"+name; -  if(name[1]=='_') { // Special option parameter +  int pi = 0, rep = 0; +  paramValues = allocate(sizeof(bindings)); +  from = allocate(sizeof(bindings)); +  array(string) to = allocate(sizeof(bindings)); +  foreach (bindings; mixed name; mixed value) { +  if (stringp(name)) { // Throws if mapping key is empty string +  if (name[0] != ':') +  name = ":" + name; +  if (name[1] == '_') { // Special option parameter    switch(name) {    case ":_cache": -  forcecache=(int)value; +  forcecache = (int)value;    break;    case ":_text": -  forcetext=(int)value; +  forcetext = (int)value;    break;    case ":_sync": -  syncparse=(int)value; +  syncparse = (int)value;    break;    }    continue;    } -  if(!has_value(q,name)) +  if (!has_value(q, name))    continue;    } -  from[rep]=name; +  from[rep] = name;    string rval; -  if(multisetp(value)) // multisets are taken literally -  rval=indices(value)*","; // and bypass the encoding logic +  if (multisetp(value)) // multisets are taken literally +  rval = indices(value)*","; // and bypass the encoding logic    else { -  paramValues[pi++]=value; -  rval=sprintf("$%d",pi); +  paramValues[pi++] = value; +  rval = sprintf("$%d", pi);    } -  to[rep++]=rval; +  to[rep++] = rval;    }    if(rep--) -  q=replace(q,from=from[..rep],to=to[..rep]); -  paramValues= pi ? paramValues[..pi-1] : ({}); -  from=({from,to,paramValues}); +  q = replace(q, from = from[..rep], to = to[..rep]); +  paramValues = pi ? paramValues[..pi-1] : .pgsql_util.emptyarray; +  from = ({from, to, paramValues});    }    } else -  paramValues=({}); -  if(String.width(q)>8) -  ERROR("Wide string literals in %O not supported\n",q); -  if(has_value(q,"\0")) -  ERROR("Querystring %O contains invalid literal nul-characters\n",q); +  paramValues = .pgsql_util.emptyarray; +  if (String.width(q) > 8) +  ERROR("Wide string literals in %O not supported\n", q); +  if (has_value(q, "\0")) +  ERROR("Querystring %O contains invalid literal nul-characters\n", q);    mapping(string:mixed) tp;    int tstart;    /*
1888:    else if (!forcetext && forcecache == 1    || forcecache && sizeof(q) >= MINPREPARELENGTH) {    object plugbuffer = c->start(); -  if(tp=_prepareds[q]) { -  if(tp.preparedname) { +  if (tp = proxy.prepareds[q]) { +  if (tp.preparedname) {   #ifdef PG_STATS    prepstmtused++;   #endif -  preparedname=tp.preparedname; -  } else if((tstart=tp.trun) -  && tp.tparse*FACTORPLAN>=tstart -  && (undefinedp(_options.cache_autoprepared_statements) -  || _options.cache_autoprepared_statements)) -  preparedname=PREPSTMTPREFIX+int2hex(pstmtcount++); +  preparedname = tp.preparedname; +  } else if((tstart = tp.trun) +  && tp.tparse*FACTORPLAN >= tstart +  && (undefinedp(options.cache_autoprepared_statements) +  || options.cache_autoprepared_statements)) +  preparedname = PREPSTMTPREFIX + int2hex(pstmtcount++);    } else { -  if(totalhits>=cachedepth) -  foreach(_prepareds;string ind;tp) { -  int oldhits=tp.hits; -  totalhits-=oldhits-(tp.hits=oldhits>>1); -  if(oldhits<=1) { -  closestatement(plugbuffer,tp.preparedname); -  m_delete(_prepareds,ind); +  if (proxy.totalhits >= cachedepth) +  foreach (proxy.prepareds; string ind; tp) { +  int oldhits = tp.hits; +  proxy.totalhits -= oldhits-(tp.hits = oldhits >> 1); +  if (oldhits <= 1) { +  closestatement(plugbuffer, tp.preparedname); +  m_delete(proxy.prepareds, ind);    }    } -  if(forcecache!=1 && .pgsql_util.createprefix->match(q)) { +  if (forcecache != 1 && .pgsql_util.createprefix->match(q)) {    PD("Invalidate cache\n"); -  invalidatecache=1; // Flush cache on CREATE +  proxy.invalidatecache = 1; // Flush cache on CREATE    tp = 0;    } else -  _prepareds[q]=tp=([]); +  proxy.prepareds[q] = tp = ([]);    } -  if(invalidatecache) { -  invalidatecache=0; -  foreach(_prepareds;;mapping np) { -  closestatement(plugbuffer,np.preparedname); -  m_delete(np,"preparedname"); +  if (proxy.invalidatecache) { +  proxy.invalidatecache = 0; +  foreach (proxy.prepareds; ; mapping np) { +  closestatement(plugbuffer, np.preparedname); +  m_delete(np, "preparedname");    }    } -  if(sizeof(CHAIN(plugbuffer))) { +  if (sizeof(CHAIN(plugbuffer))) {    PD("%O\n",(string)CHAIN(plugbuffer));    plugbuffer->sendcmd(FLUSHSEND); // close expireds    } else    plugbuffer->sendcmd(KEEP); // close start() -  tstart=gethrtime(); +  tstart = gethrtime();    } else // sql_result autoassigns to portal    tp = 0;    .pgsql_util.sql_result portal; -  portal=.pgsql_util.sql_result(this,c,q, portalbuffersize, _alltyped, from, -  forcetext, timeout, syncparse, transtype); -  portal._tprepared=tp; +  portal = .pgsql_util.sql_result(proxy, c, q, portalbuffersize, _alltyped, +  from, forcetext, timeout, syncparse, transtype); +  portal._tprepared = tp;   #ifdef PG_STATS    portalsopened++;   #endif -  clearmessage=1; -  if(forcetext) { // FIXME What happens if portals are still open? -  portal._unnamedportalkey=_unnamedportalmux->lock(1); -  portal._portalname=""; +  proxy.clearmessage = 1; +  if (forcetext) { // FIXME What happens if portals are still open? +  portal._unnamedportalkey = proxy.unnamedportalmux->lock(1); +  portal._portalname = "";    portal->_parseportal(); portal->_bindportal(); -  _readyforquerycount++; -  Thread.MutexKey lock=unnamedstatement->lock(1); +  proxy.readyforquerycount++; +  Thread.MutexKey lock = proxy.unnamedstatement->lock(1);    .pgsql_util.conxsess cs = c->start(1);    CHAIN(cs)->add_int8('Q')->add_hstring(({q, 0}), 4, 4); -  cs->sendcmd(FLUSHLOGSEND,portal); -  lock=0; -  PD("Simple query: %O\n",q); +  cs->sendcmd(FLUSHLOGSEND, portal); +  lock = 0; +  PD("Simple query: %O\n", q);    } else {    object plugbuffer;    portal->_parseportal(); -  if(!sizeof(preparedname) || !tp || !tp.preparedname) { -  if(!sizeof(preparedname)) +  if (!sizeof(preparedname) || !tp || !tp.preparedname) { +  if (!sizeof(preparedname))    preparedname= -  (portal._unnamedstatementkey = unnamedstatement->trylock(1)) +  (portal._unnamedstatementkey = proxy.unnamedstatement->trylock(1))    ? "" : PTSTMTPREFIX+int2hex(ptstmtcount++); -  PD("Parse statement %O=%O\n",preparedname,q); +  PD("Parse statement %O=%O\n", preparedname, q);    plugbuffer = c->start();    CHAIN(plugbuffer)->add_int8('P') -  ->add_hstring(({preparedname,0,q,"\0\0\0"}),4,4) +  ->add_hstring(({preparedname, 0, q, "\0\0\0"}), 4, 4)   #if 0    // Even though the protocol doesn't require the Parse command to be    // followed by a flush, it makes a VERY noticeable difference in
1975:    preparedname = tp.preparedname; // to shortcut a potential race    PD("Using prepared statement %s for %O\n", preparedname, q);    } -  portal._preparedname=preparedname; -  if(!tp || !tp.datatypeoid) { -  PD("Describe statement %O\n",preparedname); +  portal._preparedname = preparedname; +  if (!tp || !tp.datatypeoid) { +  PD("Describe statement %O\n", preparedname);    if (!plugbuffer)    plugbuffer = c->start();    CHAIN(plugbuffer)->add_int8('D')    ->add_hstring(({'S', preparedname, 0}), 4, 4); -  plugbuffer->sendcmd(FLUSHSEND,portal); +  plugbuffer->sendcmd(FLUSHSEND, portal);    } else { -  if(plugbuffer) +  if (plugbuffer)    plugbuffer->sendcmd(KEEP);   #ifdef PG_STATS    skippeddescribe++;   #endif -  portal->_setrowdesc(tp.datarowdesc,tp.datarowtypes); +  portal->_setrowdesc(tp.datarowdesc, tp.datarowtypes);    } -  if((portal._tprepared=tp) && tp.datatypeoid) { -  mixed e=catch(portal->_preparebind(tp.datatypeoid)); -  if (e && !portal._delayederror) { +  if ((portal._tprepared=tp) && tp.datatypeoid) { +  mixed e = catch(portal->_preparebind(tp.datatypeoid)); +  if (e && !portal.delayederror) {    portal._unnamedstatementkey = 0; // Release early, release often    throw(e);    }    } -  if (!unnamedstatement) +  if (!proxy.unnamedstatement)    portal._unnamedstatementkey = 0; // Cover for a destruct race    }    throwdelayederror(portal);
2012:   //! @[big_query()], @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result]   /*semi*/final inline .pgsql_util.sql_result streaming_query(string q,    void|mapping(string|int:mixed) bindings) { -  return big_query(q,bindings); +  return big_query(q, bindings);   }      //! This function returns an object that allows streaming and typed
2022:   //! @[big_query()], @[Sql.Sql], @[Sql.sql_result]   /*semi*/final inline .pgsql_util.sql_result big_typed_query(string q,    void|mapping(string|int:mixed) bindings) { -  return big_query(q,bindings,1); +  return big_query(q, bindings, 1);   }