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

version» Context lines:

pike.git/lib/modules/Sql.pmod/pgsql.pike:84:   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; - private int reconnected; // Number of times the connection was reset - private int reconnectdelay; // Time to next reconnect +    #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;
pike.git/lib/modules/Sql.pmod/pgsql.pike:143:   //!   //! @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.   //! @member int "force_ssl"   //! If the database supports and allows SSL connections, the session   //! 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.
pike.git/lib/modules/Sql.pmod/pgsql.pike:218:       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(); -  reconnect(); +  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;   }      //! @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).   //!   //! During the execution of a statement, this function accumulates all   //! non-error messages (notices, warnings, etc.). If a statement does not
pike.git/lib/modules/Sql.pmod/pgsql.pike:285:    return 0;   }      //! Check whether the connection is alive.   //!   //! @returns   //! Returns one of the following:   //! @int   //! @value 0   //! Everything ok. - //! @value 1 - //! The connection has reconnected automatically. +    //! @value -1   //! The server has gone away, and the connection is dead.   //! @endint   //!   //! @seealso   //! @[is_open()]   /*semi*/final int ping() { -  return is_open() && !catch(c->start()->sendcmd(FLUSHSEND)) -  ? !!reconnected : -1; +  waitauthready(); +  return is_open() && !catch(c->start()->sendcmd(FLUSHSEND)) ? 0 : -1;   }      //! Cancels all currently running queries in this session.   //!   //! @seealso   //! @[reload()], @[resync()]   //!   //! @note   //! This function is PostgreSQL-specific, and thus it is not available   //! through the generic SQL-interface.
pike.git/lib/modules/Sql.pmod/pgsql.pike:416:   //! Total number of prepared statements generated.   //! @member int "portals_opened_count"   //! Total number of portals opened, i.e. number of statements issued   //! to the database.   //! Only available if PG_STATS is compile-time enabled.   //! @member int "bytes_received"   //! Total number of bytes received from the database so far.   //! @member int "messages_received"   //! Total number of messages received from the database (one SQL-statement   //! requires multiple messages to be exchanged). - //! @member int "reconnect_count" - //! Number of times the connection to the database has been lost. +    //! @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.   /*semi*/final mapping(string:mixed) getstatistics() {    mapping(string:mixed) stats=([    "warnings_dropped":warningsdropcount,    "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, +     ]);    return stats;   }      //! @param newdepth   //! Sets the new cachedepth for automatic caching of prepared statements.   //!   //! @returns   //! The previous cachedepth.   //!
pike.git/lib/modules/Sql.pmod/pgsql.pike:530:    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 void reconnect_cb() { -  lastmessage+=({sprintf("Reconnected to database %s",host_info())}); -  runcallback(backendpid,"_reconnect",""); - } -  +    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)));
pike.git/lib/modules/Sql.pmod/pgsql.pike:592:   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); -  _options.reconnect=undefinedp(_options.reconnect) || _options.reconnect; +     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())) { -  if(_options.reconnect) -  _connectfail(); -  else +     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();
pike.git/lib/modules/Sql.pmod/pgsql.pike:640: Inside #if defined(PG_DEBUG)
  #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");
pike.git/lib/modules/Sql.pmod/pgsql.pike:726:    };    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 { -  .pgsql_util.local_backend->remove_call_out(reconnect); -  reconnectdelay=0; +  } else    cancelsecret=""; -  } +     break;    case 2:    PD("KerberosV5\n");    errtype=PROTOCOLUNSUPPORTED;    break;    case 3:    PD("ClearTextPassword\n");    authresponse(({pass, 0}));    break;    case 4:
pike.git/lib/modules/Sql.pmod/pgsql.pike:1135:    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(0); +  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});
pike.git/lib/modules/Sql.pmod/pgsql.pike:1173:    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)}); -  if(has_prefix(msgresponse.C,"53")||has_prefix(msgresponse.C,"57P")) { +  int val; +  if (val = msgisfatal(msgresponse)) { // Some warnings are fatal    preplastmessage(msgresponse); -  PD(a2nls(lastmessage));throw(0); // Some warnings are fatal +  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();
pike.git/lib/modules/Sql.pmod/pgsql.pike:1275:    break;    }    PD("Closing database processloop %s\n", err ? describe_backtrace(err) : "");    _delayederror=err;    if (objectp(portal)) {   #ifdef PG_DEBUG    showportal(0);   #endif    portal->_purgeportal();    } -  if(!terminating && _options.reconnect) -  _connectfail(); -  else +     destruct(waitforauthready);    termlock=0;    if(err && !stringp(err))    throw(err);    }; -  if (err) { +     unnamedstatement=0;    termlock = 0; -  +  if (err) {    PD("Terminating processloop due to %s\n", describe_backtrace(err));    } -  +  _connectfail(err);   }      //! Closes the connection to the database, any running queries are   //! terminated instantly.   //!   //! @note   //! This function is PostgreSQL-specific, and thus it is not available   //! through the generic SQL-interface.   /*semi*/final void close() {    throwdelayederror(this);
pike.git/lib/modules/Sql.pmod/pgsql.pike:1322:      protected void _destruct() {    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, has_prefix, "ERROR "); +  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   }      final void _connectfail(void|mixed err) { -  PD("Connect failed %O reconnectdelay %d\n",err,reconnectdelay); -  if(!err || reconnectdelay) { -  int tdelay; -  switch(tdelay=reconnectdelay) { -  case 0: -  reconnectdelay=RECONNECTDELAY; -  break; -  default: -  if(err) -  _delayederror=err; -  if (_options.reconnect!=-1) { +  if (err) { +  PD("Connect failed %O\n", err); +  _delayederror = err; +  }    destruct(waitforauthready);    destruct(c); -  return; +    } -  reconnectdelay=RECONNECTBACKOFF; -  break; -  } -  Thread.MutexKey lock=_shortmux->lock(); -  if(!waitforauthready) -  waitforauthready=Thread.Condition(); -  lock=0; -  PD("Schedule reconnect in %ds\n",tdelay); -  _delayederror=0; -  callout(reconnect,tdelay,1); -  } else if(err) -  _delayederror=err; - } +     - private int reconnect() { -  int recon=0; -  PD("(Re)connect\n"); -  { -  Thread.MutexKey lock=_shortmux->lock(); -  if (!waitforauthready) -  waitforauthready=Thread.Condition(); -  lock=0; -  } -  if(c) { -  PD("Close old connection\n"); -  reconnected++;recon=1; - #ifdef PG_STATS -  prepstmtused=0; - #endif -  if (unnamedstatement) -  termlock = unnamedstatement->lock(1); -  catch(c->close()); -  unnamedstatement = 0; -  termlock = 0; -  catch(destruct(c)); -  PD("Flushing old cache\n"); -  foreach(_prepareds;;mapping tp) -  m_delete(tp,"preparedname"); -  if(!_options.reconnect) -  ERROR("Lost connection to database %s:%d\n",_host,_port); -  } -  PD("Actually start to connect\n"); -  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=recon?reconnect_cb:connect_cb; -  _portalsinflight = Thread.ResourceCount(); -  _statementsinflight = Thread.ResourceCount(); -  _wasparallelisable = 0; -  return 1; - } -  +    //! For PostgreSQL this function performs the same function as @[resync()].   //!   //! @seealso   //! @[resync()], @[cancelquery()]   /*semi*/final void reload() {    resync();   }      private void reset_dbsession() {    big_query("ROLLBACK");
pike.git/lib/modules/Sql.pmod/pgsql.pike:1468:    PD("Statementsinflight: %d Portalsinflight: %d\n",    _statementsinflight, _portalsinflight);    if(!waitforauthready) {    readyforquery_cb=resync_cb;    sendsync();    }    return;    };    PD("%O\n",err);    } -  if(!reconnect()&&sizeof(lastmessage)) +  if(sizeof(lastmessage))    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 - //! allows you to connect to a different database on the same server. + //! Due to restrictions of the Postgres frontend-backend protocol, you always + //! already have to be connected to a database. + //! To connect to a different database you have to select the right + //! database while connecting instead. This function is a no-op when + //! specifying the same database, and throws an error otherwise.   //! - //! @note - //! This function @b{can@} raise exceptions if something goes wrong - //! (backend process not running, insufficient privileges...) - //! +    //! @seealso   //! @[create()]   /*semi*/final void select_db(string dbname) { -  database=dbname; -  reconnect(); -  reconnected=0; +  if (database != dbname) +  ERROR("Cannot switch databases from %O to %O" +  " in an already established connection\n", +  database, dbname);   }      //! With PostgreSQL you can LISTEN to NOTIFY events.   //! This function allows you to detect and handle such events.   //!   //! @param condition   //! Name of the notification event we're listening   //! to. A special case is the empty string, which matches all events,   //! and can be used as fallback function which is called only when the   //! specific condition is not handled. Another special case is - //! @expr{"_reconnect"@} which gets called whenever the connection - //! unexpectedly drops and reconnects to the database. + //! @expr{"_lost"@} which gets called whenever the connection + //! to the database unexpectedly drops.   //!   //! @param notify_cb   //! Function to be called on receiving a notification-event of   //! condition @ref{condition@}.   //! The callback function is invoked with   //! @expr{void notify_cb(pid,condition,extrainfo, .. args);@}   //! @tt{pid@} is the process id of the database session that originated   //! the event. @tt{condition@} contains the current condition.   //! @tt{extrainfo@} contains optional extra information specified by   //! the database.
pike.git/lib/modules/Sql.pmod/pgsql.pike:1970:    * multistatement text-queries?    * The primary function of this detection is to ensure a SYNC    * right after a COMMIT, and no SYNC after a BEGIN.    */    int transtype = .pgsql_util.transendprefix->match(q) ? TRANSEND    : .pgsql_util.transbeginprefix->match(q) ? TRANSBEGIN : NOTRANS;    if (transtype != NOTRANS)    tp = .pgsql_util.describenodata; // Description already known    else if (!forcetext && forcecache == 1    || forcecache && sizeof(q) >= MINPREPARELENGTH) { -  object plugbuffer; -  while(catch(plugbuffer=c->start())) -  reconnect(); +  object plugbuffer = c->start();    if(tp=_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))