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

version» Context lines:

pike.git/lib/modules/Sql.pmod/pgsql.pike:71:   mapping(string:mixed) options;   protected array(string) lastmessage=({});   protected int clearmessage;   protected mapping(string:array(mixed)) notifylist=([]);   mapping(string:string) _runtimeparameter;   protected enum querystate {    streconnect,reconnectforce,queryidle,inquery,cancelpending,canceled};   protected querystate qstate;   protected mapping(string:mapping(string:mixed)) prepareds=([]);   protected int pstmtcount; - protected int ptstmtcount; + protected int ptstmtcount; // Periodically one would like to reset this +  // but checking when this is safe to do +  // probably is more costly than the gain   int _pportalcount;   protected int totalhits;   protected int cachedepth=STATEMENTCACHEDEPTH;   protected int timeout=QUERYTIMEOUT;   protected int portalbuffersize=PORTALBUFFERSIZE;   protected int reconnected; // Number of times the connection was reset   protected int reconnectdelay; // Time to next reconnect   #ifdef PG_STATS   protected int skippeddescribe; // Number of times we skipped Describe phase   protected int portalsopened; // Number of portals opened -  + protected int prepstmtused; // Number of times prepared statements were used   #endif   int _msgsreceived; // Number of protocol messages received   int _bytesreceived; // Number of bytes received   protected int warningsdropcount; // Number of uncollected warnings - protected int prepstmtused; // Number of times prepared statements were used +    protected int warningscollected;   protected int invalidatecache;   protected Thread.Queue qportals;   mixed _delayederror;   protected function (:void) readyforquery_cb;      string _host;   protected string database, user, pass;   int _port;   protected multiset cachealways=(<"BEGIN","begin","END","end","COMMIT","commit">);   protected object createprefix    =Regexp("^[ \t\f\r\n]*[Cc][Rr][Ee][Aa][Tt][Ee][ \t\f\r\n]");   protected object dontcacheprefix    =Regexp("^[ \t\f\r\n]*([Ff][Ee][Tt][Cc][Hh]|[Cc][Oo][Pp][Yy])[ \t\f\r\n]");   protected 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]*$");   protected Thread.Mutex waitforauth;   protected Thread.Condition waitforauthready; -  + int _readyforquerycount;      #define DERROR(msg ...) ({sprintf(msg),backtrace()})   #define SERROR(msg ...) (sprintf(msg))   #define USERERROR(msg) throw(msg)   #define SUSERERROR(msg ...) USERERROR(SERROR(msg))      protected string _sprintf(int type, void|mapping flags) {    string res=UNDEFINED;    switch(type) {    case 'O':
pike.git/lib/modules/Sql.pmod/pgsql.pike:421:   //! A set of statistics for the current session:   //! @mapping   //! @member int "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.   #ifdef PG_STATS   //! @member int "skipped_describe_count"   //! Number of times the driver skipped asking the database to   //! describe the statement parameters because it was already cached. - #endif +    //! @member int "used_prepared_statements"   //! Numer of times prepared statements were used from cache instead of   //! reparsing in the current session. -  + #endif   //! @member int "current_prepared_statements"   //! Cache size of currently prepared statements.   //! @member int "current_prepared_statement_hits"   //! Sum of the number hits on statements in the current statement cache.   //! @member int "prepared_statement_count"   //! Total number of prepared statements generated.   #ifdef PG_STATS   //! @member int "portals_opened_count"   //! Total number of portals opened, i.e. number of statements issued   //! to the database.
pike.git/lib/modules/Sql.pmod/pgsql.pike:453:   //! @member int "portals_in_flight"   //! Currently still open portals, i.e. running statements.   //! @endmapping   //!   //! @note   //! 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, -  "used_prepared_statements":prepstmtused, +     "current_prepared_statements":sizeof(prepareds),    "current_prepared_statement_hits":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,    "reconnect_count":reconnected,    "portals_in_flight":_portalsinflight,    ]);    return stats;   }
pike.git/lib/modules/Sql.pmod/pgsql.pike:606:      protected void storetiming(object portal) {    mapping(string:mixed) tp=portal._tprepared;    tp.trun=gethrtime()-tp.trunstart;    m_delete(tp,"trunstart");    portal._tprepared = UNDEFINED;   }      final void _processloop(object ci) {    int die=0,terminating=0; -  .pgsql_util.pgsql_result portal; +  int|.pgsql_util.pgsql_result portal;    mixed err;    {    object plugbuffer=Stdio.Buffer()->add_int32(PG_PROTOCOL(3,0));    if(user)    plugbuffer->add("user\0")->add(user)->add_int8(0);    if(database)    plugbuffer->add("database\0")->add(database)->add_int8(0);    options->reconnect=undefinedp(options->reconnect) || options->reconnect;    foreach(options    -(<"use_ssl","force_ssl","cache_autoprepared_statements","reconnect",    "text_query","is_superuser","server_encoding","server_version",    "integer_datetimes","session_authorization">);    string name;mixed value)    plugbuffer->add(name)->add_int8(0)->add((string)value)->add_int8(0);    plugbuffer->add_int8(0);    PD("%O\n",(string)plugbuffer);    ci->start()->add_hstring(plugbuffer,4,4)->sendcmd(flushsend);    }    cancelsecret=0; -  PD("Processloop\n"); +    #ifdef PG_DEBUG -  +  PD("Processloop\n");    string datarowdebug;    int datarowdebugcount; -  +  +  void showportal(int msgtype) { +  if(objectp(portal)) +  PD("<%O %d %c switch portal\n", +  portal._portalname,++ci->queueinidx,msgtype); +  else if(portal>0) +  PD("<Sync %d %d %c portal\n",++ci->queueinidx,portal,msgtype); +  };   #endif    for(;;) {    err=catch {   #ifdef PG_DEBUG    if(!portal && datarowdebug) {    PD("%s rows %d\n",datarowdebug,datarowdebugcount);    datarowdebug=0; datarowdebugcount=0;    }   #endif    int msgtype=ci->read_int8();    if(!portal) {    portal=qportals->try_read(); -  if(portal) -  PD("<%O %d %c switch portal ===================\n", -  portal._portalname,++ci->queueinidx,msgtype); + #ifdef PG_DEBUG +  showportal(msgtype); + #endif    }    if(qstate==cancelpending)    qstate=canceled,sendclose();    int msglen=ci->read_int32();    _msgsreceived++;    _bytesreceived+=1+msglen;    enum errortype {    noerror=0,    protocolerror,    protocolunsupported
pike.git/lib/modules/Sql.pmod/pgsql.pike:746:    PD("GSS\n");    errtype=protocolunsupported;    break;    case 9:    PD("SSPI\n");    errtype=protocolunsupported;    break;    case 8:    PD("GSSContinue\n");    errtype=protocolunsupported; +  cancelsecret=ci->read(msglen); // Actually SSauthdata   #ifdef PG_DEBUG    if(msglen<1)    errtype=protocolerror; - #endif -  cancelsecret=ci->read(msglen); // Actually SSauthdata - #ifdef PG_DEBUG +     msglen=0;   #endif    break;    default:    PD("Unknown Authentication Method %c\n",authtype);    errtype=protocolunsupported;    break;    }    switch(errtype) {    case noerror:
pike.git/lib/modules/Sql.pmod/pgsql.pike:776:    default:    case protocolunsupported:    ERROR("Unsupported authenticationmethod %c\n",authtype);    break;    }    break;    }    case 'K':    msglen-=4+4;backendpid=ci->read_int32();    cancelsecret=ci->read(msglen); -  PD("<BackendKeyData %O\n",cancelsecret); +    #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]; -  PD("%O=%O\n",ts[0],ts[1]); +    #ifdef PG_DEBUG -  +  PD("%O=%O\n",ts[0],ts[1]);    } else    errtype=protocolerror;   #endif    break;    }    case '3': -  PD("<CloseComplete\n"); +    #ifdef PG_DEBUG -  +  PD("<CloseComplete\n");    msglen-=4;   #endif    break;    case 'Z': -  +  backendstatus=ci->read_int8();   #ifdef PG_DEBUG    msglen-=4+1; - #endif -  backendstatus=ci->read_int8(); +     PD("<ReadyForQuery %c\n",backendstatus); -  + #endif +  for(;objectp(portal);portal->read()) { + #ifdef PG_DEBUG +  showportal(msgtype); + #endif +  portal->_closeportal(); +  } +  foreach(qportals->peek_array();;.pgsql_util.pgsql_result qp) { +  PD("Checking portal %O %d<=%d\n", +  qp._portalname,qp._synctransact,portal); +  if(qp._synctransact && qp._synctransact<=portal) +  qp->_closeportal(); +  } +  portal=0; +  _readyforquerycount--;    if(readyforquery_cb)    readyforquery_cb(),readyforquery_cb=0;    qstate=queryidle;    if(waitforauthready) {    Thread.MutexKey lock=waitforauth->lock();    waitforauthready->broadcast();    waitforauthready=0;    lock=0;    }    break;    case '1': -  PD("<ParseComplete\n"); +    #ifdef PG_DEBUG -  +  PD("<ParseComplete\n");    msglen-=4;   #endif    break;    case 't': {    array a;    int cols=ci->read_int16(); -  PD("<%O ParameterDescription %d values\n",portal._query,cols); +    #ifdef PG_DEBUG -  +  PD("<%O ParameterDescription %d values\n",portal._query,cols);    msglen-=4+2+4*cols;   #endif    foreach(a=allocate(cols);int i;)    a[i]=ci->read_int32();   #ifdef PG_DEBUGMORE    PD("%O\n",a);   #endif    if(portal._tprepared)    portal._tprepared.datatypeoid=a;    preparebind(portal);
pike.git/lib/modules/Sql.pmod/pgsql.pike:888:    PD("%O\n",a);   #endif    portal._datarowdesc=a;    processrowdescription(portal);    portal=0;    break;    }    case 'n': {   #ifdef PG_DEBUG    msglen-=4; - #endif +     PD("<NoData %O\n",portal._query); -  + #endif    portal._datarowdesc=({});    portal._fetchlimit=0; // disables subsequent Executes    processrowdescription(portal);    portal=0;    break;    }    case 'H':    portal._datarowdesc=getcols();    PD("<CopyOutResponse %d %O\n",    sizeof(portal._datarowdesc),portal._query);    processrowdescription(portal);    break;    case '2': {    mapping tp;   #ifdef PG_DEBUG    msglen-=4; - #endif +     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))
pike.git/lib/modules/Sql.pmod/pgsql.pike:933:    tp.trunstart=tend;    }    }    break;    }    case 'D': {    msglen-=4;    string serror;    if(portal._tprepared)    storetiming(portal); - #ifdef USEPGsql -  ci->decodedatarow(msglen);msglen=0; - #else +     array a, datarowdesc;    portal._bytesreceived+=msglen;    datarowdesc=portal._datarowdesc;    int cols=ci->read_int16();   #ifdef PG_DEBUG   #ifdef PG_DEBUGMORE    PD("<%O DataRow %d cols %d bytes\n",portal._portalname,cols,msglen);   #endif    datarowdebugcount++;    if(!datarowdebug)
pike.git/lib/modules/Sql.pmod/pgsql.pike:1018:    }    }    a[i]=value;    } else if(!collen)    a[i]="";    }    portal._inflight--;    portal._datarows->write(a);    if(serror)    ERROR(serror); - #endif // USEPGsql +     portal->_processdataready(fetchlimit);    break;    }    case 's': -  PD("<%O PortalSuspended\n",portal._portalname); - #if !STREAMEXECUTES -  portal->_sendexecute(portal._fetchlimit); - #endif +    #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
pike.git/lib/modules/Sql.pmod/pgsql.pike:1056: Inside #if defined(PG_DEBUG)
   errtype=protocolerror;    msglen=0;   #else    ci->consume(1);   #endif    portal->_releasesession();    portal=0;    break;    }    case 'I': -  PD("<EmptyQueryResponse %O\n",portal._portalname); +    #ifdef PG_DEBUG -  +  PD("<EmptyQueryResponse %O\n",portal._portalname);    msglen-=4;   #endif    portal->_releasesession();    portal=0;    break;    case 'd':    PD("<%O CopyData\n",portal._portalname);    if(portal._tprepared)    storetiming(portal);    msglen-=4;
pike.git/lib/modules/Sql.pmod/pgsql.pike:1098:    }    break;    case 'c':   #ifdef PG_DEBUG    PD("<%O CopyDone\n",portal._portalname);    msglen-=4;   #endif    portal=0;    break;    case 'E': { -  if(portal) -  portal->_releasesession(); +  if(!_readyforquerycount) +  sendsync();    PD("<%O ErrorResponse %O\n", -  portal&&portal._portalname,portal&&portal._query); +  portal&&(portal._portalname||portal._preparedname), +  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)));
pike.git/lib/modules/Sql.pmod/pgsql.pike:1136:    pinpointerror(portal&&portal._query,msgresponse.P)+    pinpointerror(msgresponse.q,msgresponse.p)});    if(msgresponse.W)    lastmessage+=({msgresponse.W});    lastmessage+=showbindings(portal);    switch(msgresponse.S) {    case "PANIC":werror(a2nls(lastmessage));    }    USERERROR(a2nls(lastmessage));    } +  if(portal) +  portal->_releasesession();    break;    }    case 'N': {    PD("<NoticeResponse\n");    mapping(string:string) msgresponse;    msgresponse=getresponse();    if(clearmessage) {    warningsdropcount+=warningscollected;    clearmessage=warningscollected=0;    lastmessage=({});
pike.git/lib/modules/Sql.pmod/pgsql.pike:1222:    ERROR(a2nls(lastmessage+=({msg})));    }    }; // We only get here if there is an error    if(err==MAGICTERMINATE) {    ci->start()->add("X\0\0\0\4")->sendcmd(sendout);    terminating=1;    if(!sizeof(ci))    break;    }    if(stringp(err)) { -  object to=portal?portal:this; -  if(!to._delayederror) -  to._delayederror=err; +  .pgsql_util.pgsql_result or; +  if(!(or=portal)) +  or=this; +  if(!or._delayederror) +  or._delayederror=err; +  if(portal) +  portal->_releasesession(); +  portal=0;    continue;    }    break;    }    _delayederror=err;    if(!ci->close() && !terminating && options.reconnect)    _connectfail();   }      //! Closes the connection to the database, any running queries are
pike.git/lib/modules/Sql.pmod/pgsql.pike:1287:    Thread.MutexKey lock=waitforauth->lock();    if(waitforauthready) {    lock=0;    return 0; // Connect still in progress in other thread    }    waitforauthready=Thread.Condition();    lock=0;    }    if(c) {    reconnected++; + #ifdef PG_STATS    prepstmtused=0; -  + #endif    if(!force)    c->sendterminate();    else    c->close();    c=0;    foreach(prepareds;;mapping tp)    m_delete(tp,"preparedname");    if(!options.reconnect)    return 0;    }    qportals=Thread.Queue(); -  +  _readyforquerycount=1; +  qportals->write(1);    if(!(c=getsocket())) {    string msg=sprintf("Couldn't connect to database on %s:%d",_host,_port);    if(force) {    if(!sizeof(lastmessage) || lastmessage[sizeof(lastmessage)-1]!=msg)    lastmessage+=({msg});    return 0;    } else    ERROR(msg+"\n");    }    _runtimeparameter=([]);
pike.git/lib/modules/Sql.pmod/pgsql.pike:1341:    m_delete(tp,"datatypeoid");    m_delete(tp,"datarowdesc");    }    big_query("ROLLBACK");    big_query("RESET ALL");    big_query("CLOSE ALL");    big_query("DISCARD TEMP");    }   }    + protected void sendsync() { +  _readyforquerycount++; +  c->start()->sendcmd(syncsend); + } +    //! @decl void resync()   //!   //! Resyncs the database session; typically used to make sure the session is   //! not still in a dangling transaction.   //!   //! If called while the connection is in idle state, the function is   //! lightweight and briefly touches base with the database server to   //! make sure client and server are in sync.   //!   //! If issued while inside a transaction, it will rollback the transaction,
pike.git/lib/modules/Sql.pmod/pgsql.pike:1367:   //! @note   //! This function is PostgreSQL-specific, and thus it is not available   //! through the generic SQL-interface.   void resync(void|int|object portal) {    mixed err;    if(!is_open()&&!reconnect())    ERROR(a2nls(lastmessage));    err = catch {    PD("Portalsinflight: %d\n",_portalsinflight);    readyforquery_cb=resync_cb; -  c->start()->add(PGSYNC)->sendcmd(sendout); +  sendsync();    return;    };    PD("%O\n",err);    if(!reconnect())    ERROR(a2nls(lastmessage));   }      //! 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
pike.git/lib/modules/Sql.pmod/pgsql.pike:1882:    lock=0;    }    int tstart;    if(forcetext) {    if(bindings)    q = .sql_util.emulate_bindings(q, bindings, this);    } else if(forcecache==1    || forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || cachealways[q])) {    object plugbuffer=c->start();    if(tp=prepareds[q]) { -  if(tp.preparedname) -  prepstmtused++, preparedname=tp.preparedname; -  else if((tstart=tp.trun) +  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+(string)pstmtcount++;    } else {    if(totalhits>=cachedepth)    foreach(prepareds;string ind;tp) {    int oldhits=tp.hits;    totalhits-=oldhits-(tp.hits=oldhits>>1);    if(oldhits<=1) {
pike.git/lib/modules/Sql.pmod/pgsql.pike:1930:    object portal;    portal=.pgsql_util.pgsql_result(this,c,q,    portalbuffersize,_alltyped,from,forcetext);    portal._tprepared=tp;    qstate=inquery;   #ifdef PG_STATS    portalsopened++;   #endif    clearmessage=1;    object plugbuffer=c; -  if(forcetext) { +  if(forcetext) { // FIXME What happens if portals are still open?    portal._unnamedportalkey=unnamedportalmux->lock(1);    portal->_openportal(); -  plugbuffer->start()->add_int8('Q')->add_hstring(q,4,4+1)->add_int8(0) -  ->sendcmd(flushsend,portal); +  _readyforquerycount++; +  Thread.MutexKey lock=unnamedstatement->lock(1); +  plugbuffer->start(1)->add_int8('Q')->add_hstring(q,4,4+1)->add_int8(0) +  ->sendcmd(flushlogsend,portal); +  lock=0;    PD("Simple query: %O\n",q);    } else {    object parsebuffer;    if(!sizeof(preparedname) || !tp || !tp.preparedname) {    if(!sizeof(preparedname))    preparedname=    (portal._unnamedstatementkey=unnamedstatement->trylock(1))    ? "" : PTSTMTPREFIX+(string)ptstmtcount++;    // Even though the protocol doesn't require the Parse command to be    // followed by a flush, it makes a VERY noticeable difference in