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

version» Context lines:

pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:29:   #define PURGED 7   // If this is extended, change the type of _state      #define NOERROR 0 // Error states networkparser   #define PROTOCOLERROR 1   #define PROTOCOLUNSUPPORTED 2      #define LOSTERROR "Database connection lost"      //! The instance of the pgsql dedicated backend. - final Pike.Backend local_backend = Pike.SmallBackend(); + final Pike.Backend local_backend;    -  + private Pike.Backend cb_backend;   private Thread.Mutex backendmux = Thread.Mutex();   private Thread.ResourceCount clientsregistered = Thread.ResourceCount();      constant emptyarray = ({});   constant describenodata    = (["datarowdesc":emptyarray, "datarowtypes":emptyarray,    "datatypeoid":emptyarray]);   private constant censoroptions = (<"use_ssl", "force_ssl",    "cache_autoprepared_statements", "reconnect", "text_query", "is_superuser",    "server_encoding", "server_version", "integer_datetimes",
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:90:    * not be able to deliver results belonging to other parallel queries    * running on the same filedescriptor.    *    * However, considering that the current heuristic increases query-speed    * in the majority of the real-world cases, it would be considered a good    * tradeoff.    */   private Regexp execfetchlimit    = iregexp("^\a*((UPDA|DELE)TE|INSERT)\a|\aLIMIT\a+[1-9][; \t\f\r\n]*$");    + private void default_backend_runs() { // Runs as soon as the +  cb_backend = Pike.DefaultBackend; // DefaultBackend has started + } +  + private void create() { +  // Run callbacks from our local_backend until DefaultBackend has started +  cb_backend = local_backend = Pike.SmallBackend(); +  call_out(default_backend_runs, 0); + } +    private Regexp iregexp(string expr) {    Stdio.Buffer ret = Stdio.Buffer();    foreach (expr; ; int c)    if (c >= 'A' && c <= 'Z')    ret->add('[', c, c + 'a' - 'A', ']');    else if (c == '\a') // Replace with generic whitespace    ret->add("[ \t\f\r\n]");    else    ret->add_int8(c);    return Regexp(ret->read());
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:120:    Thread.MutexKey lock;    int looponce;    do {    looponce = 0;    if (lock = backendmux->trylock()) {    PD("Starting local backend\n");    while (!clientsregistered->drained() // Autoterminate when not needed    || sizeof(local_backend->call_out_info())) {    mixed err;    if (err = catch(local_backend(4096.0))) -  werror(describe_backtrace(err)); +  master()->handle_error(err);    }    PD("Terminating local backend\n");    lock = 0;    looponce = !clientsregistered->drained();    }    } while (looponce);   }      //! Registers yourself as a user of this backend. If the backend   //! has not been started yet, it will be spawned automatically.
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:152:    parent->untolderror = 0;    else if (parent->pgsqlsess)    parent->pgsqlsess->untolderror = 0;    parent.delayederror = 0;    if (stringp(err))    err = ({err, backtrace()[..<2]});    throw(err);    }   }    - private int oidformat(int oid) { + private int readoidformat(int oid) {    switch (oid) {    case BOOLOID:    case BYTEAOID:    case CHAROID:    case INT8OID:    case INT2OID:    case INT4OID: -  +  case FLOAT4OID: + #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__) +  case FLOAT8OID: + #endif    case TEXTOID:    case OIDOID:    case XMLOID: -  +  case DATEOID: +  case TIMEOID: +  case TIMETZOID: +  case TIMESTAMPOID: +  case TIMESTAMPTZOID: +  case INTERVALOID: +  case INT4RANGEOID: +  case INT8RANGEOID: +  case DATERANGEOID: +  case TSRANGEOID: +  case TSTZRANGEOID:    case MACADDROID:    case BPCHAROID:    case VARCHAROID: -  +  case CIDROID: +  case INETOID:    case CTIDOID:    case UUIDOID:    return 1; //binary    }    return 0; // text   }    -  + private int writeoidformat(int oid, array(string|int) paramValues, +  array(int) ai) { +  mixed value = paramValues[ai[0]++]; +  switch (oid) { +  case BOOLOID: +  case BYTEAOID: +  case CHAROID: +  case INT8OID: +  case INT2OID: +  case INT4OID: +  case TEXTOID: +  case OIDOID: +  case XMLOID: +  case MACADDROID: +  case BPCHAROID: +  case VARCHAROID: +  case CTIDOID: +  case UUIDOID: +  return 1; //binary +  case CIDROID: +  case INETOID: +  case DATEOID: +  case TIMEOID: +  case TIMETZOID: +  case TIMESTAMPOID: +  case TIMESTAMPTZOID: +  case INTERVALOID: +  case INT4RANGEOID: +  case INT8RANGEOID: +  case DATERANGEOID: +  case TSRANGEOID: +  case TSTZRANGEOID: +  case FLOAT4OID: + #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__) +  case FLOAT8OID: + #endif +  if (!stringp(value)) +  return 1; +  } +  return 0; // text + } +  + #define DAYSEPOCHTO2000 10957 // 2000/01/01 00:00:00 UTC + #define USEPOCHTO2000 (DAYSEPOCHTO2000*24*3600*1000000) +  + private array timestamptotype +  = ({Val.Timestamp, 8, USEPOCHTO2000, "usecs", 8}); + private array datetotype = ({Val.Date, 4, DAYSEPOCHTO2000, "days", 4}); +  + private mapping(int:array) oidtotype = ([ +  DATEOID: datetotype, +  TIMEOID: ({Val.Time, 8, 0, "usecs", 8}), +  TIMETZOID: ({Val.TimeTZ, 12, 0, "usecs", 8, "timezone", 4}), +  INTERVALOID: ({Val.Interval, 16, 0, "usecs", 8, "days", 4, "months",4}), +  TIMESTAMPOID: timestamptotype, +  TIMESTAMPTZOID: timestamptotype, +  INT4RANGEOID: ({0, 4}), +  INT8RANGEOID: ({0, 8}), +  DATERANGEOID: datetotype, +  TSRANGEOID: timestamptotype, +  TSTZRANGEOID: timestamptotype, +  ]); +    private inline mixed callout(function(mixed ...:void) f,    float|int delay, mixed ... args) { -  return local_backend->call_out(f, delay, @args); +  return cb_backend->call_out(f, delay, @args);   }      // Some pgsql utility functions      class bufcon {    inherit Stdio.Buffer;    private Thread.ResourceCountKey dirty;      #ifdef PG_DEBUGRACE    final bufcon `chain() {
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:258: Inside #if defined(PG_DEBUG)
   protected final bool range_error(int howmuch) {   #ifdef PG_DEBUG    if (howmuch <= 0)    error("Out of range %d\n", howmuch);   #endif    if (fillread) {    Thread.MutexKey lock = fillreadmux->lock();    if (!didreadcb)    fillread.wait(lock);    didreadcb = 0; -  lock = 0; +     } else    throw(MAGICTERMINATE);    return true;    }       final int read_cb(mixed id, mixed b) {    PD("Read callback %O\n", b && ((string)b)   #ifndef PG_DEBUGMORE    [..255]   #endif    );    Thread.MutexKey lock = fillreadmux->lock();    if (procmsg && id)    procmsg = 0, lock = 0, Thread.Thread(id);    else if (fillread)    didreadcb = 1, fillread.signal(); -  lock = 0; +     return 0;    }       private void create() {    i::create();    fillreadmux = Thread.Mutex();    fillread = Thread.Condition();    }   };   
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:432:    PD("Flush\n");    add(PGFLUSH);    case SENDOUT:;    }    if (towrite = sizeof(this)) {    PD("%d>Sendcmd %O\n",    socket->query_fd(), ((string)this)[..towrite-1]);    towrite -= output_to(socket, towrite);    }    } while (0); -  lock = started = 0; +  started = 0;    return;    };    lock = 0;    PD("Sendcmd failed %s\n", describe_backtrace(err));    destruct(this);    }       final int close() {    if (!closenext && nostash) {    closenext = 1; -  +  {    Thread.MutexKey lock = i->fillreadmux->lock(); -  if (i->fillread) { // Delayed close() after flushing the output buffer +  if (i->fillread) { // Delayed close() after flushing the output buffer    i->fillread.signal();    i->fillread = 0;    } -  lock = 0; +  }    PD("%d>Delayed close, flush write\n", socket->query_fd());    i->read_cb(socket->query_id(), 0);    return 0;    } else    return -1;    }       final void purge() {    if (stashcount) {    stashcount = 0;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:677:    || (_unnamedstatementkey ? "*parsing*" : ""));    break;    }    return res;    }       protected void create(proxy _pgsqlsess, conxion _c, string query,    int _portalbuffersize, int alltyped, array params, int forcetext,    int _timeout, int _syncparse, int _transtype) {    pgsqlsess = _pgsqlsess; -  if (catch(cr = (c = _c)->i)) +  if (c = _c) +  cr = c->i; +  else    losterror();    _query = query;    datarows = Thread.Queue();    _ddescribe = Thread.Condition();    _ddescribemux = Thread.Mutex();    closemux = Thread.Mutex();    portalbuffersize = _portalbuffersize;    alltext = !alltyped;    _params = params;    _forcetext = forcetext;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:724:       final void _storetiming() {    if (_tprepared) {    _tprepared.trun = gethrtime() - _tprepared.trunstart;    m_delete(_tprepared, "trunstart");    _tprepared = 0;    }    }       private void waitfordescribe() { +  {    Thread.MutexKey lock = _ddescribemux->lock();    if (!datarowtypes)    PT(_ddescribe->wait(lock)); -  lock = 0; +  }    if (this) // If object already destructed, skip the next call    trydelayederror(); // since you cannot call functions anymore    else    error(LOSTERROR);    }       //! @seealso    //! @[Sql.Result()->num_fields()]    /*semi*/final int num_fields() {    if (!datarowtypes)
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:809:   #endif    foreach (datarowtypes; int i; int typ) {    int collen = cr->read_sint(4);    if (collen > 0) {   #ifdef PG_DEBUG    msglen -= collen;   #endif    mixed value;    switch (typ) {    case FLOAT4OID: - #if SIZEOF_FLOAT>=8 + #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__)    case FLOAT8OID:   #endif -  +  if (_forcetext) {    if (!alltext) {    value = (float)cr->read(collen);    break;    } -  +  } else { +  [ value ] = cr->sscanf(collen == 4 ? "%4F" : "%8F"); +  if (alltext) +  value = (string)value; +  break; +  }    default:value = cr->read(collen);    break;    case CHAROID:    value = alltext ? cr->read(1) : cr->read_int8();    break;    case BOOLOID:value = cr->read_int8();    switch (value) {    case 'f':value = 0;    break;    case 't':value = 1;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:839:    break;    case TEXTOID:    case BPCHAROID:    case VARCHAROID:    value = cr->read(collen);    if (cenc == UTF8CHARSET && catch(value = utf8_to_string(value))    && !serror)    serror = SERROR("%O contains non-%s characters\n",    value, UTF8CHARSET);    break; +  case INT4RANGEOID: +  case INT8RANGEOID: +  case DATERANGEOID: +  case TSRANGEOID: +  case TSTZRANGEOID: +  if (_forcetext) +  value = cr->read(collen); +  else { +  array totype = oidtotype[typ]; +  mixed from = -Math.inf, till = Math.inf; +  switch (cr->read_int8()) { +  case 1: from = till = 0; +  break; +  case 0x12: from = cr->read_sint(cr->read_int32()); +  break; +  case 2: from = cr->read_sint(cr->read_int32()); +  case 8: till = cr->read_sint(cr->read_int32()); +  } +  if (totype[0]) { +  if (intp(from)) { +  value = totype[0](); +  value[totype[3]] = from + totype[2]; +  from = value; +  } +  if (intp(till)) { +  value = totype[0](); +  value[totype[3]] = till + totype[2]; +  till = value; +  } +  } +  value = Val.Range(from, till); +  if (alltext) +  value = value->sql(); +  } +  break; +  case CIDROID: +  case INETOID: +  if (_forcetext) +  value = cr->read(collen); +  else { +  value = Val.Inet(); +  int iptype = cr->read_int8(); // 2 == IPv4, 3 == IPv6 +  value->masklen = cr->read_int8() + (iptype == 2 && 12*8); +  cr->read_int8(); // 0 == INET, 1 == CIDR +  value->address = cr->read_hint(1); +  if (alltext) +  value = (string)value; +  } +  break; +  case TIMESTAMPOID: +  case TIMESTAMPTZOID: +  case INTERVALOID: +  case TIMETZOID: +  case TIMEOID: +  case DATEOID: +  if (_forcetext) +  value = cr->read(collen); +  else { +  array totype = oidtotype[typ]; +  value = totype[0](); +  value[totype[3]] = cr->read_sint(totype[4]) + totype[2]; +  int i = 5; +  while (i < sizeof(totype)) { +  value[totype[i]] = cr->read_sint(totype[i+1]); +  i += 2; +  } +  if (alltext) +  value = (string)value; +  } +  break;    case INT8OID:case INT2OID:    case OIDOID:case INT4OID:    if (_forcetext) {    value = cr->read(collen);    if (!alltext)    value = (int)value;    } else {    switch (typ) {    case INT8OID:value = cr->read_sint(8);    break;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:876:    return msglen;   #endif    }       final void _setrowdesc(array(mapping(string:mixed)) drowdesc,    array(int) drowtypes) {    Thread.MutexKey lock = _ddescribemux->lock();    datarowdesc = drowdesc;    datarowtypes = drowtypes;    _ddescribe->broadcast(); -  lock = 0; +     }       final void _preparebind(array dtoid) {    array(string|int) paramValues =_params ? _params[2] : emptyarray;    if (sizeof(dtoid) != sizeof(paramValues))    SUSERERROR("Invalid number of bindings, expected %d, got %d\n",    sizeof(dtoid), sizeof(paramValues));    Thread.MutexKey lock = _ddescribemux->lock();    if (!_portalname) {    _portalname
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:900:    + (string)(c->socket->query_fd()) + "_"   #endif    + String.int2hex(pgsqlsess.pportalcount++);    lock = 0;   #ifdef PG_DEBUGMORE    PD("ParamValues to bind: %O\n", paramValues);   #endif    Stdio.Buffer plugbuffer = Stdio.Buffer();    { array dta = ({sizeof(dtoid)});    plugbuffer->add(_portalname, 0, _preparedname, 0) -  ->add_ints(dta + map(dtoid, oidformat) + dta, 2); +  ->add_ints(dta +  + map(dtoid, writeoidformat, paramValues, ({0})) + dta, 2);    }    string cenc = pgsqlsess.runtimeparameter[CLIENT_ENCODING];    foreach (paramValues; int i; mixed value) {    if (undefinedp(value) || objectp(value) && value->is_val_null)    plugbuffer->add_int32(-1); // NULL    else if (stringp(value) && !sizeof(value)) {    int k = 0;    switch (dtoid[i]) {    default:    k = -1; // cast empty strings to NULL for non-string types
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:999:    };    break;    default:    value = 1;    break;    case 0:case '0':case 'f':case 'F':case 'n':case 'N':    value = 0;    break;    }    } while (0); -  plugbuffer->add_int32(1)->add_int8(value); +  plugbuffer->add("\0\0\0\1", value);    break;    case CHAROID:    if (intp(value))    plugbuffer->add_hstring(value, 4);    else {    value = (string)value;    switch (sizeof(value)) {    default:    SUSERERROR(    "\"char\" types must be 1 byte wide, got %O\n", value);    case 0:    plugbuffer->add_int32(-1); // NULL    break;    case 1:    plugbuffer->add_hstring(value[0], 4);    }    }    break; -  +  case INT4RANGEOID: +  case INT8RANGEOID: +  case DATERANGEOID: +  case TSRANGEOID: +  case TSTZRANGEOID: +  if (stringp(value)) +  plugbuffer->add_hstring(value, 4); +  else if (value->from >= value->till) +  plugbuffer->add("\0\0\0\1\1"); +  else { +  array totype = oidtotype[dtoid[i]]; +  int w = totype[1]; +  int from, till; +  if (totype[0]) +  from = value->from, till = value->till; +  else { +  from = value->from[totype[3]] - totype[2]; +  till = value->till[totype[3]] - totype[2]; +  } +  if (value->till == Math.inf) +  if (value->from == -Math.inf) +  plugbuffer->add("\0\0\0\1\30"); +  else +  plugbuffer->add("\0\0\0", 1 + 4 + w, "\22\0\0\0", w) +  ->add_int(from, w); +  else { +  if (value->from == -Math.inf) +  plugbuffer->add("\0\0\0", 1 + 4 + w, 8); +  else +  plugbuffer->add("\0\0\0", 1 + 4 * 2 + w * 2, "\2\0\0\0", w) +  ->add_int(from, w); +  plugbuffer->add_int32(w)->add_int(till, w); +  } +  } +  break; +  case CIDROID: +  case INETOID: +  if (stringp(value)) +  plugbuffer->add_hstring(value, 4); +  else if (value->address <= 0xffffffff) // IPv4 +  plugbuffer->add("\0\0\0\10\2", +  value->masklen - 12 * 8, dtoid[i] == CIDROID, 4) +  ->add_int32(value->address); +  else // IPv6 +  plugbuffer->add("\0\0\0\24\3", +  value->masklen, dtoid[i] == CIDROID, 16) +  ->add_int(value->address, 16); +  break; +  case DATEOID: +  case TIMEOID: +  case TIMETZOID: +  case INTERVALOID: +  case TIMESTAMPOID: +  case TIMESTAMPTZOID: +  if (stringp(value)) +  plugbuffer->add_hstring(value, 4); +  else { +  array totype = oidtotype[dtoid[i]]; +  if (!objectp(value)) +  value = totype[0](value); +  plugbuffer->add_int32(totype[1]) +  ->add_int(value[totype[3]] - totype[2], totype[4]); +  int i = 5; +  while (i < sizeof(totype)) { +  plugbuffer->add_int(value[totype[i]], totype[i+1]); +  i += 2; +  } +  } +  break; +  case FLOAT4OID: + #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__) +  case FLOAT8OID: + #endif +  if (stringp(value)) +  plugbuffer->add_hstring(value, 4); +  else { +  int w = dtoid[i] == FLOAT4OID ? 4 : 8; +  plugbuffer->add_int32(w) +  ->sprintf(w == 4 ? "%4F" : "%8F", value); +  } +  break;    case INT8OID:    plugbuffer->add_int32(8)->add_int((int)value, 8);    break;    case OIDOID:    case INT4OID:    plugbuffer->add_int32(4)->add_int32((int)value);    break;    case INT2OID:    plugbuffer->add_int32(2)->add_int16((int)value);    break;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1040:    if (!datarowtypes) {    if (_tprepared && dontcacheprefix->match(_query))    m_delete(pgsqlsess->prepareds, _query), _tprepared = 0;    waitfordescribe();    }    if (_state >= CLOSING)    lock = _unnamedstatementkey = 0;    else {    plugbuffer->add_int16(sizeof(datarowtypes));    if (sizeof(datarowtypes)) -  plugbuffer->add_ints(map(datarowtypes, oidformat), 2); +  plugbuffer->add_ints(map(datarowtypes, readoidformat), 2);    else if (syncparse < 0 && !pgsqlsess->wasparallelisable    && !pgsqlsess->statementsinflight->drained(1)) {    lock = pgsqlsess->shortmux->lock();    PD("Commit waiting for statements to finish\n");    catch(PT(pgsqlsess->statementsinflight->wait_till_drained(lock, 1)));    }    lock = 0;    PD("Bind portal %O statement %O\n", _portalname, _preparedname);    _fetchlimit = pgsqlsess->_fetchlimit;    _bindportal();    conxsess bindbuffer = c->start();    _unnamedstatementkey = 0; -  +  stmtifkey = 0;    CHAIN(bindbuffer)->add_int8('B')->add_hstring(plugbuffer, 4, 4);    if (!_tprepared && sizeof(_preparedname))    closestatement(CHAIN(bindbuffer), _preparedname);    _sendexecute(_fetchlimit    && !(transtype != NOTRANS    || sizeof(_query) >= MINPREPARELENGTH &&    execfetchlimit->match(_query))    && _fetchlimit, bindbuffer);    } -  } else -  lock = 0; +     } -  +  }       final void _processrowdesc(array(mapping(string:mixed)) datarowdesc,    array(int) datarowtypes) {    _setrowdesc(datarowdesc, datarowtypes);    if (_tprepared) {    _tprepared.datarowdesc = datarowdesc;    _tprepared.datarowtypes = datarowtypes;    }    }       final void _parseportal() { -  +  {    Thread.MutexKey lock = closemux->lock();    _state = PARSING; -  +  {    Thread.MutexKey lockc = pgsqlsess->shortmux->lock();    if (syncparse || syncparse < 0 && pgsqlsess->wasparallelisable) {    PD("Commit waiting for statements to finish\n");    catch(PT(pgsqlsess->statementsinflight->wait_till_drained(lockc)));    }    stmtifkey = pgsqlsess->statementsinflight->acquire(); -  lockc = 0; -  lock = 0; +  } +  }    statuscmdcomplete = 0;    pgsqlsess->wasparallelisable = paralleliseprefix->match(_query);    }       final void _releasestatement(void|int nolock) {    Thread.MutexKey lock;    if (!nolock)    lock = closemux->lock();    if (_state <= BOUND) {    _state = COMMITTED;    stmtifkey = 0;    } -  lock = 0; +     }       final void _bindportal() {    Thread.MutexKey lock = closemux->lock();    _state = BOUND;    portalsifkey = pgsqlsess->portalsinflight->acquire(); -  lock = 0; +     }       final void _purgeportal() {    PD("Purge portal\n");    datarows->write(1); // Signal EOF -  +  {    Thread.MutexKey lock = closemux->lock();    _fetchlimit = 0; // disables further Executes    switch (_state) {    case COPYINPROGRESS:    case COMMITTED:    case BOUND:    portalsifkey = 0;    }    switch (_state) {    case BOUND:    case PARSING:    stmtifkey = 0;    }    _state = PURGED; -  lock = 0; +  }    releaseconditions();    }       final int _closeportal(conxsess cs) {    void|bufcon|conxsess plugbuffer = CHAIN(cs);    int retval = KEEP;    PD("%O Try Closeportal %d\n", _portalname, _state);    Thread.MutexKey lock = closemux->lock();    _fetchlimit = 0; // disables further Executes    switch (_state) {
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1172:    if (plugbuffer->stashcount->drained() && transtype != TRANSBEGIN)    /*    * stashcount will be non-zero if a parse request has been queued    * before the close was initiated.    * It's a bit of a tricky race, but this check should be sufficient.    */    pgsqlsess->readyforquerycount++, retval = SYNCSEND;    pgsqlsess->pportalcount = 0;    }    } -  lock = 0; +     return retval;    }       final void _processdataready(array datarow, void|int msglen) {    bytesreceived += msglen;    inflight--;    if (_state<CLOSED)    datarows->write(datarow);    if (++index == 1)    PD("<%O _fetchlimit %d=min(%d||1,%d), inflight %d\n", _portalname,
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1195:    if (_fetchlimit) {    _fetchlimit =    min((portalbuffersize >> 1) * index / bytesreceived || 1,    pgsqlsess._fetchlimit);    Thread.MutexKey lock = closemux->lock();    if (_fetchlimit && inflight <= (_fetchlimit - 1) >> 1)    _sendexecute(_fetchlimit);    else if (!_fetchlimit)    PD("<%O _fetchlimit %d, inflight %d, skip execute\n",    _portalname, _fetchlimit, inflight); -  lock = 0; +     }    }       private void releaseconditions() {    _unnamedportalkey = _unnamedstatementkey = 0;    if (!datarowtypes) {    if (_state != PURGED && !delayederror)    delayederror = LOSTERROR;    datarowtypes = emptyarray;    _ddescribe->broadcast();
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1248:    if (!fetchlimit) {    if (transtype != NOTRANS)    pgsqlsess.intransaction = transtype == TRANSBEGIN;    flushmode = _closeportal(plugbuffer) == SYNCSEND    || transtype == TRANSEND ? SYNCSEND : FLUSHSEND;    } else    inflight += fetchlimit, flushmode = FLUSHSEND;    plugbuffer->sendcmd(flushmode, this);    }    +  inline private array setuptimeout() { +  return local_backend->call_out(gottimeout, timeout); +  } +     //! @returns    //! One result row at a time.    //!    //! When using COPY FROM STDOUT, this method returns one row at a time    //! as a single string containing the entire row.    //!    //! @seealso    //! @[eof()], @[send_row()]    /*semi*/final array(mixed) fetch_row() {    int|array datarow;    if (arrayp(datarow = datarows->try_read()))    return datarow;    if (!eoffound) {    if (!datarow) {    PD("%O Block for datarow\n", _portalname); -  array cid = callout(gottimeout, timeout); +  array cid = setuptimeout();    PT(datarow = datarows->read());    local_backend->remove_call_out(cid);    if (arrayp(datarow))    return datarow;    }    eoffound = 1;    datarows->write(1); // Signal EOF for other threads    }    trydelayederror();    return 0;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1289:    //! When using COPY FROM STDOUT, this method returns one row at a time    //! as a single string containing the entire row.    //!    //! @seealso    //! @[eof()], @[fetch_row()]    /*semi*/final array(array(mixed)) fetch_row_array() {    if (eoffound)    return 0;    array(array|int) datarow = datarows->try_read_array();    if (!sizeof(datarow)) { -  array cid = callout(gottimeout, timeout); +  array cid = setuptimeout();    PT(datarow = datarows->read_array());    local_backend->remove_call_out(cid);    }    if (arrayp(datarow[-1]))    return datarow;    trydelayederror();    eoffound = 1;    datarows->write(1); // Signal EOF for other threads    return (datarow = datarow[..<1]);    }
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1329:    cs->sendcmd(SENDOUT);    } else    _releasesession();    }       private void run_result_cb(    function(Result, array(mixed), mixed ...:void) callback,    array(mixed) args) {    int|array datarow;    for (;;) { -  array cid = callout(gottimeout, timeout); +  array cid = setuptimeout();    PT(datarow = datarows->read());    local_backend->remove_call_out(cid);    if (!arrayp(datarow))    break;    callout(callback, 0, this, datarow, @args);    }    eoffound = 1;    callout(callback, 0, this, 0, @args);    }   
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1358:    mixed ... args) {    if (callback)    Thread.Thread(run_result_cb, callback, args);    }       private void run_result_array_cb(    function(Result, array(array(mixed)), mixed ...:void) callback,    array(mixed) args) {    array(array|int) datarow;    for (;;) { -  array cid = callout(gottimeout, timeout); +  array cid = setuptimeout();    PT(datarow = datarows->read_array());    local_backend->remove_call_out(cid);    if (!datarow || !arrayp(datarow[-1]))    break;    callout(callback, 0, this, datarow, @args);    }    eoffound = 1;    if (sizeof(datarow)>1)    callout(callback, 0, this, datarow = datarow[..<1], @args);    callout(callback, 0, this, 0, @args);
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1421:    final int warningsdropcount; // Number of uncollected warnings    private int warningscollected;    final int(0..1) invalidatecache;    private Thread.Queue qportals;    final mixed delayederror;    private function (:void) readyforquery_cb;       final string host;    final int(0..65535) port;    final string database, user, pass; -  private Crypto.SCRAM SASLcontext; +  private Crypto.Hash.SCRAM SASLcontext;    final Thread.Condition waitforauthready;    final Thread.Mutex shortmux;    final int readyforquerycount;       private string _sprintf(int type) {    string res;    switch (type) {    case 'O':    res = sprintf(DRIVERNAME".proxy(%s@%s:%d/%s,%d,%d)", -  user, host, port, database, c?->socket && c->socket->query_fd(), +  user, host, port, database, c && c->socket && c->socket->query_fd(),    backendpid);    break;    }    return res;    }       private void create(void|string host, void|string database,    void|string user, void|string pass,    void|mapping(string:mixed) options) {    if (this::pass = pass) {
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1475:    runtimeparameter = ([]);    unnamedportalmux = Thread.Mutex();    unnamedstatement = Thread.Mutex();    readyforquery_cb = connect_cb;    portalsinflight = Thread.ResourceCount();    statementsinflight = Thread.ResourceCount();    wasparallelisable = 0;    }       final int is_open() { -  catch { -  return c->socket->is_open(); -  }; -  return 0; +  return c && c->socket && c->socket->is_open();    }       final string geterror(void|int clear) {    throwdelayederror(this);    untolderror = 0;    string s = lastmessage * "\n";    if (clear)    lastmessage = emptyarray;    warningscollected = 0;    return sizeof(s) && s;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1646:    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++;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:1779:    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); +  SASLcontext = Crypto.SHA256.SCRAM();    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;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:2284:    PD("Terminating processloop due to %s\n", describe_backtrace(err));    delayederror = err;    }    destruct(waitforauthready);    c->purge();    };    }       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)    c->purge(); -  lock = 0; +  }    destruct(waitforauthready);    }       private void _destruct() {    string errstring;    mixed err = catch(close());    backendreg = 0;    if (untolderror) {    /*    * Flush out any asynchronously reported errors to stderr; because we are