pike.git / lib / modules / Sql.pmod / pgsql.pike

version» Context lines:

pike.git/lib/modules/Sql.pmod/pgsql.pike:96:   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;   private int invalidatecache;   private int connectionclosed;      private string host, database, user, pass;   private int port; + private multiset cachealways=(<"BEGIN","begin","END","end","COMMIT","commit">);   private object createprefix    =Regexp("^[ \t\f\r\n]*[Cc][Rr][Ee][Aa][Tt][Ee][ \t\f\r\n]");   private object dontcacheprefix    =Regexp("^[ \t\f\r\n]*([Ff][Ee][Tt][Cc][Hh]|[Cc][Oo][Pp][Yy])[ \t\f\r\n]"); - private object limitpostfix -  =Regexp("[ \t\f\r\n][Ll][Ii][Mm][Ii][Tt][ \t\f\r\n]+[12][; \t\f\r\n]*$"); + private object execfetchlimit +  =Regexp("^[ \t\f\r\n]*(([Uu][Pp][Dd][Aa]|[Dd][Ee][Ll][Ee])[Tt][Ee]|\ + [Ii][Nn][Ss][Ee][Rr][Tt])[ \t\f\r\n]|\ + [ \t\f\r\n][Ll][Ii][Mm][Ii][Tt][ \t\f\r\n]+[12][; \t\f\r\n]*$");   Thread.Mutex _querymutex;   Thread.Mutex _stealmutex;      #define USERERROR(msg) throw(({(msg), backtrace()[..<1]}))      protected string _sprintf(int type, void|mapping flags)   { string res=UNDEFINED;    switch(type)    { case 'O':    res=sprintf(DRIVERNAME"(%s@%s:%d/%s,%d)",
pike.git/lib/modules/Sql.pmod/pgsql.pike:169:   //!   //! @param database   //! Specifies the database to connect to. Not specifying this is   //! only supported if the PostgreSQL backend has a default database   //! configured. If you do not want to connect to any live database,   //! you can use @expr{"template1"@}.   //!   //! @param options   //! Currently supports at least the following:   //! @mapping + //! @member int "reconnect" + //! Set it to zero to disable automatic reconnects upon losing + //! the connection to the database. Not setting it, or setting + //! it to one, will cause one timed reconnect to take place. + //! Setting it to -1 will cause the system to try and reconnect + //! indefinitely.   //! @member int "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 + //! to plain unencrypted.   //! @member int "force_ssl"   //! If the database supports and allows SSL connections, the session - //! will be SSL encrypted, if not, the connection will abort + //! will be SSL encrypted, if not, the connection will abort. + //! @member int "text_query" + //! 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.   //! @member int "cache_autoprepared_statements"   //! If set to zero, it disables the automatic statement prepare and   //! cache logic; caching prepared statements can be problematic   //! when stored procedures and tables are redefined which leave stale - //! references in the already cached prepared statements + //! references in the already cached prepared statements.   //! @member string "client_encoding"   //! Character encoding for the client side, it defaults to using   //! the default encoding specified by the database, e.g.   //! @expr{"UTF8"@} or @expr{"SQL_ASCII"@}.   //! @member string "standard_conforming_strings"   //! When on, backslashes in strings must not be escaped any longer, - //! @[quote()] automatically adjusts quoting strategy accordingly + //! @[quote()] automatically adjusts quoting strategy accordingly.   //! @member string "escape_string_warning"   //! When on, a warning is issued if a backslash (\) appears in an   //! ordinary string literal and @expr{"standard_conforming_strings"@}   //! is off, defaults to on.   //! @endmapping   //! For the numerous other options please check the PostgreSQL manual.   //!   //! @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], @[select_db()], - //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=client+connection+defaults@} + //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+defaults@}   protected void create(void|string _host, void|string _database,    void|string _user, void|string _pass, void|mapping(string:mixed) _options)   { pass = _pass; _pass = "CENSORED";    if(pass)    String.secure(pass);    user = _user; database = _database; host = _host || PGSQL_DEFAULT_HOST;    options = _options || ([]);    if(has_value(host,":") && sscanf(_host,"%s:%d",host,port)!=2)    ERROR("Error in parsing the hostname argument\n");    if(!port)    port = PGSQL_DEFAULT_PORT; -  +  _runtimeparameter=([]);    _querymutex=Thread.Mutex();    _stealmutex=Thread.Mutex();    reconnect();   }      //! @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).
pike.git/lib/modules/Sql.pmod/pgsql.pike:258:   //! This function returns a string describing what host are we talking to,   //! and how (TCP/IP or UNIX sockets).   //!   //! @seealso   //! @[server_info()]   string host_info()   { return sprintf("fd:%d TCP/IP %s:%d PID %d",    _c?_c.query_fd():-1,host,port,backendpid);   }    + //! Returns true if the connection seems to be open. + //! + //! @note + //! This function only checks that there's an open connection, + //! and that the other end hasn't closed it yet. No data is + //! sent over the connection. + //! + //! For a more reliable check of whether the connection + //! is alive, please use @[ping()]. + //! + //! @seealso + //! @[ping()] + int is_open() + { return _c&&_c.query_fd()>=0; + } +  + //! @decl int ping() + //! + //! Check whether the connection is alive. + //! + //! @returns + //! Returns one of the following: + //! @int + //! @value 0 + //! Everything ok. + //! @value 1 + //! The connection reconnected automatically. + //! @value -1 + //! The server has gone away, and the connection is dead. + //! @endint + //! + //! @seealso + //! @[is_open()] + int ping() + { int oldbackendpid=backendpid; +  mixed err; +  if(_c && (err = catch +  { _c.sendflush(); +  return backendpid!=oldbackendpid; +  })) +  { PD("%O\n",err); +  if(reconnect(1)) +  return 1; +  } +  return -1; + } +    final private object getsocket(void|int nossl)   { object lcon = Stdio.File();    if(!lcon.connect(host,port))    return UNDEFINED;       object fcon;   #if constant(SSL.sslfile)    if(!nossl && (options->use_ssl || options->force_ssl))    { PD("SSLRequest\n");    { object c=.pgsql_util.PGassist();
pike.git/lib/modules/Sql.pmod/pgsql.pike:324:   }      //! Changes the connection charset. When set to @expr{"UTF8"@}, the query,   //! parameters and results can be Pike-native wide strings.   //!   //! @param charset   //! A PostgreSQL charset name.   //!   //! @seealso   //! @[get_charset()], @[create()], - //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=character+sets@} + //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=character+sets@}   void set_charset(string charset)   { big_query(sprintf("SET CLIENT_ENCODING TO '%s'",quote(charset)));   }      //! @returns   //! The PostgreSQL name for the current connection charset.   //!   //! @seealso   //! @[set_charset()], @[getruntimeparameters()], - //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=character+sets@} + //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=character+sets@}   string get_charset()   { return _runtimeparameter[CLIENT_ENCODING];   }      //! @returns   //! Currently active runtimeparameters for   //! the open session; these are initialised by the @tt{options@} parameter   //! during session creation, and then processed and returned by the server.   //! Common values are:   //! @mapping   //! @member string "client_encoding"   //! Character encoding for the client side, e.g.   //! @expr{"UTF8"@} or @expr{"SQL_ASCII"@}.   //! @member string "server_encoding"   //! Character encoding for the server side as determined when the - //! database was created, e.g. @expr{"UTF8"@} or @expr{"SQL_ASCII"@} + //! database was created, e.g. @expr{"UTF8"@} or @expr{"SQL_ASCII"@}.   //! @member string "DateStyle" - //! Date parsing/display, e.g. @expr{"ISO, DMY"@} + //! Date parsing/display, e.g. @expr{"ISO, DMY"@}.   //! @member string "TimeZone" - //! Default timezone used by the database, e.g. @expr{"localtime"@} + //! Default timezone used by the database, e.g. @expr{"localtime"@}.   //! @member string "standard_conforming_strings" - //! When on, backslashes in strings must not be escaped any longer + //! When on, backslashes in strings must not be escaped any longer.   //! @member string "session_authorization" - //! Displays the authorisationrole which the current session runs under + //! Displays the authorisationrole which the current session runs under.   //! @member string "is_superuser"   //! Indicates if the current authorisationrole has database-superuser - //! privileges + //! privileges.   //! @member string "integer_datetimes" - //! Reports wether the database supports 64-bit-integer dates and times + //! Reports wether the database supports 64-bit-integer dates and times.   //! @member string "server_version" - //! Shows the server version, e.g. @expr{"8.3.3"@} + //! Shows the server version, e.g. @expr{"8.3.3"@}.   //! @endmapping   //!   //! The values can be changed during a session using SET commands to the   //! database.   //! For other runtimeparameters check the PostgreSQL documentation.   //!   //! @seealso - //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=client+connection+defaults@} + //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+defaults@}   //!   //! @note   //! This function is PostgreSQL-specific, and thus it is not available   //! through the generic SQL-interface.   mapping(string:string) getruntimeparameters()   { return _runtimeparameter+([]);   }      //! @returns   //! A set of statistics for the current session:
pike.git/lib/modules/Sql.pmod/pgsql.pike:533:   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;   }      private void phasedreconnect() + { if(!connectionclosed)    { connectionclosed=1;    if(!reconnect(1))    { sleep(RECONNECTDELAY);    if(!reconnect(1)) -  { sleep(RECONNECTBACKOFF); -  reconnect(1); +  { do sleep(RECONNECTBACKOFF); +  while(!reconnect(1) && options->reconnect==-1);    }    }    } -  + }      final int _decodemsg(void|state waitforstate)   {   #ifdef DEBUG    { array line;   #ifdef DEBUGMORE    line=backtrace();   #endif    PD("Waiting for state %O %O\n",waitforstate,line&&line[sizeof(line)-2]);    }
pike.git/lib/modules/Sql.pmod/pgsql.pike:750:    { string s;    msglen-=sizeof(s=_c.getstring())+1;    mapping(string:mixed) res=(["name":s]);    msglen-=4+2+4+2+4+2;    res->tableoid=_c.getint32()||UNDEFINED;    res->tablecolattr=_c.getint16()||UNDEFINED;    res->type=_c.getint32();    { int len=_c.getint16();    res->length=len>=0?len:"variable";    } -  res->atttypmod=_c.getint32();res->formatcode=_c.getint16(); +  res->atttypmod=_c.getint32(); +  res->formatcode=_c.getint16(); // Currently broken in Postgres    a[i]=res;    }   #ifdef DEBUGMORE    PD("%O\n",a);   #endif    if(_c.portal)    _c.portal->_datarowdesc=a;    _mstate=gotrowdescription;    break;    }
pike.git/lib/modules/Sql.pmod/pgsql.pike:784:    { if(tprepared)    storetiming();   #ifdef USEPGsql    _c.decodedatarow(msglen);msglen=0;   #else    array a, datarowdesc;    _c.portal->_bytesreceived+=msglen;    datarowdesc=_c.portal->_datarowdesc;    int cols=_c.getint16();    int atext = _c.portal->_alltext; // cache locally for speed +  int forcetext = _c.portal->_forcetext; // cache locally for speed    string cenc=_runtimeparameter[CLIENT_ENCODING];    a=allocate(cols,UNDEFINED);    msglen-=2+4*cols; -  foreach(a;int i;) +  foreach(datarowdesc;int i;mapping m)    { int collen=_c.getint32();    if(collen>0)    { msglen-=collen;    mixed value; -  switch(datarowdesc[i]->type) -  { default:value=_c.getstring(collen); +  switch(int typ=m->type) +  { case FLOAT4OID: + #if SIZEOF_FLOAT>=8 +  case FLOAT8OID: + #endif +  if(!atext) +  { value=(float)_c.getstring(collen);    break; -  case TEXTOID: -  case BPCHAROID: -  case VARCHAROID: -  value=_c.getstring(collen); -  if(cenc==UTF8CHARSET && catch(value=utf8_to_string(value))) -  ERROR("%O contains non-%s characters\n",value,UTF8CHARSET); +  } +  default:value=_c.getstring(collen);    break; -  case CHAROID:value=atext?_c.getstring(1):_c.getbyte(); +  case CHAROID: +  value=atext?_c.getstring(1):_c.getbyte();    break;    case BOOLOID:value=_c.getbyte(); -  +  switch(value) +  { case 'f':value=0; +  break; +  case 't':value=1; +  }    if(atext)    value=value?"t":"f";    break; -  case INT8OID:value=_c.getint64(); -  break; - #if SIZEOF_FLOAT>=8 -  case FLOAT8OID: - #endif -  case FLOAT4OID: +  case TEXTOID: +  case BPCHAROID: +  case VARCHAROID:    value=_c.getstring(collen); -  +  if(cenc==UTF8CHARSET && catch(value=utf8_to_string(value))) +  ERROR("%O contains non-%s characters\n",value,UTF8CHARSET); +  break; +  case INT8OID:case INT2OID: +  case OIDOID:case INT4OID: +  if(forcetext) +  { value=_c.getstring(collen);    if(!atext) -  value=(float)value; +  value=(int)value; +  } +  else +  { switch(typ) +  { case INT8OID:value=_c.getint64();    break;    case INT2OID:value=_c.getint16();    break;    case OIDOID:    case INT4OID:value=_c.getint32();    } -  if(atext&&!stringp(value)) +  if(atext)    value=(string)value; -  +  } +  }    a[i]=value;    }    else if(!collen)    a[i]="";    }    a=({a});    _c.portal->_datarows+=a;    _c.portal->_inflight-=sizeof(a);   #endif    }
pike.git/lib/modules/Sql.pmod/pgsql.pike:908:    };    warningsdropcount+=warningscollected;    warningscollected=0;    switch(msgresponse->C)    { case "P0001":    lastmessage=({sprintf("%s: %s",msgresponse->S,msgresponse->M)});    USERERROR(a2nls(lastmessage    +({pinpointerror(_c.portal->_query,msgresponse->P)})    +showbindings()));    case "57P01":case "57P02":case "57P03": -  preplastmessage();phasedreconnect(); -  PD(a2nls(lastmessage)); +  preplastmessage();phasedreconnect();PD(a2nls(lastmessage));    USERERROR(a2nls(lastmessage));    case "08P01":case "42P05":    errtype=protocolerror;    case "XX000":case "42883":case "42P01":    invalidatecache=1;    default:    preplastmessage();    if(msgresponse->D)    lastmessage+=({msgresponse->D});    if(msgresponse->H)
pike.git/lib/modules/Sql.pmod/pgsql.pike:1040:    if(_c)    { reconnected++;    prepstmtused=0;    if(!force)    _c.sendterminate();    else    _c.close();    _c=0;    foreach(prepareds;;mapping tp)    m_delete(tp,"preparedname"); -  if(!(connectmtxkey = _stealmutex.trylock(2))) +  if(!options->reconnect || !(connectmtxkey = _stealmutex.trylock(2)))    return 0; // Recursive reconnect, bailing out    }    if(!(_c=getsocket()))    { string msg=sprintf("Couldn't connect to database on %s:%d",host,port);    if(force) -  { lastmessage+=({msg}); +  { if(!sizeof(lastmessage) || lastmessage[sizeof(lastmessage)-1]!=msg) +  lastmessage+=({msg});    return 0;    }    else    ERROR(msg+"\n");    }    _closesent=0;    _mstate=unauthenticated;    qstate=queryidle; -  _runtimeparameter=([]); +  portalsinflight=unnamedportalinuse=0;    array(string) plugbuf=({"",_c.plugint32(PG_PROTOCOL(3,0))});    if(user)    plugbuf+=({"user\0",user,"\0"});    if(database)    plugbuf+=({"database\0",database,"\0"}); -  foreach(options-(<"use_ssl","force_ssl","cache_autoprepared_statements">); +  options->reconnect=zero_type(options->reconnect) || options->reconnect; +  foreach((options+_runtimeparameter) +  -(<"use_ssl","force_ssl","cache_autoprepared_statements","reconnect", +  "text_query","is_superuser","server_encoding","server_version", +  "integer_datetimes","session_authorization">);    string name;mixed value)    plugbuf+=({name,"\0",(string)value,"\0"});    plugbuf+=({"\0"});    int len=4;    foreach(plugbuf;;string s)    len+=sizeof(s);    plugbuf[0]=_c.plugint32(len);    _c.write(plugbuf);    PD("%O\n",plugbuf);    { mixed err=catch(_decodemsg(readyforquery));
pike.git/lib/modules/Sql.pmod/pgsql.pike:1121:   //!   //! @seealso   //! @[cancelquery()], @[reload()]   //!   //! @note   //! This function is PostgreSQL-specific, and thus it is not available   //! through the generic SQL-interface.   void resync(void|int special)   { mixed err;    int didsync; +  if(!is_open()&&!reconnect(1)) +  ERROR(a2nls(lastmessage));    if(err = catch    { sendclose(1);    PD("Portalsinflight: %d\n",portalsinflight);    if(!portalsinflight)    { if(!earlyclose)    { PD("Sync\n");    _c.sendcmd(({"S",_c.plugint32(4)}),2);    }    didsync=1;    if(!special)
pike.git/lib/modules/Sql.pmod/pgsql.pike:1584:   //!   //! 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.   //!   //! Bindings are supported natively straight across the network.   //! Special bindings supported are:   //! @mapping   //! @member int ":_cache"   //! Forces caching on or off for the query at hand. + //! @member int ":_text" + //! Forces text mode in communication with the database for queries on or off + //! for the query at hand. Potentially more efficient than the default + //! binary method for simple queries with small or no result sets. + //! Note that this mode causes all but the first query result of a list + //! of semicolon separated statements to be discarded.   //! @endmapping   //!   //! @returns   //! A @[Sql.pgsql_util.pgsql_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   //! 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   //! data (it's harder to handle, but fetches results on demand).   //!   //! @note   //! This function @b{can@} raise exceptions.   //!   //! @note   //! This function supports multiple simultaneous queries (portals) on a single   //! database connection. This is a feature not commonly supported by other   //! database backends.   //!   //! @note - //! This function does not support multiple queries in one querystring. + //! This function, by default, 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 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.   //!   //! @seealso   //! @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result],   //! @[Sql.Sql()->query()], @[Sql.pgsql_util.pgsql_result]   object big_query(string q,void|mapping(string|int:mixed) bindings,    void|int _alltyped)   { string preparedname="";    string portalname="";    int forcecache=-1; -  +  int forcetext=options->text_query;    string cenc=_runtimeparameter[CLIENT_ENCODING];    switch(cenc)    { case UTF8CHARSET:    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);    }    array(string|int) paramValues;
pike.git/lib/modules/Sql.pmod/pgsql.pike:1640:    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;    break; +  case ":_text":forcetext=(int)value; +  break;    }    continue;    }    if(!has_value(q,name))    continue;    }    from[rep]=name;    string rval;    if(multisetp(value)) // multisets are taken literally    rval=indices(value)*","; // and bypass the encoding logic
pike.git/lib/modules/Sql.pmod/pgsql.pike:1669:    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);    mapping(string:mixed) tp;    int tstart; -  if(forcecache==1 || forcecache!=0 && sizeof(q)>=MINPREPARELENGTH) +  if(forcetext) +  { if(bindings) +  q = .sql_util.emulate_bindings(q, bindings, this); +  if(unnamedportalinuse) +  throw("Unnamed portal in use, needed for simple query"); +  } +  else if(forcecache==1 +  || forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || cachealways[q]))    { array(string) plugbuf=({});    if(tp=prepareds[q])    { if(tp->preparedname)    prepstmtused++, preparedname=tp->preparedname;    else if((tstart=tp->trun)    && tp->tparse*FACTORPLAN>=tstart    && (zero_type(options->cache_autoprepared_statements)    || options->cache_autoprepared_statements))    preparedname=PREPSTMTPREFIX+(string)pstmtcount++;    }
pike.git/lib/modules/Sql.pmod/pgsql.pike:1711:    }    }    if(sizeof(plugbuf))    { _c.sendcmd(plugbuf,1); // close expireds    PD("%O\n",plugbuf);    }    tstart=gethrtime();    } // pgsql_result autoassigns to portal    else    tp=UNDEFINED; -  connectionclosed=0; +     for(;;) -  { .pgsql_util.pgsql_result(this,q,_fetchlimit,portalbuffersize,_alltyped, -  from); +  { connectionclosed=0; +  .pgsql_util.pgsql_result(this,q,_fetchlimit, +  portalbuffersize,_alltyped,from,forcetext);    if(unnamedportalinuse)    portalname=PORTALPREFIX+(string)pportalcount++;    else    unnamedportalinuse++;    _c.portal->_portalname=portalname;    qstate=inquery;    portalsinflight++; portalsopened++;    clearmessage=1;    mixed err;    if(!(err = catch -  +  { if(forcetext) +  { _c.sendcmd(({"Q",_c.plugint32(4+sizeof(q)+1),q,"\0"}),1); +  PD("Simple query: %O\n",q); +  } +  else    { if(!sizeof(preparedname) || !tp || !tp->preparedname)    { PD("Parse statement %s\n",preparedname);    // 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    // server v8.3.3    _c.sendcmd(({"P",_c.plugint32(4+sizeof(preparedname)+1+sizeof(q)+1+2),    preparedname,"\0",q,"\0",_c.plugint16(0)}),3);    PD("Query: %O\n",q);    } // sends Parameter- and RowDescription for 'S'
pike.git/lib/modules/Sql.pmod/pgsql.pike:1811:    break;    }    default:    { int k;    if(!value)    { plugbuf+=({_c.plugint32(-1)});    break;    }    value=(string)value;    if(String.width(value)>8) +  if(dtoid[i]==BYTEAOID) +  value=string_to_utf8(value); +  else    ERROR("Wide string %O not supported for type OID %d\n",    value,dtoid[i]);    len+=k=sizeof(value);    plugbuf+=({_c.plugint32(k),value});    break;    }    case BOOLOID:plugbuf+=({_c.plugint32(1)});len++;    do    { int tval;    if(stringp(value))
pike.git/lib/modules/Sql.pmod/pgsql.pike:1838:    switch(tval)    { case 'o':case 'O':    catch    { tval=value[1];    value=tval=='n'||tval=='N';    };    break;    default:    value=1;    break; -  case 0:case 'f':case 'F':case 'n':case 'N': +  case 0:case '0':case 'f':case 'F':case 'n':case 'N':    value=0;    break;    }    }    while(0);    plugbuf+=({_c.plugbyte(value)});    break;    case CHAROID:    if(intp(value))    len++,plugbuf+=({_c.plugint32(1),_c.plugbyte(value)});
pike.git/lib/modules/Sql.pmod/pgsql.pike:1891:    }    { array a;int i;    len+=(i=sizeof(a=_c.portal->_datarowdesc))*2;    plugbuf+=({_c.plugint16(i)});    foreach(a;;mapping col)    plugbuf+=({_c.plugint16(oidformat(col->type))});    }    plugbuf[1]=_c.plugint32(len);    PD("Bind portal %s statement %s\n",portalname,preparedname);    _c.sendcmd(plugbuf); - #ifdef DEBUGMORE + #ifdef DEBUGMORE    PD("%O\n",plugbuf);   #endif    }    _c.portal->_statuscmdcomplete=UNDEFINED;    _sendexecute(_fetchlimit -  && !limitpostfix->match(q) // Optimisation for LIMIT 1 +  && !(cachealways[q] +  || sizeof(q)>=MINPREPARELENGTH && execfetchlimit->match(q))    && FETCHLIMITLONGRUN);    if(tp)    { _decodemsg(bindcomplete);    int tend=gethrtime();    if(tend==tstart)    m_delete(prepareds,q);    else    { tp->hits++;    totalhits++;    if(!tp->preparedname)    { if(sizeof(preparedname))    tp->preparedname=preparedname;    tstart=tend-tstart;    if(!tp->tparse || tp->tparse>tstart)    tp->tparse=tstart;    }    tp->trunstart=tend;    }    tprepared=tp;    } -  +  }    }))    break;    PD("%O\n",err);    resync(1);    backendstatus=UNDEFINED;    if(!connectionclosed)    throw(err);    tp=UNDEFINED;    }    { object tportal=_c.portal; // Make copy, because it might dislodge