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"
16b8832014-11-10Stephen R. van den Berg //! The instance of the pgsql dedicated backend.
615d422014-10-31Stephen R. van den Berg final Pike.Backend local_backend = Pike.SmallBackend();
16b8832014-11-10Stephen R. van den Berg 
8384822014-11-12Stephen R. van den Berg private Thread.Mutex backendmux = Thread.Mutex();
67879d2017-11-10Stephen R. van den Berg private Thread.ResourceCount clientsregistered = Thread.ResourceCount();
16ce8b2014-09-12Stephen R. van den Berg 
4c965a2016-10-13Stephen R. van den Berg constant emptyarray = ({});
b1fe662017-06-27Stephen R. van den Berg constant describenodata
cd4b7f2017-06-28Stephen R. van den Berg  = (["datarowdesc":emptyarray, "datarowtypes":emptyarray, "datatypeoid":emptyarray]);
b840fa2014-11-19Stephen R. van den Berg final multiset censoroptions=(<"use_ssl","force_ssl", "cache_autoprepared_statements","reconnect","text_query","is_superuser", "server_encoding","server_version","integer_datetimes", "session_authorization">);
7c9d772017-07-10Stephen R. van den Berg constant stdiobuftype = typeof(Stdio.Buffer());
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 */
c138a02014-11-26Stephen 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 */
c138a02014-11-26Stephen 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
6623742017-06-17Stephen R. van den Berg  =iregexp("^\a*((SELEC|INSER)T|(UPDA|DELE)TE|(FETC|WIT)H)\a");
609b782014-11-26Stephen R. van den Berg 
55a08b2017-06-25Stephen R. van den Berg  /* Statements matching transbeginprefix will cause the driver * 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
2580812017-07-08Stephen R. van den Berg  =iregexp("^\a*(BEGIN|START)([; \t\f\r\n]|$)");
55a08b2017-06-25Stephen R. van den Berg  /* Statements matching transendprefix will cause the driver * 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
2580812017-07-08Stephen R. van den Berg  =iregexp("^\a*(COMMIT|ROLLBACK|END)([; \t\f\r\n]|$)");
55a08b2017-06-25Stephen R. van den Berg 
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
c138a02014-11-26Stephen R. van den Berg  =iregexp("^\a*((UPDA|DELE)TE|INSERT)\a|\aLIMIT\a+[1-9][; \t\f\r\n]*$"); private Regexp iregexp(string expr) { Stdio.Buffer ret=Stdio.Buffer(); foreach(expr;;int c) if(c>='A'&&c<='Z') ret->add('[',c,c+'a'-'A',']'); else if(c=='\a') // Replace with generic whitespace ret->add("[ \t\f\r\n]"); else ret->add_int8(c); return Regexp(ret->read()); }
615d422014-10-31Stephen R. van den Berg 
c02c1d2017-06-03Stephen R. van den Berg final void closestatement(bufcon|conxsess plugbuffer,string oldprep) {
615d422014-10-31Stephen R. van den Berg  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 { looponce=0; if(lock=backendmux->trylock()) { PD("Starting local backend\n");
67879d2017-11-10Stephen R. van den Berg  while (!clientsregistered->drained()) { // Autoterminate when not needed
cbac2f2016-10-12Stephen R. van den Berg  mixed err; if (err = catch(local_backend(4096.0))) werror(describe_backtrace(err)); }
16ce8b2014-09-12Stephen R. van den Berg  PD("Terminating local backend\n"); lock=0;
67879d2017-11-10Stephen R. van den Berg  looponce = !clientsregistered->drained();
16ce8b2014-09-12Stephen R. van den Berg  } } while(looponce); }
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.
67879d2017-11-10Stephen R. van den Berg final Thread.ResourceCountKey register_backend() { int startbackend = clientsregistered->drained(); Thread.ResourceCountKey key = clientsregistered->acquire(); if (startbackend)
16ce8b2014-09-12Stephen R. van den Berg  Thread.Thread(run_local_backend);
67879d2017-11-10Stephen R. van den Berg  return key;
16ce8b2014-09-12Stephen R. van den Berg }
4741cd2008-07-31Stephen R. van den Berg 
16ce8b2014-09-12Stephen R. van den Berg final void throwdelayederror(object parent) { if(mixed err=parent._delayederror) { parent._delayederror=UNDEFINED; if(stringp(err)) err=({err,backtrace()[..<2]}); throw(err);
ecbab12008-07-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg }
ecbab12008-07-27Stephen R. van den Berg 
615d422014-10-31Stephen R. van den Berg final int oidformat(int oid) { 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 } return 0; // text }
8384822014-11-12Stephen R. van den Berg private inline mixed callout(function(mixed ...:void) f,
2c98002014-11-10Stephen R. van den Berg  float|int delay,mixed ... args) { return local_backend->call_out(f,delay,@args); }
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 
67879d2017-11-10Stephen R. van den Berg  private void create(conxion _realbuffer) {
16ce8b2014-09-12Stephen 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; }
39e1c42014-11-14Stephen R. van den Berg  final bufcon start(void|int waitforreal) {
67879d2017-11-10Stephen R. van den Berg  dirty = realbuffer->stashcount->acquire();
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG 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; }
2eef0b2014-11-15Stephen R. van den Berg  final void sendcmd(int mode,void|sql_result portal) {
03dfca2014-11-18Stephen R. van den Berg  Thread.MutexKey lock=realbuffer->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;
67879d2017-11-10Stephen R. van den Berg  dirty = 0;
16ce8b2014-09-12Stephen R. van den Berg  this->clear(); 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; final Thread.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 
67879d2017-11-10Stephen R. van den Berg  protected final bool range_error(int howmuch) {
c7de862014-11-20Stephen R. van den Berg #ifdef PG_DEBUG if(howmuch<=0) error("Out of range %d\n",howmuch); #endif if(fillread) { Thread.MutexKey lock=fillreadmux->lock();
7607f52014-11-25Stephen R. van den Berg  if(!didreadcb)
0c69072014-11-25Stephen R. van den Berg  fillread.wait(lock);
7607f52014-11-25Stephen R. van den Berg  didreadcb=0;
c7de862014-11-20Stephen R. van den Berg  lock=0; } else throw(MAGICTERMINATE); return true; } 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 );
c7de862014-11-20Stephen R. van den Berg  Thread.MutexKey lock=fillreadmux->lock();
e7f7d12016-02-17Stephen R. van den Berg  if(procmsg&&id) procmsg=0,lock=0,Thread.Thread(id);
7f73f42014-12-16Stephen R. van den Berg  else if(fillread)
7607f52014-11-25Stephen R. van den Berg  didreadcb=1, fillread.signal();
c7de862014-11-20Stephen R. van den Berg  lock=0; return 0; }
67879d2017-11-10Stephen R. van den Berg  private void create() {
c7de862014-11-20Stephen R. van den Berg  i::create(); fillreadmux=Thread.Mutex(); fillread=Thread.Condition(); }
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;
03dfca2014-11-18Stephen R. van den Berg  final Thread.Mutex shortmux;
dcc15c2016-02-27Stephen R. van den Berg  private int closenext;
da3dc82016-02-24Stephen R. van den Berg 
0282f92017-10-29Stephen R. van den Berg  final sfile socket;
7f73f42014-12-16Stephen R. van den Berg  private function(void|mixed:void) connectfail;
8384822014-11-12Stephen R. van den Berg  private int towrite;
3b5d772015-12-23Stephen R. van den Berg  final multiset(function(void|mixed:void)) closecallbacks=(<>);
16ce8b2014-09-12Stephen R. van den Berg 
3c93de2014-11-03Stephen R. van den Berg  final Thread.Mutex nostash; final Thread.MutexKey started; final Thread.Queue stashqueue; final Thread.Condition stashavail; final Stdio.Buffer stash; final int stashflushmode;
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;
f0522e2014-11-13Stephen R. van den Berg  final int queueinidx=-1;
ecbab12008-07-27Stephen R. van den Berg #endif
39e1c42014-11-14Stephen R. van den Berg  private inline void queueup(sql_result portal) {
3e8df22014-10-29Stephen R. van den Berg  qportals->write(portal); portal->_synctransact=synctransact;
4f2ed82014-11-15Stephen R. van den Berg  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  }
c02c1d2017-06-03Stephen R. van den Berg  final bufcon|conxsess start(void|int waitforreal) {
16ce8b2014-09-12Stephen R. van den Berg  Thread.MutexKey lock; if(lock=(waitforreal?nostash->lock:nostash->trylock)(1)) {
b1fe662017-06-27Stephen R. van den Berg  int mode;
c02c1d2017-06-03Stephen R. van den Berg #ifdef PG_DEBUGRACE conxsess sess = conxsess(this); #endif started = lock;
03dfca2014-11-18Stephen R. van den Berg  lock=shortmux->lock();
67879d2017-11-10Stephen R. van den Berg  stashcount->wait_till_drained(lock);
b1fe662017-06-27Stephen R. van den Berg  mode = getstash(KEEP);
16ce8b2014-09-12Stephen R. van den Berg  lock=0;
b1fe662017-06-27Stephen R. van den Berg  if (mode > KEEP) sendcmd(mode); // Force out stash to the server
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  }
d1b3c52017-11-09Stephen R. van den Berg  return 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 towrite -= output_to(socket, towrite); 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) { if (sizeof(stash)) { add(stash); stash->clear(); foreach (stashqueue->try_read_array();; int|sql_result portal) if (intp(portal)) qportals->write(synctransact++); else queueup(portal);
a123c42017-07-01Stephen R. van den Berg  PD("%d>Got stash mode %d > %d\n", socket->query_fd(), stashflushmode, mode); if (stashflushmode > mode) mode = stashflushmode;
b1fe662017-06-27Stephen R. van den Berg  stashflushmode = KEEP; } return mode; }
4c965a2016-10-13Stephen R. van den Berg  final void sendcmd(void|int mode, void|sql_result portal) { Thread.MutexKey lock;
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:
b1fe662017-06-27Stephen R. van den Berg  PD("%d>%O %d Queue simplequery %d bytes\n", socket->query_fd(), portal._portalname, ++queueoutidx, sizeof(this));
4c965a2016-10-13Stephen R. van den Berg  mode = FLUSHSEND;
35dc282014-11-13Stephen R. van den Berg  } qportals->write(synctransact++);
b1fe662017-06-27Stephen R. van den Berg  } while(0); lock = shortmux->lock(); mode = getstash(mode);
3c93de2014-11-03Stephen R. van den Berg  catch {
3e8df22014-10-29Stephen R. van den Berg outer:
3c93de2014-11-03Stephen R. van den Berg  do { switch(mode) { default:
4c965a2016-10-13Stephen R. van den Berg  PD("%d>Skip flush %d Queue %O\n", socket->query_fd(), mode, (string)this);
3c93de2014-11-03Stephen R. van den Berg  break outer;
2eef0b2014-11-15Stephen R. van den Berg  case FLUSHSEND:
3c93de2014-11-03Stephen R. van den Berg  PD("Flush\n"); add(PGFLUSH);
2eef0b2014-11-15Stephen R. van den Berg  case SENDOUT:;
3c93de2014-11-03Stephen R. van den Berg  } if(towrite=sizeof(this)) {
4f2ed82014-11-15Stephen R. van den Berg  PD("%d>Sendcmd %O\n",socket->query_fd(),((string)this)[..towrite-1]);
3c93de2014-11-03Stephen R. van den Berg  towrite-=output_to(socket,towrite); } } while(0);
2521d22014-11-19Stephen R. van den Berg  lock=started=0;
3c93de2014-11-03Stephen R. van den Berg  return; };
2521d22014-11-19Stephen R. van den Berg  lock=0;
7f73f42014-12-16Stephen R. van den Berg  catch(connectfail());
ecbab12008-07-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  final int close() {
32857b2016-02-28Stephen R. van den Berg  if(!closenext && nostash) { closenext=1; Thread.MutexKey lock=i->fillreadmux->lock();
b95bf72016-02-17Stephen R. van den Berg  if(i->fillread) { // Delayed close() after flushing the output buffer i->fillread.signal(); i->fillread=0; } lock=0;
810fb92016-02-25Stephen 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  }
67879d2017-11-10Stephen R. van den Berg  private void _destruct() {
8e64d32016-10-14Stephen R. van den Berg  PD("%d>Close conxion %d\n", socket ? socket->query_fd() : -1, !!nostash);
8469442016-12-06Stephen R. van den Berg  int|.pgsql_util.sql_result portal;
e288132017-07-01Stephen R. van den Berg  if (qportals) // CancelRequest does not use qportals while (portal = qportals->try_read()) if (objectp(portal)) portal->_purgeportal();
32857b2016-02-28Stephen R. van den Berg  if(nostash) { catch {
4e491d2016-04-01Stephen R. van den Berg  while(sizeof(closecallbacks)) foreach(closecallbacks;function(void|mixed:void) closecb;) closecb();
32857b2016-02-28Stephen R. van den Berg  destruct(nostash);
c765212016-10-15Stephen R. van den Berg  socket->set_nonblocking(); // Drop all callbacks
32857b2016-02-28Stephen R. van den Berg  PD("%d>Close socket\n",socket->query_fd()); socket->close(); }; }
7f73f42014-12-16Stephen R. van den Berg  connectfail=0;
ecbab12008-07-27Stephen R. van den Berg  }
7f73f42014-12-16Stephen R. van den Berg  final void connectloop(object pgsqlsess, int nossl) {
16ce8b2014-09-12Stephen R. van den Berg  mixed err=catch { for(;;clear()) { socket->connect(pgsqlsess._host,pgsqlsess._port);
fc7f092014-06-01Martin Nilsson #if constant(SSL.File)
16ce8b2014-09-12Stephen R. van den Berg  if(!nossl && !pgsqlsess->nossl
1412f32014-11-13Stephen R. van den Berg  && (pgsqlsess._options.use_ssl || pgsqlsess._options.force_ssl)) {
16ce8b2014-09-12Stephen R. van den Berg  PD("SSLRequest\n"); start()->add_int32(8)->add_int32(PG_PROTOCOL(1234,5679))
2eef0b2014-11-15Stephen R. van den Berg  ->sendcmd(SENDOUT);
16ce8b2014-09-12Stephen R. van den Berg  switch(read_int8()) { case 'S': object fcon=SSL.File(socket,SSL.Context()); if(fcon->connect()) { socket=fcon; 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();
16ce8b2014-09-12Stephen R. van den Berg  pgsqlsess.nossl=1; continue; case 'N':
1412f32014-11-13Stephen 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", pgsqlsess.host,pgsqlsess.port); } } #else
1412f32014-11-13Stephen 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", pgsqlsess.host,pgsqlsess.port); #endif break; }
8469442016-12-06Stephen R. van den Berg  connectfail=pgsqlsess->_connectfail;
3c93de2014-11-03Stephen R. van den Berg  if(!socket->is_open())
51f1ce2015-09-06Martin Nilsson  error(strerror(socket->errno())+".\n");
16ce8b2014-09-12Stephen R. van den Berg  socket->set_backend(local_backend);
c7de862014-11-20Stephen R. van den Berg  socket->set_buffer_mode(i,0);
5ac9e02014-12-02Stephen R. van den Berg  socket->set_nonblocking(i->read_cb,write_cb,close);
9d0a682016-10-12Stephen R. van den Berg  if (nossl != 2) { connectfail=pgsqlsess->_connectfail; Thread.Thread(pgsqlsess->_processloop,this); }
16ce8b2014-09-12Stephen R. van den Berg  return; };
7f73f42014-12-16Stephen R. van den Berg  catch(connectfail(err));
4808742014-11-14Stephen R. van den Berg  }
74b59e2015-12-20Martin Nilsson  private string _sprintf(int type) {
4808742014-11-14Stephen R. van den Berg  string res=UNDEFINED; switch(type) { case 'O':
60f4ad2016-02-17Stephen R. van den Berg  int fd=-1; if(socket) catch(fd=socket->query_fd());
39e1c42014-11-14Stephen R. van den Berg  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",
60f4ad2016-02-17Stephen 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  }
67879d2017-11-10Stephen R. van den Berg  private void create(object 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;
0282f92017-10-29Stephen R. van den Berg  socket=sfile();
c7de862014-11-20Stephen R. van den Berg  i=conxiin();
72d9bf2014-11-18Stephen R. van den Berg  shortmux=Thread.Mutex();
2521d22014-11-19Stephen R. van den Berg  nostash=Thread.Mutex();
dcc15c2016-02-27Stephen R. van den Berg  closenext = 0;
2521d22014-11-19Stephen R. van den Berg  stashavail=Thread.Condition(); stashqueue=Thread.Queue();
16ce8b2014-09-12Stephen R. van den Berg  stash=Stdio.Buffer();
67879d2017-11-10Stephen R. van den Berg  stashcount = Thread.ResourceCount();
7f73f42014-12-16Stephen 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;
67879d2017-11-10Stephen R. van den Berg  private 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; } final void sendcmd(int mode,void|sql_result portal) { chain->sendcmd(mode, portal); chain = 0; }
67879d2017-11-10Stephen R. van den Berg  private 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
0412962009-01-19Stephen R. van den Berg //! the noted differences it behaves the same as @[Sql.sql_result]. //! //! @seealso
f4c9d62009-02-15Stephen R. van den Berg //! @[Sql.sql_result], @[Sql.pgsql], @[Sql.Sql], @[Sql.pgsql()->big_query()]
39e1c42014-11-14Stephen R. van den Berg class sql_result {
ecbab12008-07-27Stephen R. van den Berg 
b52b452017-03-15Henrik Grubbström (Grubba)  inherit __builtin.Sql.Result;
8384822014-11-12Stephen R. van den Berg  private object pgsqlsess; private int eoffound;
39e1c42014-11-14Stephen R. van den Berg  private conxion c;
c7de862014-11-20Stephen R. van den Berg  private conxiin cr;
3c93de2014-11-03Stephen R. van den Berg  final mixed _delayederror;
2eef0b2014-11-15Stephen R. van den Berg  final int _state;
3c93de2014-11-03Stephen R. van den Berg  final int _fetchlimit;
aed7142014-11-18Stephen R. van den Berg  private int alltext;
3c93de2014-11-03Stephen R. van den Berg  final int _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 rowsreceived; private int inflight; private int portalbuffersize; private Thread.Mutex closemux; 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;
2e90332014-11-25Stephen R. van den Berg  private 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; final Thread.Mutex _ddescribemux; final Thread.MutexKey _unnamedportalkey,_unnamedstatementkey; 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) {
11b13b2014-08-16Martin Nilsson  string res=UNDEFINED;
16ce8b2014-09-12Stephen R. van den Berg  switch(type) { case 'O':
60f4ad2016-02-17Stephen R. van den Berg  int fd=-1; if(c&&c->socket) catch(fd=c->socket->query_fd());
05b1f52014-11-17Stephen R. van den Berg  res=sprintf("sql_result state: %d numrows: %d eof: %d inflight: %d\n"
16ce8b2014-09-12Stephen R. van den Berg  "query: %O\n"
1a02382014-11-18Stephen R. van den Berg  "fd: %O portalname: %O datarows: %d"
55a08b2017-06-25Stephen R. van den Berg  " synctransact: %d laststatus: %s\n",
05b1f52014-11-17Stephen R. van den Berg  _state,rowsreceived,eoffound,inflight,
60f4ad2016-02-17Stephen R. van den Berg  _query,fd,_portalname,datarowtypes&&sizeof(datarowtypes),
55a08b2017-06-25Stephen R. van den Berg  _synctransact,
05b1f52014-11-17Stephen R. van den Berg  statuscmdcomplete||(_unnamedstatementkey?"*parsing*":""));
16ce8b2014-09-12Stephen R. van den Berg  break;
11b13b2014-08-16Martin Nilsson  } return res;
ecbab12008-07-27Stephen R. van den Berg  }
39e1c42014-11-14Stephen R. van den Berg  protected void create(object _pgsqlsess,conxion _c,string query,
5cb4f22014-11-28Stephen R. van den Berg  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;
c7de862014-11-20Stephen R. van den Berg  cr = (c = _c)->i;
11b13b2014-08-16Martin Nilsson  _query = query;
05b1f52014-11-17Stephen R. van den Berg  datarows = Thread.Queue();
615d422014-10-31Stephen R. van den Berg  _ddescribe=Thread.Condition(); _ddescribemux=Thread.Mutex();
16ce8b2014-09-12Stephen R. van den Berg  closemux=Thread.Mutex();
05b1f52014-11-17Stephen 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;
c071bc2017-11-05Henrik Grubbström (Grubba)  c->closecallbacks+=(<_destruct>);
55a08b2017-06-25Stephen R. van den Berg  transtype = _transtype;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
11b13b2014-08-16Martin Nilsson  //! Returns the command-complete status for this query. //! //! @seealso //! @[affected_rows()] //! //! @note //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final string status_command_complete() {
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 //! @[status_command_complete()] //! //! @note //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int affected_rows() {
11b13b2014-08-16Martin Nilsson  int rows;
05b1f52014-11-17Stephen R. van den Berg  if(statuscmdcomplete) sscanf(statuscmdcomplete,"%*s %d",rows);
11b13b2014-08-16Martin Nilsson  return rows; }
ecbab12008-07-27Stephen R. van den Berg 
aed7142014-11-18Stephen R. van den Berg  final void _storetiming() { if(_tprepared) { _tprepared.trun=gethrtime()-_tprepared.trunstart; m_delete(_tprepared,"trunstart"); _tprepared = UNDEFINED; } }
8384822014-11-12Stephen R. van den Berg  private void waitfordescribe() {
615d422014-10-31Stephen R. van den Berg  Thread.MutexKey lock=_ddescribemux->lock();
5318352014-11-22Stephen R. van den Berg  if(!datarowtypes)
d4ef202014-12-01Stephen R. van den Berg  PT(_ddescribe->wait(lock));
615d422014-10-31Stephen R. van den Berg  lock=0; }
11b13b2014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->num_fields()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int num_fields() {
5318352014-11-22Stephen R. van den Berg  if(!datarowtypes)
615d422014-10-31Stephen R. van den Berg  waitfordescribe(); trydelayederror();
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 //! @[Sql.sql_result()->num_rows()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final int num_rows() {
615d422014-10-31Stephen R. van den Berg  trydelayederror();
05b1f52014-11-17Stephen R. van den Berg  return rowsreceived;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
8384822014-11-12Stephen R. van den Berg  private inline void trydelayederror() {
16ce8b2014-09-12Stephen R. van den Berg  if(_delayederror) throwdelayederror(this); }
11b13b2014-08-16Martin Nilsson  //! @seealso //! @[Sql.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 //! @[Sql.sql_result()->fetch_fields()]
e7c5c82014-11-24Stephen R. van den Berg  /*semi*/final array(mapping(string:mixed)) fetch_fields() {
5318352014-11-22Stephen R. van den Berg  if(!datarowtypes)
615d422014-10-31Stephen R. van den Berg  waitfordescribe();
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror();
8059252014-11-25Stephen R. van den Berg  return datarowdesc+emptyarray;
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
e7c5c82014-11-24Stephen 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; bytesreceived+=msglen;
c7de862014-11-20Stephen R. van den Berg  int cols=cr->read_int16();
aed7142014-11-18Stephen R. van den Berg  array a=allocate(cols,!alltext&&Val.null); #ifdef PG_DEBUG msglen-=2+4*cols; #endif
5318352014-11-22Stephen R. van den Berg  foreach(datarowtypes;int i;int typ) {
c7de862014-11-20Stephen R. van den Berg  int collen=cr->read_sint(4);
aed7142014-11-18Stephen R. van den Berg  if(collen>0) { #ifdef PG_DEBUG msglen-=collen; #endif mixed value;
5318352014-11-22Stephen R. van den Berg  switch(typ) {
aed7142014-11-18Stephen R. van den Berg  case FLOAT4OID: #if SIZEOF_FLOAT>=8 case FLOAT8OID: #endif if(!alltext) {
c7de862014-11-20Stephen R. van den Berg  value=(float)cr->read(collen);
aed7142014-11-18Stephen R. van den Berg  break; }
c7de862014-11-20Stephen R. van den Berg  default:value=cr->read(collen);
aed7142014-11-18Stephen R. van den Berg  break; case CHAROID:
c7de862014-11-20Stephen R. van den Berg  value=alltext?cr->read(1):cr->read_int8();
aed7142014-11-18Stephen R. van den Berg  break;
c7de862014-11-20Stephen R. van den Berg  case BOOLOID:value=cr->read_int8();
aed7142014-11-18Stephen R. van den Berg  switch(value) { case 'f':value=0; break; case 't':value=1; } if(alltext) value=value?"t":"f"; break; case TEXTOID: case BPCHAROID: case VARCHAROID:
c7de862014-11-20Stephen R. van den Berg  value=cr->read(collen);
aed7142014-11-18Stephen R. van den Berg  if(cenc==UTF8CHARSET && catch(value=utf8_to_string(value)) && !serror) serror=SERROR("%O contains non-%s characters\n", value,UTF8CHARSET); break; case INT8OID:case INT2OID: case OIDOID:case INT4OID: if(_forcetext) {
c7de862014-11-20Stephen R. van den Berg  value=cr->read(collen);
aed7142014-11-18Stephen R. van den Berg  if(!alltext) value=(int)value; } else { switch(typ) {
c7de862014-11-20Stephen R. van den Berg  case INT8OID:value=cr->read_sint(8);
aed7142014-11-18Stephen R. van den Berg  break;
c7de862014-11-20Stephen R. van den Berg  case INT2OID:value=cr->read_sint(2);
aed7142014-11-18Stephen R. van den Berg  break; case OIDOID:
c7de862014-11-20Stephen R. van den Berg  case INT4OID:value=cr->read_sint(4);
aed7142014-11-18Stephen R. van den Berg  } if(alltext) value=(string)value; } } a[i]=value; } else if(!collen) a[i]=""; } _processdataready(a); if(serror) 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) {
615d422014-10-31Stephen R. van den Berg  Thread.MutexKey lock=_ddescribemux->lock();
8059252014-11-25Stephen R. van den Berg  datarowdesc=drowdesc; datarowtypes=drowtypes;
615d422014-10-31Stephen R. van den Berg  _ddescribe->broadcast(); lock=0; }
075c542014-11-03Stephen R. van den Berg  final void _preparebind(array dtoid) {
615d422014-10-31Stephen R. van den Berg  array(string|int) paramValues=_params?_params[2]:({}); if(sizeof(dtoid)!=sizeof(paramValues)) SUSERERROR("Invalid number of bindings, expected %d, got %d\n", sizeof(dtoid),sizeof(paramValues));
5318352014-11-22Stephen R. van den Berg  Thread.MutexKey lock=_ddescribemux->lock();
d57b1a2014-11-18Stephen R. van den Berg  if(!_portalname) { _portalname=(_unnamedportalkey=pgsqlsess._unnamedportalmux->trylock(1)) ? "" : PORTALPREFIX #ifdef PG_DEBUG +(string)(c->socket->query_fd())+"_" #endif +int2hex(pgsqlsess._pportalcount++); lock=0;
615d422014-10-31Stephen R. van den Berg #ifdef PG_DEBUGMORE
d57b1a2014-11-18Stephen R. van den Berg  PD("ParamValues to bind: %O\n",paramValues);
615d422014-10-31Stephen R. van den Berg #endif
d57b1a2014-11-18Stephen R. van den Berg  Stdio.Buffer plugbuffer=Stdio.Buffer();
c9c7fc2014-11-26Stephen R. van den Berg  { array dta=({sizeof(dtoid)}); plugbuffer->add(_portalname,0,_preparedname,0) ->add_ints(dta+map(dtoid,oidformat)+dta,2); }
d57b1a2014-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) plugbuffer->add_int32(-1); // NULL else if(stringp(value) && !sizeof(value)) { int k=0; switch(dtoid[i]) { default: k=-1; // cast empty strings to NULL for non-string types case BYTEAOID: case TEXTOID: case XMLOID: case BPCHAROID: case VARCHAROID:; } plugbuffer->add_int32(k); } else switch(dtoid[i]) { case TEXTOID: case BPCHAROID: case VARCHAROID: { if(!value) { plugbuffer->add_int32(-1); break; } value=(string)value; switch(cenc) { case UTF8CHARSET: value=string_to_utf8(value); break; default: if(String.width(value)>8) { SUSERERROR("Don't know how to convert %O to %s encoding\n", value,cenc); value=""; } } plugbuffer->add_hstring(value,4);
615d422014-10-31Stephen R. van den Berg  break; }
d57b1a2014-11-18Stephen R. van den Berg  default: { if(!value) { plugbuffer->add_int32(-1);
615d422014-10-31Stephen R. van den Berg  break;
d57b1a2014-11-18Stephen R. van den Berg  }
7c9d772017-07-10Stephen R. van den Berg  if (!objectp(value) || typeof(value) != stdiobuftype) /* * Like Oracle and SQLite, we accept literal binary values * from single-valued multisets. */ if (multisetp(value) && sizeof(value) == 1) value = indices(value)[0]; else { value = (string)value; if (String.width(value) > 8) if (dtoid[i] == BYTEAOID) /* * FIXME We should throw an error here, it would * have been correct, but for historical reasons and * as a DWIM convenience we autoconvert to UTF8 here. */ value = string_to_utf8(value); else { SUSERERROR( "Wide string %O not supported for type OID %d\n", value,dtoid[i]); value=""; } }
d57b1a2014-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; 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 tval=value; switch(tval) { case 'o':case 'O': catch { tval=value[1]; value=tval=='n'||tval=='N'; };
615d422014-10-31Stephen R. van den Berg  break;
d57b1a2014-11-18Stephen R. van den Berg  default: value=1; break; case 0:case '0':case 'f':case 'F':case 'n':case 'N': value=0; break; } } while(0);
42ab4c2014-11-25Stephen R. van den Berg  plugbuffer->add_int32(1)->add_int8(value);
d57b1a2014-11-18Stephen R. van den Berg  break; case CHAROID: if(intp(value)) plugbuffer->add_hstring(value,4); else { value=(string)value; switch(sizeof(value)) { default: SUSERERROR( "\"char\" types must be 1 byte wide, got %O\n",value); case 0: plugbuffer->add_int32(-1); // NULL break; case 1: plugbuffer->add_hstring(value[0],4); }
615d422014-10-31Stephen R. van den Berg  }
d57b1a2014-11-18Stephen R. van den Berg  break; case INT8OID: plugbuffer->add_int32(8)->add_int((int)value,8); break; case OIDOID: case INT4OID: plugbuffer->add_int32(4)->add_int32((int)value); break; case INT2OID: plugbuffer->add_int32(2)->add_int16((int)value); break; } }
5318352014-11-22Stephen R. van den Berg  if(!datarowtypes) { if(_tprepared && dontcacheprefix->match(_query))
d57b1a2014-11-18Stephen R. van den Berg  m_delete(pgsqlsess->_prepareds,_query),_tprepared=0;
5318352014-11-22Stephen R. van den Berg  waitfordescribe();
d57b1a2014-11-18Stephen R. van den Berg  }
5318352014-11-22Stephen R. van den Berg  if(_state>=CLOSING) lock=_unnamedstatementkey=0; else { plugbuffer->add_int16(sizeof(datarowtypes)); if(sizeof(datarowtypes))
8059252014-11-25Stephen R. van den Berg  plugbuffer->add_ints(map(datarowtypes,oidformat),2);
6623742017-06-17Stephen R. van den Berg  else if (syncparse < 0 && !pgsqlsess->_wasparallelisable
67879d2017-11-10Stephen R. van den Berg  && !pgsqlsess->_statementsinflight->drained(1)) { lock = pgsqlsess->_shortmux->lock(); PD("Commit waiting for statements to finish\n"); catch(PT(pgsqlsess->_statementsinflight->wait_till_drained(lock, 1)));
5318352014-11-22Stephen R. van den Berg  } lock=0; 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();
5318352014-11-22Stephen R. van den Berg  _unnamedstatementkey=0;
c02c1d2017-06-03Stephen R. van den Berg  CHAIN(bindbuffer)->add_int8('B')->add_hstring(plugbuffer, 4, 4);
5318352014-11-22Stephen 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
5318352014-11-22Stephen R. van den Berg  || sizeof(_query)>=MINPREPARELENGTH && execfetchlimit->match(_query)) && _fetchlimit,bindbuffer); } } else lock=0;
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) { _setrowdesc(datarowdesc,datarowtypes); if(_tprepared) {
05b1f52014-11-17Stephen R. van den Berg  _tprepared.datarowdesc=datarowdesc;
8059252014-11-25Stephen R. van den Berg  _tprepared.datarowtypes=datarowtypes; }
615d422014-10-31Stephen R. van den Berg  }
6623742017-06-17Stephen R. van den Berg  final void _parseportal() {
2e28a92017-06-18Stephen R. van den Berg  Thread.MutexKey lock = closemux->lock(); _state=PARSING; Thread.MutexKey lockc = pgsqlsess->_shortmux->lock();
6623742017-06-17Stephen R. van den Berg  if (syncparse || syncparse < 0 && pgsqlsess->_wasparallelisable) {
67879d2017-11-10Stephen R. van den Berg  PD("Commit waiting for statements to finish\n"); catch(PT(pgsqlsess->_statementsinflight->wait_till_drained(lockc)));
6623742017-06-17Stephen R. van den Berg  }
67879d2017-11-10Stephen R. van den Berg  stmtifkey = pgsqlsess->_statementsinflight->acquire();
2e28a92017-06-18Stephen R. van den Berg  lockc = 0;
6623742017-06-17Stephen R. van den Berg  lock=0; statuscmdcomplete=UNDEFINED; pgsqlsess->_wasparallelisable = paralleliseprefix->match(_query); }
2e28a92017-06-18Stephen R. van den Berg  final void _releasestatement(void|int nolock) { Thread.MutexKey lock; if (!nolock) lock = closemux->lock(); if (_state <= BOUND) { _state = COMMITTED;
67879d2017-11-10Stephen R. van den Berg  stmtifkey = 0;
2e28a92017-06-18Stephen R. van den Berg  } lock = 0; }
6623742017-06-17Stephen R. van den Berg  final void _bindportal() {
2e28a92017-06-18Stephen R. van den Berg  Thread.MutexKey lock = closemux->lock();
2eef0b2014-11-15Stephen R. van den Berg  _state=BOUND;
67879d2017-11-10Stephen R. van den Berg  portalsifkey = pgsqlsess->_portalsinflight->acquire();
16ce8b2014-09-12Stephen R. van den Berg  lock=0; }
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
615d422014-10-31Stephen R. van den Berg  Thread.MutexKey lock=closemux->lock(); _fetchlimit=0; // disables further Executes switch(_state) {
2eef0b2014-11-15Stephen R. van den Berg  case COPYINPROGRESS:
2e28a92017-06-18Stephen R. van den Berg  case COMMITTED:
2eef0b2014-11-15Stephen R. van den Berg  case BOUND:
67879d2017-11-10Stephen R. van den Berg  portalsifkey = 0;
615d422014-10-31Stephen R. van den Berg  }
2e28a92017-06-18Stephen R. van den Berg  switch(_state) { case BOUND: case PARSING:
67879d2017-11-10Stephen R. van den Berg  stmtifkey = 0;
2e28a92017-06-18Stephen R. van den Berg  }
2eef0b2014-11-15Stephen R. van den Berg  _state=CLOSED;
615d422014-10-31Stephen R. van den Berg  lock=0;
04f1302014-11-14Stephen R. van den Berg  releaseconditions();
615d422014-10-31Stephen R. van den Berg  }
c02c1d2017-06-03Stephen R. van den Berg  final int _closeportal(conxsess cs) { object plugbuffer = CHAIN(cs);
2eef0b2014-11-15Stephen R. van den Berg  int retval=KEEP;
16ce8b2014-09-12Stephen R. van den Berg  PD("%O Try Closeportal %d\n",_portalname,_state); Thread.MutexKey lock=closemux->lock(); _fetchlimit=0; // disables further Executes
2e28a92017-06-18Stephen R. van den Berg  switch(_state) { case PARSING: case BOUND: _releasestatement(1); }
16ce8b2014-09-12Stephen R. van den Berg  switch(_state) {
2eef0b2014-11-15Stephen R. van den Berg  case PORTALINIT:
2e28a92017-06-18Stephen R. van den Berg  case PARSING:
04f1302014-11-14Stephen R. van den Berg  _unnamedstatementkey=0;
5bbace2014-11-18Stephen R. van den Berg  _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:
5bbace2014-11-18Stephen R. van den Berg  _state=CLOSING;
16ce8b2014-09-12Stephen R. van den Berg  lock=0; PD("Close portal %O\n",_portalname);
c02a6a2017-06-21Stephen R. van den Berg  if (_portalname && sizeof(_portalname)) {
615d422014-10-31Stephen R. van den Berg  plugbuffer->add_int8('C')->add_hstring(({'P',_portalname,0}),4,4);
2eef0b2014-11-15Stephen R. van den Berg  retval=FLUSHSEND;
16ce8b2014-09-12Stephen R. van den Berg  } else _unnamedportalkey=0;
67879d2017-11-10Stephen R. van den Berg  portalsifkey = 0; if (pgsqlsess->_portalsinflight->drained()) { 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. * It's a bit of a tricky race, but this check should be sufficient. */
2eef0b2014-11-15Stephen R. van den Berg  pgsqlsess->_readyforquerycount++, retval=SYNCSEND;
3c93de2014-11-03Stephen R. van den Berg  pgsqlsess->_pportalcount=0;
16ce8b2014-09-12Stephen R. van den Berg  } } lock=0; return retval; }
aed7142014-11-18Stephen R. van den Berg  final void _processdataready(array datarow,void|int msglen) { bytesreceived+=msglen;
05b1f52014-11-17Stephen R. van den Berg  inflight--;
5bbace2014-11-18Stephen R. van den Berg  if(_state<CLOSED) datarows->write(datarow);
aed7142014-11-18Stephen R. van den Berg  if(++rowsreceived==1)
05b1f52014-11-17Stephen R. van den Berg  PD("<%O _fetchlimit %d=min(%d||1,%d), inflight %d\n",_portalname,
aed7142014-11-18Stephen R. van den Berg  _fetchlimit,(portalbuffersize>>1)*rowsreceived/bytesreceived,
05b1f52014-11-17Stephen R. van den Berg  pgsqlsess._fetchlimit,inflight);
16ce8b2014-09-12Stephen R. van den Berg  if(_fetchlimit) { _fetchlimit=
aed7142014-11-18Stephen R. van den Berg  min((portalbuffersize>>1)*rowsreceived/bytesreceived||1,
3c93de2014-11-03Stephen R. van den Berg  pgsqlsess._fetchlimit);
16ce8b2014-09-12Stephen R. van den Berg  Thread.MutexKey lock=closemux->lock();
7d5f252014-11-22Stephen R. van den Berg  if(_fetchlimit && inflight<=(_fetchlimit-1)>>1)
16ce8b2014-09-12Stephen R. van den Berg  _sendexecute(_fetchlimit); else if(!_fetchlimit)
05b1f52014-11-17Stephen R. van den Berg  PD("<%O _fetchlimit %d, inflight %d, skip execute\n", _portalname,_fetchlimit,inflight);
16ce8b2014-09-12Stephen R. van den Berg  lock=0;
ecbab12008-07-27Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  }
04f1302014-11-14Stephen R. van den Berg  private void releaseconditions() {
60549b2015-01-05Stephen R. van den Berg  _unnamedportalkey=_unnamedstatementkey=0;
04f1302014-11-14Stephen R. van den Berg  pgsqlsess=0;
5318352014-11-22Stephen R. van den Berg  if(!datarowtypes) { Thread.MutexKey lock=_ddescribemux->lock();
8059252014-11-25Stephen R. van den Berg  datarowtypes=emptyarray; datarowdesc=emptyarray;
56cd392014-11-17Stephen R. van den Berg  _ddescribe->broadcast();
5318352014-11-22Stephen R. van den Berg  lock=0;
f0522e2014-11-13Stephen R. van den Berg  }
ecbab12008-07-27Stephen R. van den Berg  }
05b1f52014-11-17Stephen R. van den Berg  final void _releasesession(void|string statusccomplete) {
c071bc2017-11-05Henrik Grubbström (Grubba)  c->closecallbacks-=(<_destruct>);
05b1f52014-11-17Stephen R. van den Berg  if(statusccomplete && !statuscmdcomplete) statuscmdcomplete=statusccomplete; inflight=0;
c02c1d2017-06-03Stephen R. van den Berg  conxsess plugbuffer; if (!catch(plugbuffer = c->start()))
3b5d772015-12-23Stephen R. van den Berg  plugbuffer->sendcmd(_closeportal(plugbuffer));
5bbace2014-11-18Stephen R. van den Berg  _state=CLOSED;
0889172017-11-09Stephen R. van den Berg  datarows->write(1); // Signal EOF
04f1302014-11-14Stephen R. van den Berg  releaseconditions(); }
c071bc2017-11-05Henrik Grubbström (Grubba)  protected void _destruct() {
16ce8b2014-09-12Stephen R. van den Berg  catch { // inside destructors, exceptions don't work _releasesession();
11b13b2014-08-16Martin Nilsson  }; }
ecbab12008-07-27Stephen R. van den Berg 
c02c1d2017-06-03Stephen R. van den Berg  final void _sendexecute(int fetchlimit,void|bufcon|conxsess plugbuffer) {
16ce8b2014-09-12Stephen R. van den Berg  int flushmode;
b1fe662017-06-27Stephen R. van den Berg  PD("Execute portal %O fetchlimit %d transtype %d\n", _portalname, fetchlimit, transtype);
16ce8b2014-09-12Stephen R. van den Berg  if(!plugbuffer) plugbuffer=c->start(1);
c02c1d2017-06-03Stephen 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) pgsqlsess._intransaction = transtype == TRANSBEGIN; flushmode = _closeportal(plugbuffer) == SYNCSEND || transtype == TRANSEND ? SYNCSEND : FLUSHSEND; } else
05b1f52014-11-17Stephen R. van den Berg  inflight+=fetchlimit, flushmode=FLUSHSEND;
16ce8b2014-09-12Stephen R. van den Berg  plugbuffer->sendcmd(flushmode,this);
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
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;
05b1f52014-11-17Stephen R. van den Berg  if(arrayp(datarow=datarows->try_read()))
16ce8b2014-09-12Stephen R. van den Berg  return datarow;
615d422014-10-31Stephen R. van den Berg  if(!eoffound) {
5cb4f22014-11-28Stephen R. van den Berg  if(!datarow) { PD("%O Block for datarow\n",_portalname); array cid=callout(gottimeout,timeout);
d4ef202014-12-01Stephen R. van den Berg  PT(datarow=datarows->read());
5cb4f22014-11-28Stephen R. van den Berg  local_backend->remove_call_out(cid); if(arrayp(datarow)) return datarow; }
615d422014-10-31Stephen 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() {
16ce8b2014-09-12Stephen R. van den Berg  if(eoffound) return 0;
05b1f52014-11-17Stephen R. van den Berg  array(array|int) datarow=datarows->try_read_array();
5cb4f22014-11-28Stephen R. van den Berg  if(!datarow) { array cid=callout(gottimeout,timeout);
d4ef202014-12-01Stephen R. van den Berg  PT(datarow=datarows->read_array());
5cb4f22014-11-28Stephen R. van den Berg  local_backend->remove_call_out(cid); }
16ce8b2014-09-12Stephen R. van den Berg  if(arrayp(datarow[-1])) return datarow; trydelayederror(); eoffound=1;
05b1f52014-11-17Stephen R. van den Berg  datarows->write(1); // Signal EOF for other threads
16ce8b2014-09-12Stephen R. van den Berg  return (datarow=datarow[..<1]); }
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(); if(copydata) { PD("CopyData\n");
c02c1d2017-06-03Stephen R. van den Berg  object cs = c->start(); 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(
39e1c42014-11-14Stephen R. van den Berg  function(sql_result, array(mixed), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  array(mixed) args) { int|array datarow;
5cb4f22014-11-28Stephen R. van den Berg  for(;;) { array cid=callout(gottimeout,timeout);
d4ef202014-12-01Stephen R. van den Berg  PT(datarow=datarows->read());
5cb4f22014-11-28Stephen R. van den Berg  local_backend->remove_call_out(cid); if(!arrayp(datarow)) break;
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow, @args);
5cb4f22014-11-28Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror(); 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(
39e1c42014-11-14Stephen R. van den Berg  function(sql_result, array(mixed), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  mixed ... args) { if(callback) Thread.Thread(run_result_cb,callback,args); }
8384822014-11-12Stephen R. van den Berg  private void run_result_array_cb(
39e1c42014-11-14Stephen R. van den Berg  function(sql_result, array(array(mixed)), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  array(mixed) args) { array(array|int) datarow;
5cb4f22014-11-28Stephen R. van den Berg  for(;;) { array cid=callout(gottimeout,timeout);
d4ef202014-12-01Stephen R. van den Berg  PT(datarow=datarows->read_array());
5cb4f22014-11-28Stephen R. van den Berg  local_backend->remove_call_out(cid); if(!datarow || !arrayp(datarow[-1])) break;
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow, @args);
5cb4f22014-11-28Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  trydelayederror(); eoffound=1; if(sizeof(datarow)>1)
2c98002014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow=datarow[..<1], @args); 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(
39e1c42014-11-14Stephen R. van den Berg  function(sql_result, array(array(mixed)), mixed ...:void) callback,
16ce8b2014-09-12Stephen R. van den Berg  mixed ... args) { if(callback) Thread.Thread(run_result_array_cb,callback,args);
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
e7c5c82014-11-24Stephen R. van den Berg };