ecbab12008-07-27Stephen R. van den Berg /* * Some pgsql utility functions. * They are kept here to avoid circular references. */
ae952f2014-11-10Stephen R. van den Berg //! The pgsql backend, shared between all connection instances.
39e1c42014-11-14Stephen R. van den Berg //! It runs even in non-callback mode in a separate thread and makes sure //! that communication with the database is real-time and event driven //! at all times.
16b8832014-11-10Stephen R. van den Berg //! //! @note //! Callbacks running from this backend directly determine the latency //! in reacting to communication with the database server; so it //! would be prudent not to block in these callbacks.
ae952f2014-11-10Stephen R. van den Berg 
ecbab12008-07-27Stephen R. van den Berg #pike __REAL_VERSION__
5a4c012016-11-08Stephen R. van den Berg #pragma dynamic_dot
5691e42014-11-10Stephen R. van den Berg #require constant(Thread.Thread)
ecbab12008-07-27Stephen R. van den Berg  #include "pgsql.h"
f66d602017-11-18Stephen R. van den Berg #define PORTALINIT 0 // Portal states #define PARSING 1 #define BOUND 2 #define COMMITTED 3 #define COPYINPROGRESS 4 #define CLOSING 5 #define CLOSED 6 #define PURGED 7 #define NOERROR 0 // Error states networkparser #define PROTOCOLERROR 1 #define PROTOCOLUNSUPPORTED 2 #define LOSTERROR "Database connection lost"
62f1ba2018-05-08Stephen R. van den Berg #if PG_DEADLOCK_SENTINEL private multiset mutexes = set_weak_flag((<>), Pike.WEAK); private int deadlockseq; private class MUTEX { inherit Thread.Mutex; private Thread.Thread sentinelthread; private void dump_lockgraph(Thread.Thread curthread) { sleep(PG_DEADLOCK_SENTINEL); String.Buffer buf = String.Buffer(); buf.add("\n{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{\n"); buf.sprintf("Deadlock detected at\n%s\n", describe_backtrace(curthread.backtrace(), -1)); foreach(mutexes; this_program mu; ) if (mu) { Thread.Thread ct; if (ct = mu.current_locking_thread()) buf.sprintf("====================================== Mutex: %O\n%s\n", mu, describe_backtrace(ct.backtrace(), -1)); } buf.add("}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); werror(replace(buf.get(), "\n", sprintf("\n%d> ", deadlockseq++)) + "\n"); } Thread.MutexKey lock(void|int type) { Thread.MutexKey key; if (!(key = trylock(type))) { sentinelthread = Thread.Thread(dump_lockgraph, this_thread()); key = ::lock(type); sentinelthread->kill(); } return key; } protected void create() { //::create(); mutexes[this] = 1; } }; #else #define MUTEX Thread.Mutex #endif
16b8832014-11-10Stephen R. van den Berg //! The instance of the pgsql dedicated backend.
1cfbdc2017-12-02Stephen R. van den Berg final Pike.Backend local_backend;
16b8832014-11-10Stephen R. van den Berg 
1cfbdc2017-12-02Stephen R. van den Berg private Pike.Backend cb_backend;
62f1ba2018-05-08Stephen R. van den Berg private Result qalreadyprinted;
8384822014-11-12Stephen R. van den Berg private Thread.Mutex backendmux = Thread.Mutex();
3fbf572018-12-16Stephen R. van den Berg final multiset(proxy) clients = set_weak_flag((<>), Pike.WEAK);
16ce8b2014-09-12Stephen R. van den Berg 
3d6cb02020-02-23Stephen R. van den Berg mapping describenodata = (["datarowdesc":({}), "datarowtypes":({}), "datatypeoid":({})]); private multiset censoroptions = (<"use_ssl", "force_ssl",
f66d602017-11-18Stephen R. van den Berg  "cache_autoprepared_statements", "reconnect", "text_query", "is_superuser", "server_encoding", "server_version", "integer_datetimes",
b840fa2014-11-19Stephen R. van den Berg  "session_authorization">);
609b782014-11-26Stephen R. van den Berg  /* Statements matching createprefix cause the prepared statement cache * to be flushed to prevent stale references to (temporary) tables */
f66d602017-11-18Stephen R. van den Berg final Regexp createprefix = iregexp("^\a*(CREATE|DROP)\a");
609b782014-11-26Stephen R. van den Berg  /* Statements matching dontcacheprefix never enter the cache */
f66d602017-11-18Stephen R. van den Berg private Regexp dontcacheprefix = iregexp("^\a*(FETCH|COPY)\a");
609b782014-11-26Stephen R. van den Berg  /* Statements not matching paralleliseprefix will cause the driver * to stall submission until all previously started statements have * run to completion */
c138a02014-11-26Stephen R. van den Berg private Regexp paralleliseprefix
f66d602017-11-18Stephen R. van den Berg  = iregexp("^\a*((SELEC|INSER)T|(UPDA|DELE)TE|(FETC|WIT)H)\a");
609b782014-11-26Stephen R. van den Berg 
ac15032020-05-18Stephen R. van den Berg  /* Statements matching transbeginprefix will cause the driver to
55a08b2017-06-25Stephen R. van den Berg  * insert a sync after the statement. * Failure to do so, will result in portal synchronisation errors * in the event of an ErrorResponse. */ final Regexp transbeginprefix
f66d602017-11-18Stephen R. van den Berg  = iregexp("^\a*(BEGIN|START)([; \t\f\r\n]|$)");
55a08b2017-06-25Stephen R. van den Berg 
ac15032020-05-18Stephen R. van den Berg  /* Statements matching transendprefix will cause the driver to
55a08b2017-06-25Stephen R. van den Berg  * insert a sync after the statement. * Failure to do so, will result in portal synchronisation errors * in the event of an ErrorResponse. */ final Regexp transendprefix
f66d602017-11-18Stephen R. van den Berg  = iregexp("^\a*(COMMIT|ROLLBACK|END)([; \t\f\r\n]|$)");
55a08b2017-06-25Stephen R. van den Berg 
ac15032020-05-18Stephen R. van den Berg  /* Statements matching nodataresprefix will cause the driver * to skip asking for a query description and query results, since there * are no arguments or results to begin with. */ final Regexp nodataresprefix = iregexp("^\a*(RESET|CLOSE|DISCARD)\a");
609b782014-11-26Stephen R. van den Berg  /* For statements matching execfetchlimit the resultrows will not be
8828332017-07-28Stephen R. van den Berg  * fetched in pieces. This heuristic will be sub-optimal whenever * either an UPDATE/DELETE/INSERT statement is prefixed by WITH, or * if there is a RETURNING with a *lot* of results. In those cases * the portal will be busy until all results have been fetched, and will * 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.
609b782014-11-26Stephen R. van den Berg  */
8384822014-11-12Stephen R. van den Berg private Regexp execfetchlimit
ac15032020-05-18Stephen R. van den Berg  = iregexp("^\a*((UPDA|DELE)TE|INSERT|CREATE|DROP"
acc6dd2020-05-14Stephen R. van den Berg  "|RESET|CLOSE|DISCARD)\a|\aLIMIT\a+[1-9][; \t\f\r\n]*$");
c138a02014-11-26Stephen R. van den Berg 
1cfbdc2017-12-02Stephen R. van den Berg private void default_backend_runs() { // Runs as soon as the cb_backend = Pike.DefaultBackend; // DefaultBackend has started }
83fabd2019-04-17Henrik Grubbström (Grubba) protected void create() {
3fbf572018-12-16Stephen R. van den Berg  atexit(_destruct);
1cfbdc2017-12-02Stephen R. van den Berg  // Run callbacks from our local_backend until DefaultBackend has started cb_backend = local_backend = Pike.SmallBackend(); call_out(default_backend_runs, 0); }
83fabd2019-04-17Henrik Grubbström (Grubba) protected void _destruct() {
3fbf572018-12-16Stephen R. van den Berg  foreach (clients; proxy client; ) destruct(client); }
c138a02014-11-26Stephen R. van den Berg private Regexp iregexp(string expr) {
f66d602017-11-18Stephen R. van den Berg  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
c138a02014-11-26Stephen R. van den Berg  ret->add("[ \t\f\r\n]"); else ret->add_int8(c); return Regexp(ret->read()); }
615d422014-10-31Stephen R. van den Berg 
f66d602017-11-18Stephen R. van den Berg final void closestatement(bufcon|conxsess plugbuffer, string oldprep) { if (oldprep) { PD("Close statement %s\n", oldprep);
c02c1d2017-06-03Stephen R. van den Berg  CHAIN(plugbuffer)->add_int8('C')->add_hstring(({'S', oldprep, 0}), 4, 4);
615d422014-10-31Stephen R. van den Berg  } }
8384822014-11-12Stephen R. van den Berg private void run_local_backend() {
16ce8b2014-09-12Stephen R. van den Berg  Thread.MutexKey lock; int looponce; do {
f66d602017-11-18Stephen R. van den Berg  looponce = 0; if (lock = backendmux->trylock()) {
16ce8b2014-09-12Stephen R. van den Berg  PD("Starting local backend\n");
3fbf572018-12-16Stephen R. van den Berg  while (sizeof(clients) // Autoterminate when not needed
e1963e2017-11-15Stephen R. van den Berg  || sizeof(local_backend->call_out_info())) {
cbac2f2016-10-12Stephen R. van den Berg  mixed err; if (err = catch(local_backend(4096.0)))
1a2da92017-11-30Stephen R. van den Berg  master()->handle_error(err);
cbac2f2016-10-12Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  PD("Terminating local backend\n");
f66d602017-11-18Stephen R. van den Berg  lock = 0;
3fbf572018-12-16Stephen R. van den Berg  looponce = sizeof(clients);
16ce8b2014-09-12Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  } while (looponce);
16ce8b2014-09-12Stephen R. van den Berg }
ecbab12008-07-27Stephen R. van den Berg 
16b8832014-11-10Stephen R. van den Berg //! Registers yourself as a user of this backend. If the backend //! has not been started yet, it will be spawned automatically.
3fbf572018-12-16Stephen R. van den Berg final void register_backend(proxy client) { int startbackend = !sizeof(clients); clients[client] = 1;
67879d2017-11-10Stephen R. van den Berg  if (startbackend)
16ce8b2014-09-12Stephen R. van den Berg  Thread.Thread(run_local_backend); }
4741cd2008-07-31Stephen R. van den Berg 
63fcae2017-11-22Stephen R. van den Berg final void throwdelayederror(Result|proxy parent) {
f66d602017-11-18Stephen R. van den Berg  if (mixed err = parent->delayederror) { if (!objectp(parent->pgsqlsess)) parent->untolderror = 0; else if (parent->pgsqlsess)
3d6e3c2020-02-22Stephen R. van den Berg  parent->pgsqlsess->untolderror = parent->pgsqlsess->delayederror = 0; parent->delayederror = 0;
f66d602017-11-18Stephen R. van den Berg  if (stringp(err)) err = ({err, backtrace()[..<2]});
16ce8b2014-09-12Stephen R. van den Berg  throw(err);
ecbab12008-07-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg }
ecbab12008-07-27Stephen R. van den Berg 
01b8152017-12-06Stephen R. van den Berg private int readoidformat(int oid) {
f66d602017-11-18Stephen R. van den Berg  switch (oid) {
615d422014-10-31Stephen R. van den Berg  case BOOLOID: case BYTEAOID: case CHAROID: case INT8OID: case INT2OID: case INT4OID:
a469192017-12-11Stephen R. van den Berg  case FLOAT4OID: #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__) case FLOAT8OID: #endif
4855e22018-02-05Stephen R. van den Berg  case NUMERICOID:
615d422014-10-31Stephen R. van den Berg  case TEXTOID: case OIDOID: case XMLOID:
01b8152017-12-06Stephen R. van den Berg  case DATEOID: case TIMEOID: case TIMETZOID: case TIMESTAMPOID: case TIMESTAMPTZOID: case INTERVALOID:
7d57482017-12-11Stephen R. van den Berg  case INT4RANGEOID: case INT8RANGEOID: case DATERANGEOID: case TSRANGEOID: case TSTZRANGEOID:
615d422014-10-31Stephen R. van den Berg  case MACADDROID: case BPCHAROID: case VARCHAROID:
adfd4e2017-12-07Stephen R. van den Berg  case CIDROID: case INETOID:
615d422014-10-31Stephen R. van den Berg  case CTIDOID: case UUIDOID: return 1; //binary } return 0; // text }
01b8152017-12-06Stephen R. van den Berg 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
4855e22018-02-05Stephen R. van den Berg  case NUMERICOID:
adfd4e2017-12-07Stephen R. van den Berg  case CIDROID: case INETOID:
01b8152017-12-06Stephen R. van den Berg  case DATEOID: case TIMEOID: case TIMETZOID: case TIMESTAMPOID: case TIMESTAMPTZOID: case INTERVALOID:
7d57482017-12-11Stephen R. van den Berg  case INT4RANGEOID: case INT8RANGEOID: case DATERANGEOID: case TSRANGEOID: case TSTZRANGEOID:
a469192017-12-11Stephen R. van den Berg  case FLOAT4OID: #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__) case FLOAT8OID: #endif
01b8152017-12-06Stephen R. van den Berg  if (!stringp(value)) return 1; } return 0; // text }
c44ed92017-12-13Stephen R. van den Berg #define DAYSEPOCHTO2000 10957 // 2000/01/01 00:00:00 UTC #define USEPOCHTO2000 (DAYSEPOCHTO2000*24*3600*1000000) private array timestamptotype
3794dd2017-12-21Martin Nilsson  = ({Val.Timestamp, 8, USEPOCHTO2000, "usecs", 8}); private array datetotype = ({Val.Date, 4, DAYSEPOCHTO2000, "days", 4});
01b8152017-12-06Stephen R. van den Berg  private mapping(int:array) oidtotype = ([
7d57482017-12-11Stephen R. van den Berg  DATEOID: datetotype,
3794dd2017-12-21Martin Nilsson  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}),
7d57482017-12-11Stephen R. van den Berg  TIMESTAMPOID: timestamptotype, TIMESTAMPTZOID: timestamptotype, INT4RANGEOID: ({0, 4}), INT8RANGEOID: ({0, 8}), DATERANGEOID: datetotype, TSRANGEOID: timestamptotype, TSTZRANGEOID: timestamptotype,
01b8152017-12-06Stephen R. van den Berg  ]);
8384822014-11-12Stephen R. van den Berg private inline mixed callout(function(mixed ...:void) f,
f66d602017-11-18Stephen R. van den Berg  float|int delay, mixed ... args) {
1cfbdc2017-12-02Stephen R. van den Berg  return cb_backend->call_out(f, delay, @args);
2c98002014-11-10Stephen R. van den Berg }
ae952f2014-11-10Stephen R. van den Berg // Some pgsql utility functions
ecbab12008-07-27Stephen R. van den Berg 
39e1c42014-11-14Stephen R. van den Berg class bufcon {
16ce8b2014-09-12Stephen R. van den Berg  inherit Stdio.Buffer;
67879d2017-11-10Stephen R. van den Berg  private Thread.ResourceCountKey dirty;
ecbab12008-07-27Stephen R. van den Berg 
c02c1d2017-06-03Stephen R. van den Berg #ifdef PG_DEBUGRACE final bufcon `chain() { return this; } #endif
39e1c42014-11-14Stephen R. van den Berg  private conxion realbuffer;
ecbab12008-07-27Stephen R. van den Berg 
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void create(conxion _realbuffer) {
f66d602017-11-18Stephen R. van den Berg  realbuffer = _realbuffer;
ecbab12008-07-27Stephen R. van den Berg  }
d432822008-07-31Stephen R. van den Berg 
67879d2017-11-10Stephen R. van den Berg  final Thread.ResourceCount `stashcount() {
44de4f2016-10-15Stephen R. van den Berg  return realbuffer->stashcount; }
acc6dd2020-05-14Stephen R. van den Berg  final MUTEX `shortmux() { return realbuffer->shortmux; } final bufcon start(void|int|array(Thread.MutexKey) waitforreal) { dirty = stashcount->acquire();
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (waitforreal)
39e1c42014-11-14Stephen R. van den Berg  error("pgsql.bufcon not allowed here\n");
d432822008-07-31Stephen R. van den Berg #endif
16ce8b2014-09-12Stephen R. van den Berg  return this; }
63fcae2017-11-22Stephen R. van den Berg  final void sendcmd(int mode, void|Result portal) {
acc6dd2020-05-14Stephen R. van den Berg  Thread.MutexKey lock = shortmux->lock();
aac2972017-07-03Stephen R. van den Berg  if (portal)
16ce8b2014-09-12Stephen R. van den Berg  realbuffer->stashqueue->write(portal);
aac2972017-07-03Stephen R. van den Berg  if (mode == SYNCSEND) { add(PGSYNC); realbuffer->stashqueue->write(1); mode = SENDOUT; // Demote it to prevent an extra SYNC upon stashflush
b1fe662017-06-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  realbuffer->stash->add(this);
aac2972017-07-03Stephen R. van den Berg  PD("%d>Stashed mode %d > %d\n",
a123c42017-07-01Stephen R. van den Berg  realbuffer->socket->query_fd(), mode, realbuffer->stashflushmode); if (mode > realbuffer->stashflushmode) realbuffer->stashflushmode = mode;
acc6dd2020-05-14Stephen R. van den Berg  dirty = 0; // Countdown before releasing the lock
79b54f2018-05-22Stephen R. van den Berg  lock = 0;
16ce8b2014-09-12Stephen R. van den Berg  this->clear();
f66d602017-11-18Stephen R. van den Berg  if (lock = realbuffer->nostash->trylock(1)) {
c02c1d2017-06-03Stephen R. van den Berg #ifdef PG_DEBUGRACE conxsess sess = conxsess(realbuffer); realbuffer->started = lock; lock = 0; sess->sendcmd(SENDOUT); #else realbuffer->started = lock; lock = 0;
2eef0b2014-11-15Stephen R. van den Berg  realbuffer->sendcmd(SENDOUT);
c02c1d2017-06-03Stephen R. van den Berg #endif
16ce8b2014-09-12Stephen R. van den Berg  }
d432822008-07-31Stephen R. van den Berg  }
e7c5c82014-11-24Stephen R. van den Berg };
ecbab12008-07-27Stephen R. van den Berg 
c7de862014-11-20Stephen R. van den Berg class conxiin {
16ce8b2014-09-12Stephen R. van den Berg  inherit Stdio.Buffer:i;
c7de862014-11-20Stephen R. van den Berg  final Thread.Condition fillread;
62f1ba2018-05-08Stephen R. van den Berg  final MUTEX fillreadmux;
7f73f42014-12-16Stephen R. van den Berg  final int procmsg;
7607f52014-11-25Stephen R. van den Berg  private int didreadcb;
c7de862014-11-20Stephen R. van den Berg 
180df82018-05-18Stephen R. van den Berg #if PG_DEBUGHISTORY > 0 final array history = ({}); final int(-1..) input_from(Stdio.Stream stm, void|int(0..) nbytes) { int oldsize = sizeof(this); int ret = i::input_from(stm, nbytes); if (ret) { Stdio.Buffer tb = Stdio.Buffer(this); tb->consume(oldsize); history += ({"<<"+tb->read(ret)}); history = history[<PG_DEBUGHISTORY - 1 ..]; } return ret; } #endif
67879d2017-11-10Stephen R. van den Berg  protected final bool range_error(int howmuch) {
c7de862014-11-20Stephen R. van den Berg #ifdef PG_DEBUG
6268a62018-05-17Stephen R. van den Berg  if (howmuch < 0) { int available = unread(0); unread(available); error("Out of range %d %O %O\n", howmuch, ((string)this)[.. available-1], ((string)this)[available ..]); }
c7de862014-11-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  if (fillread) { Thread.MutexKey lock = fillreadmux->lock(); if (!didreadcb)
0c69072014-11-25Stephen R. van den Berg  fillread.wait(lock);
f66d602017-11-18Stephen R. van den Berg  didreadcb = 0;
c7de862014-11-20Stephen R. van den Berg  } else throw(MAGICTERMINATE); return true; }
f66d602017-11-18Stephen R. van den Berg  final int read_cb(mixed id, mixed b) {
b13bac2017-07-01Stephen R. van den Berg  PD("Read callback %O\n", b && ((string)b)
9d0a682016-10-12Stephen R. van den Berg #ifndef PG_DEBUGMORE [..255] #endif );
f66d602017-11-18Stephen R. van den Berg  Thread.MutexKey lock = fillreadmux->lock(); if (procmsg && id) procmsg = 0, lock = 0, Thread.Thread(id); else if (fillread) didreadcb = 1, fillread.signal();
c7de862014-11-20Stephen R. van den Berg  return 0; }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void create() {
c7de862014-11-20Stephen R. van den Berg  i::create();
62f1ba2018-05-08Stephen R. van den Berg  fillreadmux = MUTEX();
f66d602017-11-18Stephen R. van den Berg  fillread = Thread.Condition();
c7de862014-11-20Stephen R. van den Berg  }
e7c5c82014-11-24Stephen R. van den Berg };
c7de862014-11-20Stephen R. van den Berg 
0282f92017-10-29Stephen R. van den Berg class sfile { inherit Stdio.File;
67879d2017-11-10Stephen R. van den Berg  final int query_fd() {
0282f92017-10-29Stephen R. van den Berg  return is_open() ? ::query_fd() : -1; } };
c7de862014-11-20Stephen R. van den Berg class conxion {
16ce8b2014-09-12Stephen R. van den Berg  inherit Stdio.Buffer:o;
c7de862014-11-20Stephen R. van den Berg  final conxiin i;
16ce8b2014-09-12Stephen R. van den Berg 
8384822014-11-12Stephen R. van den Berg  private Thread.Queue qportals;
62f1ba2018-05-08Stephen R. van den Berg  final MUTEX shortmux;
dcc15c2016-02-27Stephen R. van den Berg  private int closenext;
da3dc82016-02-24Stephen R. van den Berg 
f66d602017-11-18Stephen R. van den Berg  final sfile #if constant(SSL.File) |SSL.File #endif socket;
63fcae2017-11-22Stephen R. van den Berg  final multiset(Result) runningportals = (<>);
16ce8b2014-09-12Stephen R. van den Berg 
62f1ba2018-05-08Stephen R. van den Berg  final MUTEX nostash;
3c93de2014-11-03Stephen R. van den Berg  final Thread.MutexKey started; final Thread.Queue stashqueue; final Stdio.Buffer stash;
6534f92018-01-20Stephen R. van den Berg  //! @ignore final int(KEEP..SYNCSEND) stashflushmode; //! @endignore
67879d2017-11-10Stephen R. van den Berg  final Thread.ResourceCount stashcount;
3c93de2014-11-03Stephen R. van den Berg  final int synctransact;
c02c1d2017-06-03Stephen R. van den Berg #ifdef PG_DEBUGRACE final mixed nostrack; #endif
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
3c93de2014-11-03Stephen R. van den Berg  final int queueoutidx;
f66d602017-11-18Stephen R. van den Berg  final int queueinidx = -1;
ecbab12008-07-27Stephen R. van den Berg #endif
180df82018-05-18Stephen R. van den Berg #if PG_DEBUGHISTORY > 0
20d1fa2020-02-25Stephen R. van den Berg  final int(-1..) output_to(Stdio.Stream stm, void|int(0..) nbytes) {
180df82018-05-18Stephen R. van den Berg  Stdio.Buffer tb = Stdio.Buffer(this);
20d1fa2020-02-25Stephen R. van den Berg  int ret = o::output_to(stm, nbytes);
180df82018-05-18Stephen R. van den Berg  if (ret) { i->history += ({">>" + tb->read(ret)}); i->history = i->history[<PG_DEBUGHISTORY - 1 ..]; }
20d1fa2020-02-25Stephen R. van den Berg  return ret;
c3de6f2020-02-18Stephen R. van den Berg  }
20d1fa2020-02-25Stephen R. van den Berg #endif
180df82018-05-18Stephen R. van den Berg 
63fcae2017-11-22Stephen R. van den Berg  private inline void queueup(Result portal) {
f66d602017-11-18Stephen R. van den Berg  qportals->write(portal); portal->_synctransact = synctransact; PD("%d>%O %d %d Queue portal %d bytes\n", socket->query_fd(), portal._portalname, ++queueoutidx, synctransact, sizeof(this));
3e8df22014-10-29Stephen R. van den Berg  }
acc6dd2020-05-14Stephen R. van den Berg  final bufcon|conxsess start(void|int|array(Thread.MutexKey) waitforreal) {
16ce8b2014-09-12Stephen R. van den Berg  Thread.MutexKey lock;
1616b22018-05-07Stephen R. van den Berg #ifdef PG_DEBUGRACE if (nostash->current_locking_thread()) PD("Nostash locked by %s\n", describe_backtrace(nostash->current_locking_thread()->backtrace())); #endif
acc6dd2020-05-14Stephen R. van den Berg  while (lock = (intp(waitforreal) && waitforreal > 0 ? nostash->lock : nostash->trylock)(1)) {
337b8d2018-05-22Stephen R. van den Berg  int mode; if (sizeof(stash) && (mode = getstash(KEEP)) > KEEP)
e7efba2018-05-26Stephen R. van den Berg  sendcmd(mode); // Force out stash to the server
fbb0b12018-06-02Stephen R. van den Berg  if (!stashcount->drained()) { lock = 0; // Unlock while we wait
acc6dd2020-05-14Stephen R. van den Berg  if (arrayp(waitforreal)) waitforreal[0] = 0;
fbb0b12018-06-02Stephen R. van den Berg  stashcount->wait_till_drained();
acc6dd2020-05-14Stephen R. van den Berg  if (arrayp(waitforreal)) return 0;
fbb0b12018-06-02Stephen R. van den Berg  continue; // Try again } #ifdef PG_DEBUGRACE conxsess sess = conxsess(this); #endif
e7efba2018-05-26Stephen R. van den Berg  started = lock; // sendcmd() clears started, so delay assignment
c02c1d2017-06-03Stephen R. van den Berg #ifdef PG_DEBUGRACE return sess; #else
16ce8b2014-09-12Stephen R. van den Berg  return this;
c02c1d2017-06-03Stephen R. van den Berg #endif
16ce8b2014-09-12Stephen R. van den Berg  }
acc6dd2020-05-14Stephen R. van den Berg  if (arrayp(waitforreal)) waitforreal[0] = 0;
a809712019-11-01Stephen R. van den Berg  return !waitforreal && bufcon(this)->start();
ecbab12008-07-27Stephen R. van den Berg  }
8384822014-11-12Stephen R. van den Berg  private int write_cb() {
8e64d32016-10-14Stephen R. van den Berg  Thread.MutexKey lock = shortmux->lock();
cfcb4c2016-10-15Stephen R. van den Berg  if (this) { // Guard against async destructs
8115e72020-02-25Stephen R. van den Berg  output_to(socket);
cfcb4c2016-10-15Stephen R. van den Berg  lock = 0; if (!i->fillread && !sizeof(this)) close(); }
16ce8b2014-09-12Stephen R. van den Berg  return 0; }
b1fe662017-06-27Stephen R. van den Berg  private int getstash(int mode) {
79b54f2018-05-22Stephen R. van den Berg  Thread.MutexKey lock = shortmux->lock(); add(stash); stash->clear(); foreach (stashqueue->try_read_array(); ; int|Result portal) if (intp(portal)) qportals->write(synctransact++); else queueup(portal); PD("%d>Got stash mode %d > %d\n", socket->query_fd(), stashflushmode, mode); if (stashflushmode > mode) mode = stashflushmode; stashflushmode = KEEP;
b1fe662017-06-27Stephen R. van den Berg  return mode; }
63fcae2017-11-22Stephen R. van den Berg  final void sendcmd(void|int mode, void|Result portal) {
b1fe662017-06-27Stephen R. van den Berg  if (portal) queueup(portal); unfinalised:
35dc282014-11-13Stephen R. van den Berg  do {
4c965a2016-10-13Stephen R. van den Berg  switch (mode) {
35dc282014-11-13Stephen R. van den Berg  default:
b1fe662017-06-27Stephen R. van den Berg  break unfinalised;
2eef0b2014-11-15Stephen R. van den Berg  case SYNCSEND:
4f2ed82014-11-15Stephen R. van den Berg  PD("%d>Sync %d %d Queue\n",
4c965a2016-10-13Stephen R. van den Berg  socket->query_fd(), synctransact, ++queueoutidx);
35dc282014-11-13Stephen R. van den Berg  add(PGSYNC);
4c965a2016-10-13Stephen R. van den Berg  mode = SENDOUT;
35dc282014-11-13Stephen R. van den Berg  break;
2eef0b2014-11-15Stephen R. van den Berg  case FLUSHLOGSEND:
86ee0d2020-05-18Stephen R. van den Berg  PD("%d>%d Queue simplequery %d bytes\n", socket->query_fd(), ++queueoutidx, sizeof(this));
4c965a2016-10-13Stephen R. van den Berg  mode = FLUSHSEND;
35dc282014-11-13Stephen R. van den Berg  } qportals->write(synctransact++);
f66d602017-11-18Stephen R. van den Berg  } while (0);
79b54f2018-05-22Stephen R. van den Berg  if (sizeof(stash)) mode = getstash(mode);
e4ff9f2018-05-23Stephen R. van den Berg #ifdef PG_DEBUG mixed err; #endif
79b54f2018-05-22Stephen R. van den Berg  for(;;) {
f66d602017-11-18Stephen R. van den Berg #ifdef PG_DEBUG
e4ff9f2018-05-23Stephen R. van den Berg  err =
f66d602017-11-18Stephen R. van den Berg #endif
79b54f2018-05-22Stephen R. van den Berg  catch {
3e8df22014-10-29Stephen R. van den Berg outer:
79b54f2018-05-22Stephen R. van den Berg  do { switch (mode) { default: PD("%d>Skip flush %d Queue %O\n", socket->query_fd(), mode, (string)this); break outer; case FLUSHSEND: PD("Flush\n"); add(PGFLUSH); case SENDOUT:; }
5e560a2020-02-17Stephen R. van den Berg  Thread.MutexKey lock = shortmux->trylock();
8115e72020-02-25Stephen R. van den Berg  if (lock && sizeof(this)) {
acc6dd2020-05-14Stephen R. van den Berg  PD("%d>Sendcmd %O\n", socket->query_fd(), (string)this);
8115e72020-02-25Stephen R. van den Berg  output_to(socket);
79b54f2018-05-22Stephen R. van den Berg  } } while (0); started = 0;
e7efba2018-05-26Stephen R. van den Berg  if (sizeof(stash) && (started = nostash->trylock(2))) {
79b54f2018-05-22Stephen R. van den Berg #ifdef PG_DEBUGRACE conxsess sess = conxsess(this); sess->sendcmd(SENDOUT); #else mode = getstash(SENDOUT); continue; #endif
3c93de2014-11-03Stephen R. van den Berg  }
79b54f2018-05-22Stephen R. van den Berg  return; }; break; } started = 0;
f66d602017-11-18Stephen R. van den Berg  PD("Sendcmd failed %s\n", describe_backtrace(err));
4b055c2017-11-17Stephen R. van den Berg  destruct(this);
ecbab12008-07-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  final int close() {
f66d602017-11-18Stephen R. van den Berg  if (!closenext && nostash) { closenext = 1;
81790f2017-11-28Stephen R. van den Berg  { Thread.MutexKey lock = i->fillreadmux->lock(); if (i->fillread) { // Delayed close() after flushing the output buffer i->fillread.signal(); i->fillread = 0; }
b95bf72016-02-17Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  PD("%d>Delayed close, flush write\n", socket->query_fd()); i->read_cb(socket->query_id(), 0);
32857b2016-02-28Stephen R. van den Berg  return 0; } else return -1;
ecbab12008-07-27Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  final void purge() { if (stashcount) { stashcount = 0; PD("%d>Purge conxion %d\n", socket ? socket->query_fd() : -1, !!nostash);
63fcae2017-11-22Stephen R. van den Berg  int|Result portal;
f66d602017-11-18Stephen R. van den Berg  if (qportals) // CancelRequest does not use qportals while (portal = qportals->try_read()) if (objectp(portal)) portal->_purgeportal(); if (nostash) { while (sizeof(runningportals)) catch {
63fcae2017-11-22Stephen R. van den Berg  foreach (runningportals; Result result; )
f66d602017-11-18Stephen R. van den Berg  if (!result.datarowtypes) {
3d6cb02020-02-23Stephen R. van den Berg  result.datarowtypes = ({});
f66d602017-11-18Stephen R. van den Berg  if (result._state != PURGED && !result.delayederror) result.delayederror = LOSTERROR; result._ddescribe->broadcast(); runningportals[result] = 0; } else destruct(result); };
32857b2016-02-28Stephen R. van den Berg  destruct(nostash);
21efd72019-05-29Stephen R. van den Berg  if (socket->set_non_blocking) socket->set_non_blocking(); // Drop all callbacks
f66d602017-11-18Stephen R. van den Berg  PD("%d>Close socket\n", socket->query_fd());
57fdb22017-11-21Stephen R. van den Berg  socket->close(); // This will be an asynchronous close
f66d602017-11-18Stephen R. van den Berg  } destruct(this);
32857b2016-02-28Stephen R. van den Berg  }
ecbab12008-07-27Stephen R. van den Berg  }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void _destruct() {
f66d602017-11-18Stephen R. van den Berg  PD("%d>Close conxion %d\n", socket ? socket->query_fd() : -1, !!nostash); catch(purge()); } final void connectloop(proxy pgsqlsess, int nossl) {
4b7cf52017-11-21Stephen R. van den Berg #ifdef PG_DEBUG mixed err = #endif catch {
f66d602017-11-18Stephen R. van den Berg  for (; ; clear()) { socket->connect(pgsqlsess. host, pgsqlsess. port);
fc7f092014-06-01Martin Nilsson #if constant(SSL.File)
f66d602017-11-18Stephen R. van den Berg  if (!nossl && !pgsqlsess->nossl && (pgsqlsess.options.use_ssl || pgsqlsess.options.force_ssl)) {
16ce8b2014-09-12Stephen R. van den Berg  PD("SSLRequest\n");
f66d602017-11-18Stephen R. van den Berg  start()->add_int32(8)->add_int32(PG_PROTOCOL(1234, 5679))
2eef0b2014-11-15Stephen R. van den Berg  ->sendcmd(SENDOUT);
17cca22017-11-21Stephen R. van den Berg  string s = socket.read(1); switch (sizeof(s) && s[0]) {
16ce8b2014-09-12Stephen R. van den Berg  case 'S':
17cca22017-11-21Stephen R. van den Berg  SSL.File fcon = SSL.File(socket, SSL.Context());
f66d602017-11-18Stephen R. van den Berg  if (fcon->connect()) { socket->set_backend(local_backend); socket = fcon;
16ce8b2014-09-12Stephen R. van den Berg  break; }
8469442016-12-06Stephen R. van den Berg  default: PD("%d>Close socket short\n", socket->query_fd());
4f2ed82014-11-15Stephen R. van den Berg  socket->close();
f66d602017-11-18Stephen R. van den Berg  pgsqlsess.nossl = 1;
16ce8b2014-09-12Stephen R. van den Berg  continue; case 'N':
f66d602017-11-18Stephen R. van den Berg  if (pgsqlsess.options.force_ssl)
16ce8b2014-09-12Stephen R. van den Berg  error("Encryption not supported on connection to %s:%d\n",
f66d602017-11-18Stephen R. van den Berg  pgsqlsess.host, pgsqlsess.port);
16ce8b2014-09-12Stephen R. van den Berg  } } #else
f66d602017-11-18Stephen R. van den Berg  if (pgsqlsess.options.force_ssl)
16ce8b2014-09-12Stephen R. van den Berg  error("Encryption library missing," " cannot establish connection to %s:%d\n",
f66d602017-11-18Stephen R. van den Berg  pgsqlsess.host, pgsqlsess.port);
16ce8b2014-09-12Stephen R. van den Berg #endif break; }
f66d602017-11-18Stephen R. van den Berg  if (!socket->is_open()) error(strerror(socket->errno()) + ".\n");
16ce8b2014-09-12Stephen R. van den Berg  socket->set_backend(local_backend);
f66d602017-11-18Stephen R. van den Berg  socket->set_buffer_mode(i, 0); socket->set_nonblocking(i->read_cb, write_cb, close);
4b055c2017-11-17Stephen R. van den Berg  if (nossl != 2)
f66d602017-11-18Stephen R. van den Berg  Thread.Thread(pgsqlsess->processloop, this);
16ce8b2014-09-12Stephen R. van den Berg  return; };
4b7cf52017-11-21Stephen R. van den Berg  PD("Connect error %s\n", describe_backtrace(err));
f66d602017-11-18Stephen R. van den Berg  catch(destruct(pgsqlsess->waitforauthready));
4b055c2017-11-17Stephen R. van den Berg  destruct(this);
4808742014-11-14Stephen R. van den Berg  }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected string _sprintf(int type) {
f66d602017-11-18Stephen R. van den Berg  string res; switch (type) {
4808742014-11-14Stephen R. van den Berg  case 'O':
f66d602017-11-18Stephen R. van den Berg  int fd = -1; if (socket) catch(fd = socket->query_fd()); res = predef::sprintf("conxion fd: %d input queue: %d/%d "
c02c1d2017-06-03Stephen R. van den Berg  "queued portals: %d output queue: %d/%d\n"
67879d2017-11-10Stephen R. van den Berg  "started: %d\n",
f66d602017-11-18Stephen R. van den Berg  fd, sizeof(i), i->_size_object(),
e288132017-07-01Stephen R. van den Berg  qportals && qportals->size(), sizeof(this), _size_object(),
67879d2017-11-10Stephen R. van den Berg  !!started);
4808742014-11-14Stephen R. van den Berg  break; } return res;
16ce8b2014-09-12Stephen R. van den Berg  }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void create(proxy pgsqlsess, Thread.Queue _qportals, int nossl) {
c7de862014-11-20Stephen R. van den Berg  o::create();
16ce8b2014-09-12Stephen R. van den Berg  qportals = _qportals;
3e8df22014-10-29Stephen R. van den Berg  synctransact = 1;
f66d602017-11-18Stephen R. van den Berg  socket = sfile(); i = conxiin();
62f1ba2018-05-08Stephen R. van den Berg  shortmux = MUTEX(); nostash = MUTEX();
dcc15c2016-02-27Stephen R. van den Berg  closenext = 0;
f66d602017-11-18Stephen R. van den Berg  stashqueue = Thread.Queue(); stash = Stdio.Buffer();
67879d2017-11-10Stephen R. van den Berg  stashcount = Thread.ResourceCount();
f66d602017-11-18Stephen R. van den Berg  Thread.Thread(connectloop, pgsqlsess, nossl);
ecbab12008-07-27Stephen R. van den Berg  }
e7c5c82014-11-24Stephen R. van den Berg };
ecbab12008-07-27Stephen R. van den Berg 
c02c1d2017-06-03Stephen R. van den Berg #ifdef PG_DEBUGRACE class conxsess { final conxion chain;
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void create(conxion parent) {
c02c1d2017-06-03Stephen R. van den Berg  if (parent->started) werror("Overwriting conxsess %s %s\n", describe_backtrace(({"new ", backtrace()[..<1]})), describe_backtrace(({"old ", parent->nostrack}))); parent->nostrack = backtrace(); chain = parent; }
63fcae2017-11-22Stephen R. van den Berg  final void sendcmd(int mode, void|Result portal) {
c02c1d2017-06-03Stephen R. van den Berg  chain->sendcmd(mode, portal); chain = 0; }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void _destruct() {
c02c1d2017-06-03Stephen R. van den Berg  if (chain) werror("Untransmitted conxsess %s\n", describe_backtrace(({"", backtrace()[..<1]}))); } }; #endif
f4c9d62009-02-15Stephen R. van den Berg //! The result object returned by @[Sql.pgsql()->big_query()], except for
63fcae2017-11-22Stephen R. van den Berg //! the noted differences it behaves the same as @[Sql.Result].
0412962009-01-19Stephen R. van den Berg //! //! @seealso
63fcae2017-11-22Stephen R. van den Berg //! @[Sql.Result], @[Sql.pgsql], @[Sql.Sql], @[Sql.pgsql()->big_query()] class Result {
ecbab12008-07-27Stephen R. van den Berg 
b52b452017-03-15Henrik Grubbström (Grubba)  inherit __builtin.Sql.Result;
f66d602017-11-18Stephen R. van den Berg  private proxy pgsqlsess; private int(0..1) eoffound;
39e1c42014-11-14Stephen R. van den Berg  private conxion c;
c7de862014-11-20Stephen R. van den Berg  private conxiin cr;
a55cc42017-11-22Stephen R. van den Berg  final int(0..1) untolderror;
f66d602017-11-18Stephen R. van den Berg  final mixed delayederror;
6534f92018-01-20Stephen R. van den Berg  //! @ignore final int(PORTALINIT..PURGED) _state; //! @endignore
3c93de2014-11-03Stephen R. van den Berg  final int _fetchlimit;
f66d602017-11-18Stephen R. van den Berg  private int(0..1) alltext; final int(0..1) _forcetext;
657cd92017-02-04Stephen R. van den Berg  private int syncparse;
55a08b2017-06-25Stephen R. van den Berg  private int transtype;
3c93de2014-11-03Stephen R. van den Berg  final string _portalname;
05b1f52014-11-17Stephen R. van den Berg  private int inflight;
f66d602017-11-18Stephen R. van den Berg  int portalbuffersize;
62f1ba2018-05-08Stephen R. van den Berg  private MUTEX closemux;
05b1f52014-11-17Stephen R. van den Berg  private Thread.Queue datarows;
67879d2017-11-10Stephen R. van den Berg  private Thread.ResourceCountKey stmtifkey, portalsifkey;
aed7142014-11-18Stephen R. van den Berg  private array(mapping(string:mixed)) datarowdesc;
f66d602017-11-18Stephen R. van den Berg  final array(int) datarowtypes; // types from datarowdesc
05b1f52014-11-17Stephen R. van den Berg  private string statuscmdcomplete;
aed7142014-11-18Stephen R. van den Berg  private int bytesreceived;
3c93de2014-11-03Stephen R. van den Berg  final int _synctransact; final Thread.Condition _ddescribe;
62f1ba2018-05-08Stephen R. van den Berg  final MUTEX _ddescribemux;
f66d602017-11-18Stephen R. van den Berg  final Thread.MutexKey _unnamedportalkey, _unnamedstatementkey;
3c93de2014-11-03Stephen R. van den Berg  final array _params; final string _query; final string _preparedname; final mapping(string:mixed) _tprepared;
5cb4f22014-11-28Stephen R. van den Berg  private function(:void) gottimeout; private int timeout;
ecbab12008-07-27Stephen R. van den Berg 
67879d2017-11-10Stephen R. van den Berg  protected string _sprintf(int type) {
f66d602017-11-18Stephen R. van den Berg  string res; switch (type) {
16ce8b2014-09-12Stephen R. van den Berg  case 'O':
f66d602017-11-18Stephen R. van den Berg  int fd = -1; if (c && c->socket) catch(fd = c->socket->query_fd());
bea3fb2018-05-26Stephen R. van den Berg  res = sprintf( "Result state: %d numrows: %d eof: %d inflight: %d\n" "fd: %O portalname: %O coltypes: %d" " synctransact: %d laststatus: %s\n" "stash: %O\n"
180df82018-05-18Stephen R. van den Berg #if PG_DEBUGHISTORY > 0
bea3fb2018-05-26Stephen R. van den Berg  "history: %O\n"
180df82018-05-18Stephen R. van den Berg #endif
bea3fb2018-05-26Stephen R. van den Berg  "query: %O\n%s\n", _state, index, eoffound, inflight, fd, _portalname, datarowtypes && sizeof(datarowtypes), _synctransact, statuscmdcomplete || (_unnamedstatementkey ? "*parsing*" : ""), qalreadyprinted == this ? 0 : c && (string)c->stash,
180df82018-05-18Stephen R. van den Berg #if PG_DEBUGHISTORY > 0
bea3fb2018-05-26Stephen R. van den Berg  qalreadyprinted == this ? 0 : c && c->i->history,
180df82018-05-18Stephen R. van den Berg #endif
bea3fb2018-05-26Stephen R. van den Berg  qalreadyprinted == this ? "..." : replace(_query, "xxxx\n", "\\n"), qalreadyprinted == this ? "..." : _showbindings()*"\n");
62f1ba2018-05-08Stephen R. van den Berg  qalreadyprinted = this;
16ce8b2014-09-12Stephen R. van den Berg  break;
11b13b2014-08-16Martin Nilsson  } return res;
ecbab12008-07-27Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  protected void create(proxy _pgsqlsess, conxion _c, string query, int _portalbuffersize, int alltyped, array params, int forcetext,
55a08b2017-06-25Stephen R. van den Berg  int _timeout, int _syncparse, int _transtype) {
3c93de2014-11-03Stephen R. van den Berg  pgsqlsess = _pgsqlsess;
81790f2017-11-28Stephen R. van den Berg  if (c = _c) cr = c->i; else
0b714c2020-05-18Stephen R. van den Berg  losterror(LOSTERROR);
11b13b2014-08-16Martin Nilsson  _query = query;
05b1f52014-11-17Stephen R. van den Berg  datarows = Thread.Queue();
f66d602017-11-18Stephen R. van den Berg  _ddescribe = Thread.Condition();
62f1ba2018-05-08Stephen R. van den Berg  _ddescribemux = MUTEX(); closemux = MUTEX();
f66d602017-11-18Stephen R. van den Berg  portalbuffersize = _portalbuffersize;
aed7142014-11-18Stephen R. van den Berg  alltext = !alltyped;
11b13b2014-08-16Martin Nilsson  _params = params; _forcetext = forcetext;
2eef0b2014-11-15Stephen R. van den Berg  _state = PORTALINIT;
5cb4f22014-11-28Stephen R. van den Berg  timeout = _timeout;
657cd92017-02-04Stephen R. van den Berg  syncparse = _syncparse;
5cb4f22014-11-28Stephen R. van den Berg  gottimeout = _pgsqlsess->cancelquery;
f66d602017-11-18Stephen R. van den Berg  c->runningportals[this] = 1;
55a08b2017-06-25Stephen R. van den Berg  transtype = _transtype;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
bea3fb2018-05-26Stephen R. van den Berg  final array(string) _showbindings() {
3d6cb02020-02-23Stephen R. van den Berg  array(string) msgs = ({});
bea3fb2018-05-26Stephen R. van den Berg  if (_params) { array from, to, paramValues; [from, to, paramValues] = _params; if (sizeof(paramValues)) { int i; string val, 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; }
11b13b2014-08-16Martin Nilsson  //! Returns the command-complete status for this query. //! //! @seealso
42dcee2018-05-05Stephen R. van den Berg  //! @[Sql.Result()->status_command_complete()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final string status_command_complete() {
42dcee2018-05-05Stephen R. van den Berg  if (!statuscmdcomplete) { if (!datarowtypes) waitfordescribe(); { Thread.MutexKey lock = closemux->lock();
62f1ba2018-05-08Stephen R. van den Berg  if (_fetchlimit) {
acc6dd2020-05-14Stephen R. van den Berg  array(Thread.MutexKey) reflock = ({ _fetchlimit = 0 });
a809712019-11-01Stephen R. van den Berg  for (;;) { reflock[0] = lock; lock = 0; if (!_sendexecute(0, reflock)) {
86ee0d2020-05-18Stephen R. van den Berg  PD("Status_command_complete retry closemux %O\n", _portalname);
a809712019-11-01Stephen R. van den Berg  lock = closemux->lock(); continue; }
28a80a2020-05-27Stephen R. van den Berg  break;
a809712019-11-01Stephen R. van den Berg  }
acc6dd2020-05-14Stephen R. van den Berg  } lock = 0; // Force release before acquiring next
42dcee2018-05-05Stephen R. van den Berg  lock = _ddescribemux->lock(); if (!statuscmdcomplete) PT(_ddescribe->wait(lock)); } if (this) // If object already destructed, skip the next call trydelayederror(); // since you cannot call functions anymore else error(LOSTERROR); }
05b1f52014-11-17Stephen R. van den Berg  return statuscmdcomplete;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
11b13b2014-08-16Martin Nilsson  //! Returns the number of affected rows by this query. //! //! @seealso
42dcee2018-05-05Stephen R. van den Berg  //! @[Sql.Result()->affected_rows()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int affected_rows() {
11b13b2014-08-16Martin Nilsson  int rows;
42dcee2018-05-05Stephen R. van den Berg  sscanf(status_command_complete() || "", "%*s %d %d", rows, rows);
11b13b2014-08-16Martin Nilsson  return rows; }
ecbab12008-07-27Stephen R. van den Berg 
aed7142014-11-18Stephen R. van den Berg  final void _storetiming() {
f66d602017-11-18Stephen R. van den Berg  if (_tprepared) { _tprepared.trun = gethrtime() - _tprepared.trunstart; m_delete(_tprepared, "trunstart"); _tprepared = 0;
aed7142014-11-18Stephen R. van den Berg  } }
8384822014-11-12Stephen R. van den Berg  private void waitfordescribe() {
81790f2017-11-28Stephen R. van den Berg  { Thread.MutexKey lock = _ddescribemux->lock(); if (!datarowtypes) PT(_ddescribe->wait(lock)); }
f66d602017-11-18Stephen R. van den Berg  if (this) // If object already destructed, skip the next call trydelayederror(); // since you cannot call functions anymore else error(LOSTERROR);
615d422014-10-31Stephen R. van den Berg  }
11b13b2014-08-16Martin Nilsson  //! @seealso
63fcae2017-11-22Stephen R. van den Berg  //! @[Sql.Result()->num_fields()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int num_fields() {
f66d602017-11-18Stephen R. van den Berg  if (!datarowtypes)
615d422014-10-31Stephen R. van den Berg  waitfordescribe();
5318352014-11-22Stephen R. van den Berg  return sizeof(datarowtypes);
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
7214f62014-11-24Stephen R. van den Berg  //! @note //! This method returns the number of rows already received from //! the database for the current query. Note that this number //! can still increase between subsequent calls if the results from //! the query are not complete yet. This function is only guaranteed to //! return the correct count after EOF has been reached.
11b13b2014-08-16Martin Nilsson  //! @seealso
63fcae2017-11-22Stephen R. van den Berg  //! @[Sql.Result()->num_rows()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int num_rows() {
615d422014-10-31Stephen R. van den Berg  trydelayederror();
d9e1ab2017-11-22Stephen R. van den Berg  return index;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
0b714c2020-05-18Stephen R. van den Berg  private void losterror(void|string err) {
e1963e2017-11-15Stephen R. van den Berg  if (pgsqlsess)
0b714c2020-05-18Stephen R. van den Berg  err = pgsqlsess->geterror(1) || err; if (err) error("%s\n", err);
e1963e2017-11-15Stephen R. van den Berg  } private void trydelayederror() {
f66d602017-11-18Stephen R. van den Berg  if (delayederror)
16ce8b2014-09-12Stephen R. van den Berg  throwdelayederror(this);
e1963e2017-11-15Stephen R. van den Berg  else if (_state == PURGED) losterror();
16ce8b2014-09-12Stephen R. van den Berg  }
11b13b2014-08-16Martin Nilsson  //! @seealso
63fcae2017-11-22Stephen R. van den Berg  //! @[Sql.Result()->eof()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int eof() {
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror();
11b13b2014-08-16Martin Nilsson  return eoffound; }
ecbab12008-07-27Stephen R. van den Berg 
11b13b2014-08-16Martin Nilsson  //! @seealso
63fcae2017-11-22Stephen R. van den Berg  //! @[Sql.Result()->fetch_fields()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final array(mapping(string:mixed)) fetch_fields() {
f66d602017-11-18Stephen R. van den Berg  if (!datarowtypes)
615d422014-10-31Stephen R. van den Berg  waitfordescribe();
f66d602017-11-18Stephen R. van den Berg  if (!datarowdesc) error(LOSTERROR);
3d6cb02020-02-23Stephen R. van den Berg  return datarowdesc + ({});
aed7142014-11-18Stephen R. van den Berg  } #ifdef PG_DEBUG
e7c5c82014-11-24Stephen R. van den Berg #define INTVOID int
aed7142014-11-18Stephen R. van den Berg #else
e7c5c82014-11-24Stephen R. van den Berg #define INTVOID void
aed7142014-11-18Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  final INTVOID _decodedata(int msglen, string cenc) {
2e28a92017-06-18Stephen R. van den Berg  _storetiming(); _releasestatement();
aed7142014-11-18Stephen R. van den Berg  string serror;
f66d602017-11-18Stephen R. van den Berg  bytesreceived += msglen; int cols = cr->read_int16(); array a = allocate(cols, !alltext && Val.null);
aed7142014-11-18Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= 2 + 4 * cols;
aed7142014-11-18Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  foreach (datarowtypes; int i; int typ) { int collen = cr->read_sint(4); if (collen > 0) {
aed7142014-11-18Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= collen;
aed7142014-11-18Stephen R. van den Berg #endif mixed value;
f66d602017-11-18Stephen R. van den Berg  switch (typ) {
aed7142014-11-18Stephen R. van den Berg  case FLOAT4OID:
a469192017-12-11Stephen R. van den Berg #if !constant(__builtin.__SINGLE_PRECISION_FLOAT__)
aed7142014-11-18Stephen R. van den Berg  case FLOAT8OID: #endif
a469192017-12-11Stephen R. van den Berg  if (_forcetext) { if (!alltext) { value = (float)cr->read(collen); break; } } else { [ value ] = cr->sscanf(collen == 4 ? "%4F" : "%8F"); if (alltext)
b2e4312018-02-01Stephen R. van den Berg  value = sprintf("%.*g", collen == 4 ? 9 : 17, value);
aed7142014-11-18Stephen R. van den Berg  break; }
f66d602017-11-18Stephen R. van den Berg  default:value = cr->read(collen);
aed7142014-11-18Stephen R. van den Berg  break; case CHAROID:
f66d602017-11-18Stephen R. van den Berg  value = alltext ? cr->read(1) : cr->read_int8();
aed7142014-11-18Stephen R. van den Berg  break;
f66d602017-11-18Stephen R. van den Berg  case BOOLOID:value = cr->read_int8(); switch (value) { case 'f':value = 0;
aed7142014-11-18Stephen R. van den Berg  break;
f66d602017-11-18Stephen R. van den Berg  case 't':value = 1;
aed7142014-11-18Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  if (alltext) value = value ? "t" : "f";
aed7142014-11-18Stephen R. van den Berg  break; case TEXTOID: case BPCHAROID: case VARCHAROID:
f66d602017-11-18Stephen R. van den Berg  value = cr->read(collen); if (cenc == UTF8CHARSET && catch(value = utf8_to_string(value))
aed7142014-11-18Stephen R. van den Berg  && !serror)
f66d602017-11-18Stephen R. van den Berg  serror = SERROR("%O contains non-%s characters\n", value, UTF8CHARSET);
aed7142014-11-18Stephen R. van den Berg  break;
4855e22018-02-05Stephen R. van den Berg  case NUMERICOID: if (_forcetext) { value = cr->read(collen); if (!alltext) { value = value/"."; if (sizeof(value) == 1) value = (int)value[0]; else {
0d4b5d2018-02-06Stephen R. van den Berg  int i, denom; for (i = sizeof(value[1]), denom = 1; --i >= 0; denom *= 10); i = (int)value[0]; value = (int)value[1]; value = Gmp.mpq(i * denom + (i >= 0 ? value : -value),
4855e22018-02-05Stephen R. van den Berg  denom); } } } else { int nwords = cr->read_int16(); int magnitude = cr->read_sint(2); int sign = cr->read_int16(); cr->consume(2);
3bf8e82018-03-10Stephen R. van den Berg  if (nwords) { for (value = cr->read_int16(); --nwords; magnitude--) value = value * NUMERIC_MAGSTEP + cr->read_int16(); if (sign) value = -value; if (magnitude > 0) do value *= NUMERIC_MAGSTEP; while (--magnitude); else if (magnitude < 0) { for (sign = NUMERIC_MAGSTEP; ++magnitude; sign *= NUMERIC_MAGSTEP); value = Gmp.mpq(value, sign); } } else value = 0;
0d4b5d2018-02-06Stephen R. van den Berg  if (alltext) value = (string)value;
4855e22018-02-05Stephen R. van den Berg  } break;
7d57482017-12-11Stephen R. van den Berg  case INT4RANGEOID: case INT8RANGEOID: case DATERANGEOID: case TSRANGEOID: case TSTZRANGEOID: if (_forcetext) value = cr->read(collen); else { array totype = oidtotype[typ];
ebc4212017-12-11Stephen R. van den Berg  mixed from = -Math.inf, till = Math.inf;
7d57482017-12-11Stephen R. van den Berg  switch (cr->read_int8()) { case 1: from = till = 0; break;
7a97fb2017-12-13Stephen R. van den Berg  case 0x12: from = cr->read_sint(cr->read_int32());
7d57482017-12-11Stephen R. van den Berg  break;
7a97fb2017-12-13Stephen R. van den Berg  case 2: from = cr->read_sint(cr->read_int32()); case 8: till = cr->read_sint(cr->read_int32());
7d57482017-12-11Stephen R. van den Berg  } if (totype[0]) { if (intp(from)) { value = totype[0]();
c44ed92017-12-13Stephen R. van den Berg  value[totype[3]] = from + totype[2];
7d57482017-12-11Stephen R. van den Berg  from = value; } if (intp(till)) { value = totype[0]();
c44ed92017-12-13Stephen R. van den Berg  value[totype[3]] = till + totype[2];
7d57482017-12-11Stephen R. van den Berg  till = value; } }
b46f4d2017-12-22Stephen R. van den Berg  value = Val.Range(from, till);
a469192017-12-11Stephen R. van den Berg  if (alltext)
90ca772017-12-28Stephen R. van den Berg  value = value->sql();
7d57482017-12-11Stephen R. van den Berg  } break;
adfd4e2017-12-07Stephen R. van den Berg  case CIDROID: case INETOID: if (_forcetext) value = cr->read(collen); else {
b46f4d2017-12-22Stephen R. van den Berg  value = Val.Inet();
db98672017-12-07Stephen R. van den Berg  int iptype = cr->read_int8(); // 2 == IPv4, 3 == IPv6 value->masklen = cr->read_int8() + (iptype == 2 && 12*8);
adfd4e2017-12-07Stephen R. van den Berg  cr->read_int8(); // 0 == INET, 1 == CIDR
db98672017-12-07Stephen R. van den Berg  value->address = cr->read_hint(1);
a469192017-12-11Stephen R. van den Berg  if (alltext) value = (string)value;
adfd4e2017-12-07Stephen R. van den Berg  } break;
01b8152017-12-06Stephen R. van den Berg  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]();
c44ed92017-12-13Stephen R. van den Berg  value[totype[3]] = cr->read_sint(totype[4]) + totype[2]; int i = 5; while (i < sizeof(totype)) {
01b8152017-12-06Stephen R. van den Berg  value[totype[i]] = cr->read_sint(totype[i+1]);
c44ed92017-12-13Stephen R. van den Berg  i += 2; }
01b8152017-12-06Stephen R. van den Berg  if (alltext) value = (string)value; } break;
aed7142014-11-18Stephen R. van den Berg  case INT8OID:case INT2OID: case OIDOID:case INT4OID:
f66d602017-11-18Stephen R. van den Berg  if (_forcetext) { value = cr->read(collen); if (!alltext) value = (int)value;
aed7142014-11-18Stephen R. van den Berg  } else {
f66d602017-11-18Stephen R. van den Berg  switch (typ) { case INT8OID:value = cr->read_sint(8);
aed7142014-11-18Stephen R. van den Berg  break;
f66d602017-11-18Stephen R. van den Berg  case INT2OID:value = cr->read_sint(2);
aed7142014-11-18Stephen R. van den Berg  break; case OIDOID:
f66d602017-11-18Stephen R. van den Berg  case INT4OID:value = cr->read_sint(4);
aed7142014-11-18Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  if (alltext) value = (string)value;
aed7142014-11-18Stephen R. van den Berg  } } a[i]=value;
f66d602017-11-18Stephen R. van den Berg  } else if (!collen)
aed7142014-11-18Stephen R. van den Berg  a[i]=""; } _processdataready(a);
f66d602017-11-18Stephen R. van den Berg  if (serror)
aed7142014-11-18Stephen R. van den Berg  error(serror); #ifdef PG_DEBUG return msglen; #endif
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
8059252014-11-25Stephen R. van den Berg  final void _setrowdesc(array(mapping(string:mixed)) drowdesc, array(int) drowtypes) {
f66d602017-11-18Stephen R. van den Berg  Thread.MutexKey lock = _ddescribemux->lock(); datarowdesc = drowdesc; datarowtypes = drowtypes;
615d422014-10-31Stephen R. van den Berg  _ddescribe->broadcast(); }
075c542014-11-03Stephen R. van den Berg  final void _preparebind(array dtoid) {
3d6cb02020-02-23Stephen R. van den Berg  array(string|int) paramValues = _params ? _params[2] : ({});
f66d602017-11-18Stephen R. van den Berg  if (sizeof(dtoid) != sizeof(paramValues))
615d422014-10-31Stephen R. van den Berg  SUSERERROR("Invalid number of bindings, expected %d, got %d\n",
f66d602017-11-18Stephen R. van den Berg  sizeof(dtoid), sizeof(paramValues));
86ee0d2020-05-18Stephen R. van den Berg  PD("PrepareBind\n");
f66d602017-11-18Stephen R. van den Berg  Thread.MutexKey lock = _ddescribemux->lock(); if (!_portalname) { _portalname = (_unnamedportalkey = pgsqlsess.unnamedportalmux->trylock(1))
d57b1a2014-11-18Stephen R. van den Berg  ? "" : PORTALPREFIX #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  + (string)(c->socket->query_fd()) + "_"
d57b1a2014-11-18Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  + String.int2hex(pgsqlsess.pportalcount++); lock = 0;
615d422014-10-31Stephen R. van den Berg #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  PD("ParamValues to bind: %O\n", paramValues);
615d422014-10-31Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  Stdio.Buffer plugbuffer = Stdio.Buffer(); { array dta = ({sizeof(dtoid)}); plugbuffer->add(_portalname, 0, _preparedname, 0)
01b8152017-12-06Stephen R. van den Berg  ->add_ints(dta + map(dtoid, writeoidformat, paramValues, ({0})) + dta, 2);
c9c7fc2014-11-26Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  string cenc = pgsqlsess.runtimeparameter[CLIENT_ENCODING]; foreach (paramValues; int i; mixed value) { if (undefinedp(value) || objectp(value) && value->is_val_null)
d57b1a2014-11-18Stephen R. van den Berg  plugbuffer->add_int32(-1); // NULL
f66d602017-11-18Stephen R. van den Berg  else if (stringp(value) && !sizeof(value)) { int k = 0; switch (dtoid[i]) {
d57b1a2014-11-18Stephen R. van den Berg  default:
f66d602017-11-18Stephen R. van den Berg  k = -1; // cast empty strings to NULL for non-string types
d57b1a2014-11-18Stephen R. van den Berg  case BYTEAOID: case TEXTOID: case XMLOID: case BPCHAROID: case VARCHAROID:; } plugbuffer->add_int32(k); } else
f66d602017-11-18Stephen R. van den Berg  switch (dtoid[i]) {
d57b1a2014-11-18Stephen R. van den Berg  case TEXTOID: case BPCHAROID: case VARCHAROID: {
f66d602017-11-18Stephen R. van den Berg  if (!value) {
d57b1a2014-11-18Stephen R. van den Berg  plugbuffer->add_int32(-1); break; }
f66d602017-11-18Stephen R. van den Berg  value = (string)value; switch (cenc) {
d57b1a2014-11-18Stephen R. van den Berg  case UTF8CHARSET:
d87fb22020-01-21Stephen R. van den Berg  if (has_value(value, 0)) SUSERERROR("NUL characters not allowed in PG-UTF-8: %O\n", value); else value = string_to_utf8(value);
d57b1a2014-11-18Stephen R. van den Berg  break; default:
f66d602017-11-18Stephen R. van den Berg  if (String.width(value)>8) {
d57b1a2014-11-18Stephen R. van den Berg  SUSERERROR("Don't know how to convert %O to %s encoding\n",
f66d602017-11-18Stephen R. van den Berg  value, cenc);
d57b1a2014-11-18Stephen R. van den Berg  value=""; } }
f66d602017-11-18Stephen R. van den Berg  plugbuffer->add_hstring(value, 4);
615d422014-10-31Stephen R. van den Berg  break; }
d57b1a2014-11-18Stephen R. van den Berg  default: {
f66d602017-11-18Stephen R. van den Berg  if (!value) {
d57b1a2014-11-18Stephen R. van den Berg  plugbuffer->add_int32(-1);
615d422014-10-31Stephen R. van den Berg  break;
d57b1a2014-11-18Stephen R. van den Berg  }
bb7afa2020-05-28Stephen R. van den Berg  if (!objectp(value) || typeof(value) != typeof(Stdio.Buffer()))
f66d602017-11-18Stephen R. van den Berg  /* * Like Oracle and SQLite, we accept literal binary values * from single-valued multisets. */ if (multisetp(value) && sizeof(value) == 1) value = indices(value)[0]; else {
7c9d772017-07-10Stephen R. van den Berg  value = (string)value;
bb7afa2020-05-28Stephen R. van den Berg  if (cenc == UTF8CHARSET && (dtoid[i] != BYTEAOID || String.width(value) > 8))
f66d602017-11-18Stephen R. van den Berg  /*
bb7afa2020-05-28Stephen R. van den Berg  * FIXME For BYTEAOID and wide strings we should * throw an error here, but for historical reasons and * as a DWIM convenience we autoconvert to UTF8 anyway.
7c9d772017-07-10Stephen R. van den Berg  */
bb7afa2020-05-28Stephen R. van den Berg  value = string_to_utf8(value); else if (String.width(value) > 8) { SUSERERROR( "Wide string %O not supported for type OID %d\n", value, dtoid[i]); value = ""; }
f66d602017-11-18Stephen R. van den Berg  } plugbuffer->add_hstring(value, 4);
615d422014-10-31Stephen R. van den Berg  break; }
42ab4c2014-11-25Stephen R. van den Berg  case BOOLOID:
d57b1a2014-11-18Stephen R. van den Berg  do { int tval;
f66d602017-11-18Stephen R. van den Berg  if (stringp(value)) tval = value[0]; else if (!intp(value)) { value = !!value; // cast to boolean
615d422014-10-31Stephen R. van den Berg  break;
d57b1a2014-11-18Stephen R. van den Berg  } else
f66d602017-11-18Stephen R. van den Berg  tval = value; switch (tval) {
d57b1a2014-11-18Stephen R. van den Berg  case 'o':case 'O': catch {
f66d602017-11-18Stephen R. van den Berg  tval = value[1]; value = tval == 'n' || tval == 'N';
d57b1a2014-11-18Stephen R. van den Berg  };
615d422014-10-31Stephen R. van den Berg  break;
d57b1a2014-11-18Stephen R. van den Berg  default:
f66d602017-11-18Stephen R. van den Berg  value = 1;
d57b1a2014-11-18Stephen R. van den Berg  break; case 0:case '0':case 'f':case 'F':case 'n':case 'N':
f66d602017-11-18Stephen R. van den Berg  value = 0;
d57b1a2014-11-18Stephen R. van den Berg  break; }
f66d602017-11-18Stephen R. van den Berg  } while (0);
65f3032017-12-08Stephen R. van den Berg  plugbuffer->add("\0\0\0\1", value);
d57b1a2014-11-18Stephen R. van den Berg  break; case CHAROID:
f66d602017-11-18Stephen R. van den Berg  if (intp(value)) plugbuffer->add_hstring(value, 4);
d57b1a2014-11-18Stephen R. van den Berg  else {
f66d602017-11-18Stephen R. van den Berg  value = (string)value; switch (sizeof(value)) {
d57b1a2014-11-18Stephen R. van den Berg  default: SUSERERROR(
f66d602017-11-18Stephen R. van den Berg  "\"char\" types must be 1 byte wide, got %O\n", value);
d57b1a2014-11-18Stephen R. van den Berg  case 0: plugbuffer->add_int32(-1); // NULL break; case 1:
f66d602017-11-18Stephen R. van den Berg  plugbuffer->add_hstring(value[0], 4);
d57b1a2014-11-18Stephen R. van den Berg  }
615d422014-10-31Stephen R. van den Berg  }
d57b1a2014-11-18Stephen R. van den Berg  break;
4855e22018-02-05Stephen R. van den Berg  case NUMERICOID: if (stringp(value)) plugbuffer->add_hstring(value, 4); else { int num, den, sign, magnitude = 0; value = Gmp.mpq(value); num = value->num(); den = value->den(); for (value = den; value > NUMERIC_MAGSTEP; value /= NUMERIC_MAGSTEP); if (value > 1) { value = NUMERIC_MAGSTEP / value; num *= value; den *= value; } if (num < 0) sign = 0x4000, num = -num; else sign = 0; array stor = ({}); if (num) { while (!(num % NUMERIC_MAGSTEP)) num /= NUMERIC_MAGSTEP, magnitude++; do stor = ({num % NUMERIC_MAGSTEP}) + stor, magnitude++; while (num /= NUMERIC_MAGSTEP); num = --magnitude << 2; while (den > 1) magnitude--, den /= NUMERIC_MAGSTEP; } plugbuffer->add_int32(4 * 2 + (sizeof(stor) << 1)) ->add_int16(sizeof(stor))->add_int16(magnitude) ->add_int16(sign)->add_int16(num)->add_ints(stor, 2); } break;
7d57482017-12-11Stephen R. van den Berg  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;
c44ed92017-12-13Stephen R. van den Berg  else { from = value->from[totype[3]] - totype[2]; till = value->till[totype[3]] - totype[2]; }
ebc4212017-12-11Stephen R. van den Berg  if (value->till == Math.inf) if (value->from == -Math.inf)
7d57482017-12-11Stephen R. van den Berg  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 {
ebc4212017-12-11Stephen R. van den Berg  if (value->from == -Math.inf)
7d57482017-12-11Stephen R. van den Berg  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;
adfd4e2017-12-07Stephen R. van den Berg  case CIDROID: case INETOID: if (stringp(value)) plugbuffer->add_hstring(value, 4);
db98672017-12-07Stephen R. van den Berg  else if (value->address <= 0xffffffff) // IPv4
65f3032017-12-08Stephen R. van den Berg  plugbuffer->add("\0\0\0\10\2", value->masklen - 12 * 8, dtoid[i] == CIDROID, 4) ->add_int32(value->address);
db98672017-12-07Stephen R. van den Berg  else // IPv6
65f3032017-12-08Stephen R. van den Berg  plugbuffer->add("\0\0\0\24\3", value->masklen, dtoid[i] == CIDROID, 16)
db98672017-12-07Stephen R. van den Berg  ->add_int(value->address, 16);
adfd4e2017-12-07Stephen R. van den Berg  break;
01b8152017-12-06Stephen R. van den Berg  case DATEOID: case TIMEOID: case TIMETZOID: case INTERVALOID: case TIMESTAMPOID: case TIMESTAMPTZOID:
adfd4e2017-12-07Stephen R. van den Berg  if (stringp(value))
01b8152017-12-06Stephen R. van den Berg  plugbuffer->add_hstring(value, 4);
adfd4e2017-12-07Stephen R. van den Berg  else {
01b8152017-12-06Stephen R. van den Berg  array totype = oidtotype[dtoid[i]]; if (!objectp(value)) value = totype[0](value);
c44ed92017-12-13Stephen R. van den Berg  plugbuffer->add_int32(totype[1]) ->add_int(value[totype[3]] - totype[2], totype[4]); int i = 5; while (i < sizeof(totype)) {
01b8152017-12-06Stephen R. van den Berg  plugbuffer->add_int(value[totype[i]], totype[i+1]);
c44ed92017-12-13Stephen R. van den Berg  i += 2; }
01b8152017-12-06Stephen R. van den Berg  } break;
a469192017-12-11Stephen R. van den Berg  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)
2ac3072020-02-17Stephen R. van den Berg  ->sprintf(w == 4 ? "%4F" : "%8F", (float)value);
a469192017-12-11Stephen R. van den Berg  } break;
d57b1a2014-11-18Stephen R. van den Berg  case INT8OID:
f66d602017-11-18Stephen R. van den Berg  plugbuffer->add_int32(8)->add_int((int)value, 8);
d57b1a2014-11-18Stephen R. van den Berg  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; } }
f66d602017-11-18Stephen R. van den Berg  if (!datarowtypes) { if (_tprepared && dontcacheprefix->match(_query)) m_delete(pgsqlsess->prepareds, _query), _tprepared = 0;
86ee0d2020-05-18Stephen R. van den Berg  PD("WaitForDescribe\n");
5318352014-11-22Stephen R. van den Berg  waitfordescribe();
d57b1a2014-11-18Stephen R. van den Berg  }
86ee0d2020-05-18Stephen R. van den Berg  PD("About to bind %d\n", _state);
f66d602017-11-18Stephen R. van den Berg  if (_state >= CLOSING) lock = _unnamedstatementkey = 0;
5318352014-11-22Stephen R. van den Berg  else { plugbuffer->add_int16(sizeof(datarowtypes));
62f1ba2018-05-08Stephen R. van den Berg  if (sizeof(datarowtypes)) {
01b8152017-12-06Stephen R. van den Berg  plugbuffer->add_ints(map(datarowtypes, readoidformat), 2);
62f1ba2018-05-08Stephen R. van den Berg  lock = 0; } else if (syncparse < 0 && !pgsqlsess->wasparallelisable
f66d602017-11-18Stephen R. van den Berg  && !pgsqlsess->statementsinflight->drained(1)) {
acc6dd2020-05-14Stephen R. van den Berg  lock = 0; // Unlock while we wait
67879d2017-11-10Stephen R. van den Berg  PD("Commit waiting for statements to finish\n");
62f1ba2018-05-08Stephen R. van den Berg  catch(PT(pgsqlsess->statementsinflight->wait_till_drained(1)));
5318352014-11-22Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  PD("Bind portal %O statement %O\n", _portalname, _preparedname); _fetchlimit = pgsqlsess->_fetchlimit;
6623742017-06-17Stephen R. van den Berg  _bindportal();
c02c1d2017-06-03Stephen R. van den Berg  conxsess bindbuffer = c->start();
1a2da92017-11-30Stephen R. van den Berg  stmtifkey = 0;
c02c1d2017-06-03Stephen R. van den Berg  CHAIN(bindbuffer)->add_int8('B')->add_hstring(plugbuffer, 4, 4);
f66d602017-11-18Stephen R. van den Berg  if (!_tprepared && sizeof(_preparedname))
c02c1d2017-06-03Stephen R. van den Berg  closestatement(CHAIN(bindbuffer), _preparedname);
5318352014-11-22Stephen R. van den Berg  _sendexecute(_fetchlimit
55a08b2017-06-25Stephen R. van den Berg  && !(transtype != NOTRANS
f66d602017-11-18Stephen R. van den Berg  || sizeof(_query) >= MINPREPARELENGTH &&
5318352014-11-22Stephen R. van den Berg  execfetchlimit->match(_query))
f66d602017-11-18Stephen R. van den Berg  && _fetchlimit, bindbuffer);
21efd72019-05-29Stephen R. van den Berg  _unnamedstatementkey = 0;
5318352014-11-22Stephen R. van den Berg  }
81790f2017-11-28Stephen R. van den Berg  }
615d422014-10-31Stephen R. van den Berg  }
8059252014-11-25Stephen R. van den Berg  final void _processrowdesc(array(mapping(string:mixed)) datarowdesc, array(int) datarowtypes) {
f66d602017-11-18Stephen R. van den Berg  _setrowdesc(datarowdesc, datarowtypes); if (_tprepared) { _tprepared.datarowdesc = datarowdesc; _tprepared.datarowtypes = datarowtypes;
8059252014-11-25Stephen R. van den Berg  }
615d422014-10-31Stephen R. van den Berg  }
6623742017-06-17Stephen R. van den Berg  final void _parseportal() {
acc6dd2020-05-14Stephen R. van den Berg  for (;;) {
81790f2017-11-28Stephen R. van den Berg  Thread.MutexKey lock = closemux->lock();
acc6dd2020-05-14Stephen R. van den Berg  if ((syncparse || syncparse < 0 && pgsqlsess->wasparallelisable) && !pgsqlsess->statementsinflight->drained()) { lock = 0; // Unlock while we wait
86ee0d2020-05-18Stephen R. van den Berg  PD("Commit waiting for statements to finish %d\n", pgsqlsess->statementsinflight->_count);
62f1ba2018-05-08Stephen R. van den Berg  catch(PT(pgsqlsess->statementsinflight->wait_till_drained()));
86ee0d2020-05-18Stephen R. van den Berg  PD("Parseportal retry closemux %O\n", _portalname);
acc6dd2020-05-14Stephen R. van den Berg  continue;
81790f2017-11-28Stephen R. van den Berg  }
acc6dd2020-05-14Stephen R. van den Berg  _state = PARSING;
62f1ba2018-05-08Stephen R. van den Berg  stmtifkey = pgsqlsess->statementsinflight->acquire();
acc6dd2020-05-14Stephen R. van den Berg  break;
6623742017-06-17Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  statuscmdcomplete = 0; pgsqlsess->wasparallelisable = paralleliseprefix->match(_query);
6623742017-06-17Stephen R. van den Berg  }
62f1ba2018-05-08Stephen R. van den Berg  final void _releasestatement() {
e85f6b2018-05-17Stephen R. van den Berg  Thread.MutexKey lock = closemux->lock();
2e28a92017-06-18Stephen R. van den Berg  if (_state <= BOUND) {
67879d2017-11-10Stephen R. van den Berg  stmtifkey = 0;
86cd342018-05-30Stephen R. van den Berg  _state = COMMITTED;
2e28a92017-06-18Stephen R. van den Berg  } }
6623742017-06-17Stephen R. van den Berg  final void _bindportal() {
2e28a92017-06-18Stephen R. van den Berg  Thread.MutexKey lock = closemux->lock();
f66d602017-11-18Stephen R. van den Berg  _state = BOUND; portalsifkey = pgsqlsess->portalsinflight->acquire();
16ce8b2014-09-12Stephen R. van den Berg  }
615d422014-10-31Stephen R. van den Berg  final void _purgeportal() {
6623742017-06-17Stephen R. van den Berg  PD("Purge portal\n");
5bbace2014-11-18Stephen R. van den Berg  datarows->write(1); // Signal EOF
81790f2017-11-28Stephen R. van den Berg  { Thread.MutexKey lock = closemux->lock(); _fetchlimit = 0; // disables further Executes
86cd342018-05-30Stephen R. van den Berg  stmtifkey = portalsifkey = 0;
81790f2017-11-28Stephen R. van den Berg  _state = PURGED;
2e28a92017-06-18Stephen R. van den Berg  }
04f1302014-11-14Stephen R. van den Berg  releaseconditions();
615d422014-10-31Stephen R. van den Berg  }
62f1ba2018-05-08Stephen R. van den Berg  final int _closeportal(conxsess cs, array(Thread.MutexKey) reflock) {
f66d602017-11-18Stephen R. van den Berg  void|bufcon|conxsess plugbuffer = CHAIN(cs); int retval = KEEP; PD("%O Try Closeportal %d\n", _portalname, _state); _fetchlimit = 0; // disables further Executes
86cd342018-05-30Stephen R. van den Berg  stmtifkey = 0;
f66d602017-11-18Stephen R. van den Berg  switch (_state) {
2eef0b2014-11-15Stephen R. van den Berg  case PORTALINIT:
2e28a92017-06-18Stephen R. van den Berg  case PARSING:
f66d602017-11-18Stephen R. van den Berg  _unnamedstatementkey = 0; _state = CLOSING;
615d422014-10-31Stephen R. van den Berg  break;
2eef0b2014-11-15Stephen R. van den Berg  case COPYINPROGRESS:
615d422014-10-31Stephen R. van den Berg  PD("CopyDone\n"); plugbuffer->add("c\0\0\0\4");
2e28a92017-06-18Stephen R. van den Berg  case COMMITTED:
2eef0b2014-11-15Stephen R. van den Berg  case BOUND:
f66d602017-11-18Stephen R. van den Berg  _state = CLOSING;
62f1ba2018-05-08Stephen R. van den Berg  if (reflock) reflock[0] = 0;
f66d602017-11-18Stephen R. van den Berg  PD("Close portal %O\n", _portalname);
c02a6a2017-06-21Stephen R. van den Berg  if (_portalname && sizeof(_portalname)) {
f66d602017-11-18Stephen R. van den Berg  plugbuffer->add_int8('C') ->add_hstring(({'P', _portalname, 0}), 4, 4); retval = FLUSHSEND;
16ce8b2014-09-12Stephen R. van den Berg  } else
f66d602017-11-18Stephen R. van den Berg  _unnamedportalkey = 0;
67879d2017-11-10Stephen R. van den Berg  portalsifkey = 0;
f66d602017-11-18Stephen R. van den Berg  if (pgsqlsess->portalsinflight->drained()) {
acc6dd2020-05-14Stephen R. van den Berg  if (plugbuffer->stashcount->drained() && transtype != TRANSBEGIN) {
44de4f2016-10-15Stephen R. van den Berg  /* * stashcount will be non-zero if a parse request has been queued * before the close was initiated. */
acc6dd2020-05-14Stephen R. van den Berg  Thread.MutexKey lock = plugbuffer->shortmux->lock(); if (plugbuffer->stashcount->drained()) pgsqlsess->readyforquerycount++, retval = SYNCSEND; }
f66d602017-11-18Stephen R. van den Berg  pgsqlsess->pportalcount = 0;
16ce8b2014-09-12Stephen R. van den Berg  } } return retval; }
ed6f022018-05-05Stephen R. van den Berg  private void replenishrows() {
c248e92018-05-24Stephen R. van den Berg  if (_fetchlimit && datarows->size() <= _fetchlimit >> 1 && _state >= COMMITTED) {
a809712019-11-01Stephen R. van den Berg  Thread.MutexKey lock; for (;;) { lock = closemux->lock(); if (_fetchlimit) { _fetchlimit = pgsqlsess._fetchlimit; if (bytesreceived) _fetchlimit = min((portalbuffersize >> 1) * index / bytesreceived || 1, _fetchlimit); if (_fetchlimit) if (inflight <= (_fetchlimit - 1) >> 1) {
acc6dd2020-05-14Stephen R. van den Berg  array(Thread.MutexKey) reflock = ({ lock });
a809712019-11-01Stephen R. van den Berg  lock = 0;
86ee0d2020-05-18Stephen R. van den Berg  if (!_sendexecute(_fetchlimit, reflock)) { PD("Replenishrows retry closemux %O\n", _portalname);
a809712019-11-01Stephen R. van den Berg  continue;
86ee0d2020-05-18Stephen R. van den Berg  }
a809712019-11-01Stephen R. van den Berg  } else PD("<%O _fetchlimit %d, inflight %d, skip execute\n", _portalname, _fetchlimit, inflight); } break;
1616b22018-05-07Stephen R. van den Berg  }
ecbab12008-07-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  }
ed6f022018-05-05Stephen R. van den Berg  final void _processdataready(array datarow, void|int msglen) { bytesreceived += msglen; inflight--;
e4ff9f2018-05-23Stephen R. van den Berg  if (_state < CLOSED)
ed6f022018-05-05Stephen R. van den Berg  datarows->write(datarow); if (++index == 1) PD("<%O _fetchlimit %d=min(%d||1,%d), inflight %d\n", _portalname, _fetchlimit, (portalbuffersize >> 1) * index / bytesreceived, pgsqlsess._fetchlimit, inflight); replenishrows(); }
3d6e3c2020-02-22Stephen R. van den Berg  private void releaseconditions(void|int aborted) {
f66d602017-11-18Stephen R. van den Berg  _unnamedportalkey = _unnamedstatementkey = 0; if (!datarowtypes) { if (_state != PURGED && !delayederror) delayederror = LOSTERROR;
3d6cb02020-02-23Stephen R. van den Berg  datarowtypes = ({});
56cd392014-11-17Stephen R. van den Berg  _ddescribe->broadcast();
f0522e2014-11-13Stephen R. van den Berg  }
3d6e3c2020-02-22Stephen R. van den Berg  if (aborted && delayederror && pgsqlsess && !pgsqlsess.delayederror)
8b6d742018-01-31Stephen R. van den Berg  pgsqlsess.delayederror = delayederror; // Preserve error upstream
3d6e3c2020-02-22Stephen R. van den Berg  pgsqlsess = 0; // Sever connection to upstream
ecbab12008-07-27Stephen R. van den Berg  }
05b1f52014-11-17Stephen R. van den Berg  final void _releasesession(void|string statusccomplete) {
86ee0d2020-05-18Stephen R. van den Berg  int aborted = statusccomplete == "ABORT" ? 2 : 0;
f66d602017-11-18Stephen R. van den Berg  c->runningportals[this] = 0;
42dcee2018-05-05Stephen R. van den Berg  if (statusccomplete && !statuscmdcomplete) { Thread.MutexKey lock = _ddescribemux->lock();
f66d602017-11-18Stephen R. van den Berg  statuscmdcomplete = statusccomplete;
42dcee2018-05-05Stephen R. van den Berg  _ddescribe->broadcast(); }
f66d602017-11-18Stephen R. van den Berg  inflight = 0;
c02c1d2017-06-03Stephen R. van den Berg  conxsess plugbuffer;
acc6dd2020-05-14Stephen R. van den Berg  array(Thread.MutexKey) reflock = ({ 0 }); for (;;) {
86ee0d2020-05-18Stephen R. van den Berg  reflock[0] = closemux->lock(aborted); if (!catch(plugbuffer = c->start(reflock)))
acc6dd2020-05-14Stephen R. van den Berg  if (plugbuffer) plugbuffer->sendcmd(_closeportal(plugbuffer, reflock));
86ee0d2020-05-18Stephen R. van den Berg  else { PD("Releasesession retry closemux %O\n", _portalname);
acc6dd2020-05-14Stephen R. van den Berg  continue;
86ee0d2020-05-18Stephen R. van den Berg  }
acc6dd2020-05-14Stephen R. van den Berg  break; }
a809712019-11-01Stephen R. van den Berg  reflock[0] = 0;
1ccfab2018-05-21Stephen R. van den Berg  if (_state < CLOSED) { stmtifkey = 0;
86cd342018-05-30Stephen R. van den Berg  _state = CLOSED;
1ccfab2018-05-21Stephen R. van den Berg  }
0889172017-11-09Stephen R. van den Berg  datarows->write(1); // Signal EOF
86ee0d2020-05-18Stephen R. van den Berg  releaseconditions(aborted);
04f1302014-11-14Stephen R. van den Berg  }
c071bc2017-11-05Henrik Grubbström (Grubba)  protected void _destruct() {
86ee0d2020-05-18Stephen R. van den Berg  if (_state < CLOSED) catch { // inside destructors, exceptions don't work _releasesession("ABORT"); };
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
a809712019-11-01Stephen R. van den Berg  final int _sendexecute(int fetchlimit,
62f1ba2018-05-08Stephen R. van den Berg  void|array(Thread.MutexKey)|bufcon|conxsess plugbuffer) {
16ce8b2014-09-12Stephen R. van den Berg  int flushmode;
62f1ba2018-05-08Stephen R. van den Berg  array(Thread.MutexKey) reflock;
b1fe662017-06-27Stephen R. van den Berg  PD("Execute portal %O fetchlimit %d transtype %d\n", _portalname, fetchlimit, transtype);
62f1ba2018-05-08Stephen R. van den Berg  if (arrayp(plugbuffer)) { reflock = plugbuffer;
acc6dd2020-05-14Stephen R. van den Berg  if (!(plugbuffer = c->start(reflock)))
a809712019-11-01Stephen R. van den Berg  return 0; // Found potential deadlock, release and try again
62f1ba2018-05-08Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  CHAIN(plugbuffer)->add_int8('E')->add_hstring(({_portalname, 0}), 4, 8)
c9c7fc2014-11-26Stephen R. van den Berg  ->add_int32(fetchlimit);
55a08b2017-06-25Stephen R. van den Berg  if (!fetchlimit) { if (transtype != NOTRANS)
f66d602017-11-18Stephen R. van den Berg  pgsqlsess.intransaction = transtype == TRANSBEGIN;
62f1ba2018-05-08Stephen R. van den Berg  flushmode = _closeportal(plugbuffer, reflock) == SYNCSEND
55a08b2017-06-25Stephen R. van den Berg  || transtype == TRANSEND ? SYNCSEND : FLUSHSEND; } else
f66d602017-11-18Stephen R. van den Berg  inflight += fetchlimit, flushmode = FLUSHSEND; plugbuffer->sendcmd(flushmode, this);
a809712019-11-01Stephen R. van den Berg  if (reflock) reflock[0] = 0; return 1;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
1cfbdc2017-12-02Stephen R. van den Berg  inline private array setuptimeout() { return local_backend->call_out(gottimeout, timeout); }
770af12018-05-17Stephen R. van den Berg  inline private void scuttletimeout(array cid) {
b05ab22018-05-17Stephen R. van den Berg  if (local_backend) local_backend->remove_call_out(cid); }
11b13b2014-08-16Martin Nilsson  //! @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. //!
16ce8b2014-09-12Stephen R. van den Berg  //! @seealso //! @[eof()], @[send_row()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final array(mixed) fetch_row() {
16ce8b2014-09-12Stephen R. van den Berg  int|array datarow;
a809712019-11-01Stephen R. van den Berg  if (!this) // If object already destructed, return fast return 0;
ed6f022018-05-05Stephen R. van den Berg  replenishrows();
f66d602017-11-18Stephen R. van den Berg  if (arrayp(datarow = datarows->try_read()))
16ce8b2014-09-12Stephen R. van den Berg  return datarow;
f66d602017-11-18Stephen R. van den Berg  if (!eoffound) { if (!datarow) { PD("%O Block for datarow\n", _portalname);
1cfbdc2017-12-02Stephen R. van den Berg  array cid = setuptimeout();
f66d602017-11-18Stephen R. van den Berg  PT(datarow = datarows->read());
770af12018-05-17Stephen R. van den Berg  if (!this) // If object already destructed, return fast return 0;
b05ab22018-05-17Stephen R. van den Berg  scuttletimeout(cid);
f66d602017-11-18Stephen R. van den Berg  if (arrayp(datarow))
5cb4f22014-11-28Stephen R. van den Berg  return datarow; }
f66d602017-11-18Stephen R. van den Berg  eoffound = 1;
05b1f52014-11-17Stephen R. van den Berg  datarows->write(1); // Signal EOF for other threads
615d422014-10-31Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror(); return 0; } //! @returns //! Multiple result rows at a time (at least one). //! //! 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()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final array(array(mixed)) fetch_row_array() {
f66d602017-11-18Stephen R. van den Berg  if (eoffound)
16ce8b2014-09-12Stephen R. van den Berg  return 0;
ed6f022018-05-05Stephen R. van den Berg  replenishrows();
f66d602017-11-18Stephen R. van den Berg  array(array|int) datarow = datarows->try_read_array();
574c652017-11-22Stephen R. van den Berg  if (!sizeof(datarow)) {
1cfbdc2017-12-02Stephen R. van den Berg  array cid = setuptimeout();
f66d602017-11-18Stephen R. van den Berg  PT(datarow = datarows->read_array());
770af12018-05-17Stephen R. van den Berg  if (!this) // If object already destructed, return fast return 0;
b05ab22018-05-17Stephen R. van den Berg  scuttletimeout(cid);
5cb4f22014-11-28Stephen R. van den Berg  }
ed6f022018-05-05Stephen R. van den Berg  replenishrows();
f66d602017-11-18Stephen R. van den Berg  if (arrayp(datarow[-1]))
16ce8b2014-09-12Stephen R. van den Berg  return datarow;
e952582018-01-05Stephen R. van den Berg  do datarow = datarow[..<1]; // Swallow EOF mark(s) while (sizeof(datarow) && !arrayp(datarow[-1]));
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror();
f66d602017-11-18Stephen R. van den Berg  eoffound = 1;
05b1f52014-11-17Stephen R. van den Berg  datarows->write(1); // Signal EOF for other threads
e952582018-01-05Stephen R. van den Berg  return datarow;
16ce8b2014-09-12Stephen R. van den Berg  }
ae952f2014-11-10Stephen R. van den Berg  //! @param copydata
11b13b2014-08-16Martin Nilsson  //! When using COPY FROM STDIN, this method accepts a string or an //! array of strings to be processed by the COPY command; when sending //! the amount of data sent per call does not have to hit row or column //! boundaries. //! //! The COPY FROM STDIN sequence needs to be completed by either
16ce8b2014-09-12Stephen R. van den Berg  //! explicitly or implicitly destroying the result object, or by passing no //! argument to this method.
11b13b2014-08-16Martin Nilsson  //! //! @seealso
16ce8b2014-09-12Stephen R. van den Berg  //! @[fetch_row()], @[eof()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final void send_row(void|string|array(string) copydata) {
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror();
f66d602017-11-18Stephen R. van den Berg  if (copydata) {
16ce8b2014-09-12Stephen R. van den Berg  PD("CopyData\n");
f66d602017-11-18Stephen R. van den Berg  void|bufcon|conxsess cs = c->start();
c02c1d2017-06-03Stephen R. van den Berg  CHAIN(cs)->add_int8('d')->add_hstring(copydata, 4, 4); cs->sendcmd(SENDOUT);
16ce8b2014-09-12Stephen R. van den Berg  } else _releasesession(); }
8384822014-11-12Stephen R. van den Berg  private void run_result_cb(
63fcae2017-11-22Stephen R. van den Berg  function(Result, array(mixed), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  array(mixed) args) { int|array datarow;
f66d602017-11-18Stephen R. van den Berg  for (;;) {
1cfbdc2017-12-02Stephen R. van den Berg  array cid = setuptimeout();
f66d602017-11-18Stephen R. van den Berg  PT(datarow = datarows->read());
770af12018-05-17Stephen R. van den Berg  if (!this) // If object already destructed, return fast return 0;
b05ab22018-05-17Stephen R. van den Berg  scuttletimeout(cid);
f66d602017-11-18Stephen R. van den Berg  if (!arrayp(datarow))
5cb4f22014-11-28Stephen R. van den Berg  break;
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow, @args);
5cb4f22014-11-28Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  eoffound = 1;
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, 0, @args);
16ce8b2014-09-12Stephen R. van den Berg  } //! Sets up a callback for every row returned from the database. //! First argument passed is the resultobject itself, second argument //! is the result row (zero on EOF). //! //! @seealso //! @[fetch_row()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final void set_result_callback(
63fcae2017-11-22Stephen R. van den Berg  function(Result, array(mixed), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  mixed ... args) {
f66d602017-11-18Stephen R. van den Berg  if (callback) Thread.Thread(run_result_cb, callback, args);
16ce8b2014-09-12Stephen R. van den Berg  }
8384822014-11-12Stephen R. van den Berg  private void run_result_array_cb(
63fcae2017-11-22Stephen R. van den Berg  function(Result, array(array(mixed)), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  array(mixed) args) { array(array|int) datarow;
f66d602017-11-18Stephen R. van den Berg  for (;;) {
1cfbdc2017-12-02Stephen R. van den Berg  array cid = setuptimeout();
f66d602017-11-18Stephen R. van den Berg  PT(datarow = datarows->read_array());
770af12018-05-17Stephen R. van den Berg  if (!this) // If object already destructed, return fast return 0;
b05ab22018-05-17Stephen R. van den Berg  scuttletimeout(cid);
f66d602017-11-18Stephen R. van den Berg  if (!datarow || !arrayp(datarow[-1]))
5cb4f22014-11-28Stephen R. van den Berg  break;
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow, @args);
5cb4f22014-11-28Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  eoffound = 1; if (sizeof(datarow)>1) callout(callback, 0, this, datarow = datarow[..<1], @args);
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, 0, @args);
16ce8b2014-09-12Stephen R. van den Berg  } //! Sets up a callback for sets of rows returned from the database. //! First argument passed is the resultobject itself, second argument //! is the array of result rows (zero on EOF). //! //! @seealso //! @[fetch_row()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final void set_result_array_callback(
63fcae2017-11-22Stephen R. van den Berg  function(Result, array(array(mixed)), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  mixed ... args) {
f66d602017-11-18Stephen R. van den Berg  if (callback) Thread.Thread(run_result_array_cb, callback, args); } }; class proxy { final int _fetchlimit = FETCHLIMIT;
62f1ba2018-05-08Stephen R. van den Berg  final MUTEX unnamedportalmux; final MUTEX unnamedstatement;
f66d602017-11-18Stephen R. van den Berg  private Thread.MutexKey termlock; final Thread.ResourceCount portalsinflight, statementsinflight; final int(0..1) wasparallelisable; final int(0..1) intransaction; final conxion c; private string cancelsecret; private int backendpid; final int(-128..127) backendstatus; final mapping(string:mixed) options;
3d6cb02020-02-23Stephen R. van den Berg  private array(string) lastmessage = ({});
f66d602017-11-18Stephen R. van den Berg  final int(0..1) clearmessage; final int(0..1) untolderror;
3d6e3c2020-02-22Stephen R. van den Berg  final mixed delayederror;
f66d602017-11-18Stephen R. van den Berg  private mapping(string:array(mixed)) notifylist = ([]); final mapping(string:string) runtimeparameter; final mapping(string:mapping(string:mixed)) prepareds = ([]); final int pportalcount; final int totalhits; final int msgsreceived; // Number of protocol messages received final int bytesreceived; // Number of bytes received final int warningsdropcount; // Number of uncollected warnings private int warningscollected; final int(0..1) invalidatecache; private Thread.Queue qportals;
aa81772018-01-26Stephen R. van den Berg  final function (:void) readyforquery_cb;
f66d602017-11-18Stephen R. van den Berg  final string host; final int(0..65535) port;
63fcae2017-11-22Stephen R. van den Berg  final string database, user, pass;
d921db2017-11-30Henrik Grubbström (Grubba)  private Crypto.Hash.SCRAM SASLcontext;
f66d602017-11-18Stephen R. van den Berg  final Thread.Condition waitforauthready;
62f1ba2018-05-08Stephen R. van den Berg  final MUTEX shortmux;
f66d602017-11-18Stephen R. van den Berg  final int readyforquerycount;
83fabd2019-04-17Henrik Grubbström (Grubba)  protected string _sprintf(int type) {
f66d602017-11-18Stephen R. van den Berg  string res; switch (type) { case 'O': res = sprintf(DRIVERNAME".proxy(%s@%s:%d/%s,%d,%d)",
81790f2017-11-28Stephen R. van den Berg  user, host, port, database, c && c->socket && c->socket->query_fd(),
f66d602017-11-18Stephen R. van den Berg  backendpid); break; } return res; }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void create(void|string host, void|string database,
f66d602017-11-18Stephen R. van den Berg  void|string user, void|string pass, void|mapping(string:mixed) options) { if (this::pass = pass) { String.secure(pass); pass = "CENSORED"; } this::user = user; this::database = database; this::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;
3fbf572018-12-16Stephen R. van den Berg  register_backend(this);
62f1ba2018-05-08Stephen R. van den Berg  shortmux = MUTEX();
f66d602017-11-18Stephen R. van den Berg  PD("Connect\n"); waitforauthready = Thread.Condition(); qportals = Thread.Queue(); readyforquerycount = 1; qportals->write(1); if (!(c = conxion(this, qportals, 0))) error("Couldn't connect to database on %s:%d\n", host, port); runtimeparameter = ([]);
62f1ba2018-05-08Stephen R. van den Berg  unnamedportalmux = MUTEX(); unnamedstatement = MUTEX();
f66d602017-11-18Stephen R. van den Berg  readyforquery_cb = connect_cb; portalsinflight = Thread.ResourceCount(); statementsinflight = Thread.ResourceCount(); wasparallelisable = 0; } final int is_open() {
81790f2017-11-28Stephen R. van den Berg  return c && c->socket && c->socket->is_open();
f66d602017-11-18Stephen R. van den Berg  } final string geterror(void|int clear) { untolderror = 0; string s = lastmessage * "\n"; if (clear)
3d6cb02020-02-23Stephen R. van den Berg  lastmessage = ({});
f66d602017-11-18Stephen R. van den Berg  warningscollected = 0; return sizeof(s) && s; } final string host_info() { return sprintf("fd:%d TCP/IP %s:%d PID %d", c ? c->socket->query_fd() : -1, host, port, backendpid); } final void cancelquery() { if (cancelsecret > "") { PD("CancelRequest\n"); conxion lcon = conxion(this, 0, 2); #ifdef PG_DEBUG mixed err = #endif catch(lcon->add_int32(16)->add_int32(PG_PROTOCOL(1234, 5678)) ->add_int32(backendpid)->add(cancelsecret)->sendcmd(FLUSHSEND)); #ifdef PG_DEBUG if (err)
86ee0d2020-05-18Stephen R. van den Berg  PD("CancelRequest failed to connect %s\n", describe_backtrace(err));
f66d602017-11-18Stephen R. van den Berg #endif destruct(lcon); // Destruct explicitly to avoid delayed close #ifdef PG_DEBUGMORE PD("Closetrace %O\n", backtrace()); #endif } else error("Connection not established, cannot cancel any query\n"); } 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); }
63fcae2017-11-22Stephen R. van den Berg  private array(string) showbindings(Result portal) {
3d6cb02020-02-23Stephen R. van den Berg  return portal ? portal._showbindings() : ({});
f66d602017-11-18Stephen R. van den Berg  } 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||"")}); }
63fcae2017-11-22Stephen R. van den Berg  private int|Result portal; // state information procmessage
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  private string datarowdebug; private int datarowdebugcount;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  final void processloop(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 - censoroptions; string name; mixed value) plugbuffer->add(name, 0, (string)value, 0); plugbuffer->add_int8(0); PD("%O\n", (string)plugbuffer); void|bufcon|conxsess 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 {
bea3fb2018-05-26Stephen R. van den Berg  conxion ci = c; // cache value conxiin cr = ci->i; // cache value
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("Processloop\n");
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  void showportalstack(string label) { PD(sprintf(">>>>>>>>>>> Portalstack %s: %O\n", label, portal));
63fcae2017-11-22Stephen R. van den Berg  foreach (qportals->peek_array(); ; int|Result qp)
f66d602017-11-18Stephen R. van den Berg  PD(" =========== Portal: %O\n", qp); PD("<<<<<<<<<<<<<< Portalstack end\n"); };
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  void showportal(int msgtype) { if (objectp(portal)) PD("%d<%O %d %c switch portal\n", ci->socket->query_fd(), portal._portalname, ++ci->queueinidx, msgtype);
e4ff9f2018-05-23Stephen R. van den Berg  else if (portal > 0)
f66d602017-11-18Stephen R. van den Berg  PD("%d<Sync %d %d %c portal\n", ci->socket->query_fd(), ++ci->queueinidx, portal, msgtype); };
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  int msgisfatal(mapping(string:string) msgresponse) {
aa81772018-01-26Stephen R. van den Berg  int isfatal = (has_prefix(msgresponse.C, "53") || has_prefix(msgresponse.C, "3D") || has_prefix(msgresponse.C, "57P")) && MAGICTERMINATE; if (isfatal && !terminating) // Run the callback once per lost connection runcallback(backendpid, "_lost", ""); return isfatal;
f66d602017-11-18Stephen R. van den Berg  }; for (;;) { err = catch {
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (!portal && datarowdebug) { PD("%s rows %d\n", datarowdebug, datarowdebugcount); datarowdebug = 0; datarowdebugcount = 0; }
6534f92018-01-20Stephen R. van den Berg #endif #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("LOOPTOP");
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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; return; // Terminate thread, wait for callback } } int msgtype = cr->read_int8(); if (!portal) { portal = qportals->try_read();
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  showportal(msgtype);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  } 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();
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  array a; msglen -= 4 + 1 + 2 + 2 * cols; foreach (a = allocate(cols, ([])); ; mapping m) m.type = cr->read_int16();
6534f92018-01-20Stephen R. van den Berg #else
f66d602017-11-18Stephen R. van den Berg  cr->consume(cols << 1);
6534f92018-01-20Stephen R. van den Berg #endif // Discard column info, and make it line oriented
f66d602017-11-18Stephen R. van den Berg  return ({ ({(["name":"line"])}), ({bintext?BYTEAOID:TEXTOID}) }); }; array(string) reads() {
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen < 1) errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif
3d6cb02020-02-23Stephen R. van den Berg  array ret = ({}), aw = ({0});
f66d602017-11-18Stephen R. van den Berg  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) { void|bufcon|conxsess 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");
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen < 4) errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif #define md5hex(x) String.string2hex(Crypto.MD5.hash(x))
f66d602017-11-18Stephen R. van den Berg  authresponse(({"md5", md5hex(md5hex(pass + user) + cr->read(msglen)), 0}));
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen<1) errtype = PROTOCOLERROR; msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; case 10: { string word; PD("AuthenticationSASL\n"); k = 0; while (sizeof(word = cr->read_cstring())) { switch (word) { case "SCRAM-SHA-256": k = 1; }
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= sizeof(word) + 1; if (msglen < 1) break;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  } if (k) {
d921db2017-11-30Henrik Grubbström (Grubba)  SASLcontext = Crypto.SHA256.SCRAM();
f66d602017-11-18Stephen R. van den Berg  word = SASLcontext.client_1(); authresponse(({ "SCRAM-SHA-256", 0, sprintf("%4c", sizeof(word)), word })); } else errtype = PROTOCOLUNSUPPORTED;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen != 1) errtype = PROTOCOLERROR; msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; } case 11: { PD("AuthenticationSASLContinue\n"); string response; if (response = SASLcontext.client_2(cr->read(msglen), pass)) authresponse(response); else errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; } case 12: PD("AuthenticationSASLFinal\n"); if (SASLcontext.client_3(cr->read(msglen))) SASLcontext = 0; // Clears context and approves server else errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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);
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("BackendKeyData %O\n", cancelsecret); msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; case 'S': { PD("ParameterStatus "); msglen -= 4; array(string) ts = reads();
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (sizeof(ts) == 2) {
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  runtimeparameter[ts[0]] = ts[1];
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("%O=%O\n", ts[0], ts[1]); } else errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; } case '3':
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("CloseComplete\n"); msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; case 'Z': { backendstatus = cr->read_int8();
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= 4 + 1; PD("ReadyForQuery %c\n", backendstatus);
6534f92018-01-20Stephen R. van den Berg #endif #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("READYFORQUERY");
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  int keeplooking; do for (keeplooking = 0; objectp(portal); portal = qportals->read()) {
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  showportal(msgtype);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  if (backendstatus == 'I' && intransaction && portal->transtype != TRANSEND) keeplooking = 1; portal->_purgeportal(); } while (keeplooking && (portal = qportals->read())); if (backendstatus == 'I') intransaction = 0;
63fcae2017-11-22Stephen R. van den Berg  foreach (qportals->peek_array(); ; Result qp) {
f66d602017-11-18Stephen R. van den Berg  if (objectp(qp) && qp._synctransact && qp._synctransact <= portal) { PD("Checking portal %O %d<=%d\n", qp._portalname, qp._synctransact, portal); qp->_purgeportal(); } } portal = 0;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("AFTER READYFORQUERY");
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  readyforquerycount--;
6476a42020-05-12Stephen R. van den Berg  function (:void) cb;
acc6dd2020-05-14Stephen R. van den Berg  destruct(waitforauthready);
6476a42020-05-12Stephen R. van den Berg  if (cb = readyforquery_cb) readyforquery_cb = 0, cb();
f66d602017-11-18Stephen R. van den Berg  break; } case '1':
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("ParseComplete portal %O\n", portal); msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; case 't': { array a;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  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);
6534f92018-01-20Stephen R. van den Berg #else
f66d602017-11-18Stephen R. van den Berg  a = cr->read_ints(cr->read_int16(), 4);
6534f92018-01-20Stephen R. van den Berg #endif #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  PD("%O\n", a);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  if (portal._tprepared) portal._tprepared.datatypeoid = a; Thread.Thread(portal->_preparebind, a); break; } case 'T': { array a, at; int cols = cr->read_int16();
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("RowDescription %d columns %O\n", cols, portal._query); msglen -= 4 + 2;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  at = allocate(cols); foreach (a = allocate(cols); int i; ) { string s = cr->read_cstring(); mapping(string:mixed) res = (["name":s]);
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= sizeof(s) + 1 + 4 + 2 + 4 + 2 + 4 + 2; res.tableoid = cr->read_int32() || UNDEFINED; res.tablecolattr = cr->read_int16() || UNDEFINED;
6534f92018-01-20Stephen R. van den Berg #else
f66d602017-11-18Stephen R. van den Berg  cr->consume(6);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  at[i] = cr->read_int32();
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  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();
6534f92018-01-20Stephen R. van den Berg #else
f66d602017-11-18Stephen R. van den Berg  cr->consume(8);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  a[i] = res; }
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  PD("%O\n", a);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  if (portal._forcetext) portal->_setrowdesc(a, at); // Do not consume queued portal else { portal->_processrowdesc(a, at); portal = 0; } break; } case 'n': {
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= 4; PD("NoData %O\n", portal._query);
6534f92018-01-20Stephen R. van den Berg #endif
0b714c2020-05-18Stephen R. van den Berg  portal._fetchlimit = 0; // disables subsequent Executes portal->_processrowdesc(({}), ({})); portal = 0;
f66d602017-11-18Stephen R. van den Berg  break; } case 'H': portal->_processrowdesc(@getcols()); PD("CopyOutResponse %O\n", portal. _query); break; case '2': { mapping tp;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen -= 4; PD("%O BindComplete\n", portal._portalname);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  PD("%O DataRow %d bytes\n", portal._portalname, msglen);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  datarowdebugcount++; if (!datarowdebug) datarowdebug = sprintf( "%O DataRow %d bytes", portal._portalname, msglen);
6534f92018-01-20Stephen R. van den Berg #endif #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen=
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  portal->_decodedata(msglen, runtimeparameter[CLIENT_ENCODING]); break; case 's':
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("%O PortalSuspended\n", portal._portalname); msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  portal = 0; break; case 'C': { msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen < 1) errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  string s = cr->read(msglen - 1);
6476a42020-05-12Stephen R. van den Berg  PD("%O CommandComplete %O\n", objectp(portal) && portal._portalname, s);
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (cr->read_int8()) errtype = PROTOCOLERROR; msglen = 0;
6534f92018-01-20Stephen R. van den Berg #else
f66d602017-11-18Stephen R. van den Berg  cr->consume(1);
6534f92018-01-20Stephen R. van den Berg #endif #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("COMMANDCOMPLETE");
6534f92018-01-20Stephen R. van den Berg #endif
6476a42020-05-12Stephen R. van den Berg  if (!portal._forcetext) { portal->_storetiming(); portal->_releasesession(s); portal = 0; }
f66d602017-11-18Stephen R. van den Berg  break; } case 'I':
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("EmptyQueryResponse %O\n", portal._portalname); msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #endif #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("EMPTYQUERYRESPONSE");
6534f92018-01-20Stephen R. van den Berg #endif
6476a42020-05-12Stephen R. van den Berg  if (!portal._forcetext) { portal->_releasesession(); portal = 0; }
f66d602017-11-18Stephen R. van den Berg  break; case 'd': PD("%O CopyData\n", portal._portalname); portal->_storetiming(); msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen < 0) errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  portal->_processdataready(({cr->read(msglen)}), msglen);
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  break; case 'G': portal->_releasestatement(); portal->_setrowdesc(@getcols()); PD("%O CopyInResponse\n", portal._portalname); portal._state = COPYINPROGRESS; break; case 'c':
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  PD("%O CopyDone\n", portal._portalname); msglen -= 4;
6534f92018-01-20Stephen R. van den Berg #endif
6476a42020-05-12Stephen R. van den Berg  if (!portal._forcetext) portal = 0;
f66d602017-11-18Stephen R. van den Berg  break; case 'E': {
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("ERRORRESPONSE");
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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; untolderror = 1; 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":
0e133b2020-01-20Stephen R. van den Berg  case "34000":case "08P01":
f66d602017-11-18Stephen R. van den Berg  preplastmessage(msgresponse); PD(a2nls(lastmessage)); throw(msgisfatal(msgresponse));
0e133b2020-01-20Stephen R. van den Berg  case "42P05":
f66d602017-11-18Stephen R. van den Berg  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)); }
86ee0d2020-05-18Stephen R. van den Berg  case "25P02": // Preserve last error message
f66d602017-11-18Stephen R. van den Berg  USERERROR(a2nls(lastmessage)); // Implicitly closed portal } break; } case 'N': { PD("NoticeResponse\n"); mapping(string:string) msgresponse = getresponse();
86ee0d2020-05-18Stephen R. van den Berg  switch (msgresponse.C) { default: 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); } case "25P01": // Suppress some warnings break;
f66d602017-11-18Stephen R. van den Berg  } break; } case 'A': { PD("NotificationResponse\n"); msglen -= 4 + 4; int pid = cr->read_int32(); string condition, extrainfo; { array(string) ts = reads(); switch (sizeof(ts)) {
6534f92018-01-20Stephen R. van den Berg #if PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  case 0: errtype = PROTOCOLERROR; break; default: errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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);
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  msglen = 0;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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; }
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  if (msglen) errtype = PROTOCOLERROR;
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  { 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 { void|bufcon|conxsess cs = ci->start();
3fbf572018-12-16Stephen R. van den Berg  CHAIN(cs)->add(PGSYNC)->add("X\0\0\0\4");
f66d602017-11-18Stephen R. van den Berg  cs->sendcmd(SENDOUT); }; terminating = 1; err = 0; } else if (stringp(err)) {
63fcae2017-11-22Stephen R. van den Berg  Result or;
f66d602017-11-18Stephen R. van den Berg  if (!objectp(or = portal)) or = this; if (!or.delayederror) or.delayederror = err;
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUGMORE
f66d602017-11-18Stephen R. van den Berg  showportalstack("THROWN");
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  if (objectp(portal))
21efd72019-05-29Stephen R. van den Berg  portal->_releasesession("ERROR");
f66d602017-11-18Stephen R. van den Berg  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)) {
6534f92018-01-20Stephen R. van den Berg #ifdef PG_DEBUG
f66d602017-11-18Stephen R. van den Berg  showportal(0);
6534f92018-01-20Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  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); c->purge(); };
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
f66d602017-11-18Stephen R. van den Berg  final void close() { throwdelayederror(this);
81790f2017-11-28Stephen R. van den Berg  {
cb81a82018-05-05Stephen R. van den Berg  Thread.MutexKey lock;
81790f2017-11-28Stephen R. van den Berg  if (unnamedstatement) termlock = unnamedstatement->lock(1);
cb81a82018-05-05Stephen R. van den Berg  foreach (c->runningportals; Result result; )
4333362020-05-28Stephen R. van den Berg  if (result->_state < CLOSED) catch(result->status_command_complete());
81790f2017-11-28Stephen R. van den Berg  if (c) // Prevent trivial backtraces c->close(); if (unnamedstatement) lock = unnamedstatement->lock(1); if (c) c->purge(); }
f66d602017-11-18Stephen R. van den Berg  destruct(waitforauthready); }
83fabd2019-04-17Henrik Grubbström (Grubba)  protected void _destruct() {
f66d602017-11-18Stephen R. van den Berg  string errstring; mixed err = catch(close());
3fbf572018-12-16Stephen R. van den Berg  clients[this] = 0;
f66d602017-11-18Stephen R. van den Berg  if (untolderror) { /* * 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 = geterror(1)))) werror(describe_backtrace(err)); else if (errstring && sizeof(errstring)) werror("%s\n", errstring); // Add missing terminating newline } }
aa81772018-01-26Stephen R. van den Berg  final void sendsync() {
f66d602017-11-18Stephen R. van den Berg  readyforquerycount++; c->start()->sendcmd(SYNCSEND); } private void runcallback(int pid, string condition, string extrainfo) { array cb;
aa81772018-01-26Stephen R. van den Berg  if (condition == "_lost") destruct(c);
f66d602017-11-18Stephen R. van den Berg  if ((cb = notifylist[condition] || notifylist[""]) && (pid != backendpid || sizeof(cb) > 1 && cb[1])) callout(cb[0], 0, pid, condition, extrainfo, @cb[2..]); } private inline void closestatement( bufcon|conxsess plugbuffer, string oldprep) { closestatement(plugbuffer, oldprep); }
e7c5c82014-11-24Stephen R. van den Berg };
63fcae2017-11-22Stephen R. van den Berg  #pragma deprecation_warnings //! @class sql_result //! @deprecated Result //! @endclass __deprecated__(program(Result)) sql_result = (__deprecated__(program(Result)))Result;