ecbab12008-07-27Stephen R. van den Berg /* * Some pgsql utility functions. * They are kept here to avoid circular references. */
933a762014-11-10Stephen R. van den Berg //! The pgsql backend, shared between all connection instances.
3b31fa2014-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.
3a881c2014-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.
933a762014-11-10Stephen R. van den Berg 
ecbab12008-07-27Stephen R. van den Berg #pike __REAL_VERSION__
91e2382014-11-10Stephen R. van den Berg #require constant(Thread.Thread)
ecbab12008-07-27Stephen R. van den Berg  #include "pgsql.h"
3a881c2014-11-10Stephen R. van den Berg //! The instance of the pgsql dedicated backend.
fb8eb42014-10-31Stephen R. van den Berg final Pike.Backend local_backend = Pike.SmallBackend();
3a881c2014-11-10Stephen R. van den Berg 
6eb5942014-11-12Stephen R. van den Berg private Thread.Mutex backendmux = Thread.Mutex(); private int clientsregistered;
cff3a32014-09-12Stephen R. van den Berg 
d743bf2014-11-13Stephen R. van den Berg final multiset cachealways=(<"BEGIN","begin","END","end","COMMIT","commit">); final Regexp createprefix
fb8eb42014-10-31Stephen R. van den Berg  =Regexp("^[ \t\f\r\n]*[Cc][Rr][Ee][Aa][Tt][Ee][ \t\f\r\n]");
6eb5942014-11-12Stephen R. van den Berg private Regexp dontcacheprefix
fb8eb42014-10-31Stephen R. van den Berg  =Regexp("^[ \t\f\r\n]*([Ff][Ee][Tt][Cc][Hh]|[Cc][Oo][Pp][Yy])[ \t\f\r\n]");
d743bf2014-11-13Stephen R. van den Berg private Regexp commitprefix=Regexp( "^[ \t\f\r\n]*([Cc][Oo][Mm][Mm][Ii][Tt]|[Ee][Nn][Dd])([ \t\f\r\n;]|$)");
6eb5942014-11-12Stephen R. van den Berg private Regexp execfetchlimit
fb8eb42014-10-31Stephen R. van den Berg  =Regexp("^[ \t\f\r\n]*(([Uu][Pp][Dd][Aa]|[Dd][Ee][Ll][Ee])[Tt][Ee]|\ [Ii][Nn][Ss][Ee][Rr][Tt])[ \t\f\r\n]|\ [ \t\f\r\n][Ll][Ii][Mm][Ii][Tt][ \t\f\r\n]+[12][; \t\f\r\n]*$");
3b31fa2014-11-14Stephen R. van den Berg final void closestatement(bufcon|conxion plugbuffer,string oldprep) {
fb8eb42014-10-31Stephen R. van den Berg  if(oldprep) { PD("Close statement %s\n",oldprep); plugbuffer->add_int8('C')->add_hstring(({'S',oldprep,0}),4,4); } }
6eb5942014-11-12Stephen R. van den Berg private void run_local_backend() {
cff3a32014-09-12Stephen R. van den Berg  Thread.MutexKey lock; int looponce; do { looponce=0; if(lock=backendmux->trylock()) { PD("Starting local backend\n"); while(clientsregistered) // Autoterminate when not needed local_backend(4096.0); PD("Terminating local backend\n"); lock=0; looponce=clientsregistered; } } while(looponce); }
ecbab12008-07-27Stephen R. van den Berg 
3a881c2014-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.
cff3a32014-09-12Stephen R. van den Berg final void register_backend() { if(!clientsregistered++) Thread.Thread(run_local_backend); }
ecbab12008-07-27Stephen R. van den Berg 
3a881c2014-11-10Stephen R. van den Berg //! Unregisters yourself as a user of this backend. If there are //! no longer any registered users, the backend will be terminated.
cff3a32014-09-12Stephen R. van den Berg final void unregister_backend() { --clientsregistered; }
4741cd2008-07-31Stephen R. van den Berg 
cff3a32014-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  }
cff3a32014-09-12Stephen R. van den Berg }
ecbab12008-07-27Stephen R. van den Berg 
fb8eb42014-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 }
0e7f332014-11-15Stephen R. van den Berg private int mergemode(conxion realbuffer,int mode) {
4ef8a32014-10-29Stephen R. van den Berg  if(mode>realbuffer->stashflushmode) realbuffer->stashflushmode=mode; return realbuffer->stashflushmode;
cff3a32014-09-12Stephen R. van den Berg }
ecbab12008-07-27Stephen R. van den Berg 
6eb5942014-11-12Stephen R. van den Berg private inline mixed callout(function(mixed ...:void) f,
0fed8c2014-11-10Stephen R. van den Berg  float|int delay,mixed ... args) { return local_backend->call_out(f,delay,@args); }
933a762014-11-10Stephen R. van den Berg // Some pgsql utility functions
ecbab12008-07-27Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg class bufcon {
cff3a32014-09-12Stephen R. van den Berg  inherit Stdio.Buffer;
ecbab12008-07-27Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg  private conxion realbuffer;
ecbab12008-07-27Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg  protected void create(conxion _realbuffer) {
cff3a32014-09-12Stephen R. van den Berg  realbuffer=_realbuffer;
ecbab12008-07-27Stephen R. van den Berg  }
d432822008-07-31Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg  final bufcon start(void|int waitforreal) {
cff3a32014-09-12Stephen R. van den Berg  realbuffer->stashcount++; #ifdef PG_DEBUG if(waitforreal)
3b31fa2014-11-14Stephen R. van den Berg  error("pgsql.bufcon not allowed here\n");
d432822008-07-31Stephen R. van den Berg #endif
cff3a32014-09-12Stephen R. van den Berg  return this; }
0e7f332014-11-15Stephen R. van den Berg  final void sendcmd(int mode,void|sql_result portal) {
cff3a32014-09-12Stephen R. van den Berg  Thread.MutexKey lock=realbuffer->stashupdate->lock(); if(portal) realbuffer->stashqueue->write(portal); realbuffer->stash->add(this); mode=mergemode(realbuffer,mode); if(!--realbuffer->stashcount) realbuffer->stashavail.signal(); lock=0; this->clear(); if(lock=realbuffer->nostash->trylock(1)) { realbuffer->started=lock; lock=0;
0e7f332014-11-15Stephen R. van den Berg  realbuffer->sendcmd(SENDOUT);
cff3a32014-09-12Stephen R. van den Berg  }
d432822008-07-31Stephen R. van den Berg  }
ecbab12008-07-27Stephen R. van den Berg 
cff3a32014-09-12Stephen R. van den Berg }
ecbab12008-07-27Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg class conxion {
cff3a32014-09-12Stephen R. van den Berg  inherit Stdio.Buffer:i; inherit Stdio.Buffer:o;
6eb5942014-11-12Stephen R. van den Berg  private Thread.Condition fillread; private Thread.Mutex fillreadmux; private Thread.Queue qportals;
cff3a32014-09-12Stephen R. van den Berg  final Stdio.File socket;
6eb5942014-11-12Stephen R. van den Berg  private object pgsqlsess; private int towrite;
bab0eb2014-11-17Stephen R. van den Berg  private Thread.Mutex towritemux;
cff3a32014-09-12Stephen R. van den Berg  final function(:void) gottimeout; final int timeout;
a362e32014-11-03Stephen R. van den Berg  final Thread.Mutex nostash; final Thread.MutexKey started; final Thread.Mutex stashupdate; final Thread.Queue stashqueue; final Thread.Condition stashavail; final Stdio.Buffer stash; final int stashflushmode; final int stashcount; final int synctransact;
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
a362e32014-11-03Stephen R. van den Berg  final int queueoutidx;
cbe52f2014-11-13Stephen R. van den Berg  final int queueinidx=-1;
ecbab12008-07-27Stephen R. van den Berg #endif
3b31fa2014-11-14Stephen R. van den Berg  private inline void queueup(sql_result portal) {
4ef8a32014-10-29Stephen R. van den Berg  qportals->write(portal); portal->_synctransact=synctransact;
bf75ce2014-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));
4ef8a32014-10-29Stephen R. van den Berg  }
3b31fa2014-11-14Stephen R. van den Berg  final conxion|bufcon start(void|int waitforreal) {
cff3a32014-09-12Stephen R. van den Berg  Thread.MutexKey lock; if(lock=(waitforreal?nostash->lock:nostash->trylock)(1)) { started=lock; lock=stashupdate->lock();
4ef8a32014-10-29Stephen R. van den Berg  if(stashcount)
cff3a32014-09-12Stephen R. van den Berg  stashavail.wait(lock);
4ef8a32014-10-29Stephen R. van den Berg  add(stash); stash->clear();
3b31fa2014-11-14Stephen R. van den Berg  foreach(stashqueue->try_read_array();;sql_result portal)
4ef8a32014-10-29Stephen R. van den Berg  queueup(portal);
cff3a32014-09-12Stephen R. van den Berg  lock=0; return this; } stashcount++;
3b31fa2014-11-14Stephen R. van den Berg  return bufcon(this);
ecbab12008-07-27Stephen R. van den Berg  }
2075c32014-11-10Stephen R. van den Berg  protected bool range_error(int howmuch) {
cff3a32014-09-12Stephen R. van den Berg  if(!howmuch) return false; #ifdef PG_DEBUG if(howmuch<0) error("Out of range %d\n",howmuch); #endif
fb8eb42014-10-31Stephen R. van den Berg  if(fillread) {
0fed8c2014-11-10Stephen R. van den Berg  array cid=callout(gottimeout,timeout);
fb8eb42014-10-31Stephen R. van den Berg  Thread.MutexKey lock=fillreadmux->lock(); fillread.wait(lock);
cff3a32014-09-12Stephen R. van den Berg  lock=0; local_backend->remove_call_out(cid); } else throw(MAGICTERMINATE); return true; }
6eb5942014-11-12Stephen R. van den Berg  private int read_cb(mixed id,mixed b) {
fb8eb42014-10-31Stephen R. van den Berg  Thread.MutexKey lock=fillreadmux->lock(); if(fillread) fillread.signal();
cff3a32014-09-12Stephen R. van den Berg  lock=0; return 0; }
6eb5942014-11-12Stephen R. van den Berg  private int write_cb() {
bab0eb2014-11-17Stephen R. van den Berg  Thread.MutexKey lock=towritemux->lock();
cff3a32014-09-12Stephen R. van den Berg  towrite-=output_to(socket,towrite);
bab0eb2014-11-17Stephen R. van den Berg  lock=0;
bf75ce2014-11-15Stephen R. van den Berg  if(!fillread && !sizeof(this)) { PD("%d>Close socket delayed\n",socket->query_fd());
cff3a32014-09-12Stephen R. van den Berg  socket->close();
bf75ce2014-11-15Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  return 0; }
6eb5942014-11-12Stephen R. van den Berg  final inline int consume(int w) { return i::consume(w); } final inline int unread(int w) { return i::unread(w); } final inline string read(int w) { return i::read(w); } final inline object read_buffer(int w) { return i::read_buffer(w); } final inline int read_sint(int w) { return i::read_sint(w); } final inline int read_int8() { return i::read_int8(); } final inline int read_int16() { return i::read_int16(); } final inline int read_int32() { return i::read_int32(); } final inline string read_cstring() { return i::read_cstring(); }
cff3a32014-09-12Stephen R. van den Berg 
0e7f332014-11-15Stephen R. van den Berg  final void sendcmd(void|int mode,void|sql_result portal) {
4ef8a32014-10-29Stephen R. van den Berg  if(portal) queueup(portal);
6a5bed2014-11-13Stephen R. van den Berg nosync: do { switch(mode) { default: break nosync;
0e7f332014-11-15Stephen R. van den Berg  case SYNCSEND:
bf75ce2014-11-15Stephen R. van den Berg  PD("%d>Sync %d %d Queue\n", socket->query_fd(),synctransact,++queueoutidx);
6a5bed2014-11-13Stephen R. van den Berg  add(PGSYNC);
0e7f332014-11-15Stephen R. van den Berg  mode=SENDOUT;
6a5bed2014-11-13Stephen R. van den Berg  break;
0e7f332014-11-15Stephen R. van den Berg  case FLUSHLOGSEND:
bf75ce2014-11-15Stephen R. van den Berg  PD("%d>%O %d Queue simplequery %d bytes\n", socket->query_fd(),portal._portalname,++queueoutidx,sizeof(this));
0e7f332014-11-15Stephen R. van den Berg  mode=FLUSHSEND;
6a5bed2014-11-13Stephen R. van den Berg  } qportals->write(synctransact++); } while(0);
cff3a32014-09-12Stephen R. van den Berg  if(started) { Thread.MutexKey lock=stashupdate->lock(); if(sizeof(stash)) { add(stash); stash->clear();
3b31fa2014-11-14Stephen R. van den Berg  foreach(stashqueue->try_read_array();;sql_result portal)
4ef8a32014-10-29Stephen R. van den Berg  queueup(portal);
cff3a32014-09-12Stephen R. van den Berg  } mode=mergemode(this,mode);
0e7f332014-11-15Stephen R. van den Berg  stashflushmode=KEEP;
cff3a32014-09-12Stephen R. van den Berg  lock=0; }
a362e32014-11-03Stephen R. van den Berg  catch {
4ef8a32014-10-29Stephen R. van den Berg outer:
a362e32014-11-03Stephen R. van den Berg  do { switch(mode) { default: break outer;
0e7f332014-11-15Stephen R. van den Berg  case FLUSHSEND:
a362e32014-11-03Stephen R. van den Berg  PD("Flush\n"); add(PGFLUSH);
0e7f332014-11-15Stephen R. van den Berg  case SENDOUT:;
a362e32014-11-03Stephen R. van den Berg  }
bab0eb2014-11-17Stephen R. van den Berg  Thread.MutexKey lock=towritemux->lock();
a362e32014-11-03Stephen R. van den Berg  if(towrite=sizeof(this)) {
bf75ce2014-11-15Stephen R. van den Berg  PD("%d>Sendcmd %O\n",socket->query_fd(),((string)this)[..towrite-1]);
a362e32014-11-03Stephen R. van den Berg  towrite-=output_to(socket,towrite); }
bab0eb2014-11-17Stephen R. van den Berg  lock=0;
a362e32014-11-03Stephen R. van den Berg  } while(0); started=0; return; };
d8be282014-11-14Stephen R. van den Berg  if(pgsqlsess) pgsqlsess->_connectfail();
ecbab12008-07-27Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  final void sendterminate() {
fb8eb42014-10-31Stephen R. van den Berg  destruct(fillread); // Delayed close() after flushing the output buffer
ecbab12008-07-27Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  final int close() {
d8be282014-11-14Stephen R. van den Berg  destruct(nostash);
bf75ce2014-11-15Stephen R. van den Berg  PD("%d>Close socket\n",socket->query_fd());
cff3a32014-09-12Stephen R. van den Berg  return socket->close();
ecbab12008-07-27Stephen R. van den Berg  }
6eb5942014-11-12Stephen R. van den Berg  protected void destroy() {
cff3a32014-09-12Stephen R. van den Berg  catch(close()); // Exceptions don't work inside destructors
a362e32014-11-03Stephen R. van den Berg  pgsqlsess=0;
ecbab12008-07-27Stephen R. van den Berg  }
a362e32014-11-03Stephen R. van den Berg  final void connectloop(int nossl) {
cff3a32014-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)
cff3a32014-09-12Stephen R. van den Berg  if(!nossl && !pgsqlsess->nossl
d743bf2014-11-13Stephen R. van den Berg  && (pgsqlsess._options.use_ssl || pgsqlsess._options.force_ssl)) {
cff3a32014-09-12Stephen R. van den Berg  PD("SSLRequest\n"); start()->add_int32(8)->add_int32(PG_PROTOCOL(1234,5679))
0e7f332014-11-15Stephen R. van den Berg  ->sendcmd(SENDOUT);
cff3a32014-09-12Stephen R. van den Berg  switch(read_int8()) { case 'S': object fcon=SSL.File(socket,SSL.Context()); if(fcon->connect()) { socket=fcon; break; }
bf75ce2014-11-15Stephen R. van den Berg  default:PD("%d>Close socket\n",socket->query_fd()); socket->close();
cff3a32014-09-12Stephen R. van den Berg  pgsqlsess.nossl=1; continue; case 'N':
d743bf2014-11-13Stephen R. van den Berg  if(pgsqlsess._options.force_ssl)
cff3a32014-09-12Stephen R. van den Berg  error("Encryption not supported on connection to %s:%d\n", pgsqlsess.host,pgsqlsess.port); } } #else
d743bf2014-11-13Stephen R. van den Berg  if(pgsqlsess._options.force_ssl)
cff3a32014-09-12Stephen R. van den Berg  error("Encryption library missing," " cannot establish connection to %s:%d\n", pgsqlsess.host,pgsqlsess.port); #endif break; }
a362e32014-11-03Stephen R. van den Berg  if(!socket->is_open()) error(strerror(socket->errno()));
cff3a32014-09-12Stephen R. van den Berg  socket->set_backend(local_backend); socket->set_buffer_mode(i::this,0); socket->set_nonblocking(read_cb,write_cb,0); Thread.Thread(pgsqlsess->_processloop,this); return; };
d8be282014-11-14Stephen R. van den Berg  if(pgsqlsess) pgsqlsess->_connectfail(err);
0d1a5e2014-11-14Stephen R. van den Berg  } private string _sprintf(int type, void|mapping flags) { string res=UNDEFINED; switch(type) { case 'O':
3b31fa2014-11-14Stephen R. van den Berg  res=predef::sprintf("conxion fd: %d input queue: %d/%d "
0d1a5e2014-11-14Stephen R. van den Berg  "queued portals: %d output queue: %d/%d\n", socket&&socket->query_fd(), sizeof(i::this),i::_size_object(), qportals->size(),sizeof(this),_size_object()); break; } return res;
cff3a32014-09-12Stephen R. van den Berg  }
a362e32014-11-03Stephen R. van den Berg  protected void create(object _pgsqlsess,Thread.Queue _qportals,int nossl) {
cff3a32014-09-12Stephen R. van den Berg  i::create(); o::create(); qportals = _qportals;
4ef8a32014-10-29Stephen R. van den Berg  synctransact = 1;
bab0eb2014-11-17Stephen R. van den Berg  towritemux=Thread.Mutex();
fb8eb42014-10-31Stephen R. van den Berg  fillread=Thread.Condition(); fillreadmux=Thread.Mutex();
cff3a32014-09-12Stephen R. van den Berg  gottimeout=sendcmd; // Preset it with a NOP timeout=128; // Just a reasonable amount socket=Stdio.File(); nostash=Thread.Mutex(); stashupdate=Thread.Mutex(); stashqueue=Thread.Queue(); stashavail=Thread.Condition(); stash=Stdio.Buffer();
a362e32014-11-03Stephen R. van den Berg  pgsqlsess=_pgsqlsess; Thread.Thread(connectloop,nossl);
ecbab12008-07-27Stephen R. van den Berg  } }
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()]
3b31fa2014-11-14Stephen R. van den Berg class sql_result {
ecbab12008-07-27Stephen R. van den Berg 
6eb5942014-11-12Stephen R. van den Berg  private object pgsqlsess; private int eoffound;
3b31fa2014-11-14Stephen R. van den Berg  private conxion c;
a362e32014-11-03Stephen R. van den Berg  final mixed _delayederror;
0e7f332014-11-15Stephen R. van den Berg  final int _state;
a362e32014-11-03Stephen R. van den Berg  final int _fetchlimit;
15d4e12014-11-18Stephen R. van den Berg  private int alltext;
a362e32014-11-03Stephen R. van den Berg  final int _forcetext; final string _portalname;
bf20922014-11-17Stephen R. van den Berg  private int rowsreceived; private int inflight; private int portalbuffersize; private Stdio.Buffer prepbuffer; private Thread.Condition prepbufferready; private Thread.Mutex prepbuffermux; private Thread.Mutex closemux; private Thread.Queue datarows;
15d4e12014-11-18Stephen R. van den Berg  private array(mapping(string:mixed)) datarowdesc;
bf20922014-11-17Stephen R. van den Berg  private string statuscmdcomplete;
15d4e12014-11-18Stephen R. van den Berg  private int bytesreceived;
a362e32014-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;
ecbab12008-07-27Stephen R. van den Berg 
6eb5942014-11-12Stephen R. van den Berg  private string _sprintf(int type, void|mapping flags) {
11b13b2014-08-16Martin Nilsson  string res=UNDEFINED;
cff3a32014-09-12Stephen R. van den Berg  switch(type) { case 'O':
bf20922014-11-17Stephen R. van den Berg  res=sprintf("sql_result state: %d numrows: %d eof: %d inflight: %d\n"
cff3a32014-09-12Stephen R. van den Berg  "query: %O\n" "portalname: %O datarows: %d" " laststatus: %s\n",
bf20922014-11-17Stephen R. van den Berg  _state,rowsreceived,eoffound,inflight,
cff3a32014-09-12Stephen R. van den Berg  _query,
15d4e12014-11-18Stephen R. van den Berg  _portalname,datarowdesc&&sizeof(datarowdesc),
bf20922014-11-17Stephen R. van den Berg  statuscmdcomplete||(_unnamedstatementkey?"*parsing*":""));
cff3a32014-09-12Stephen R. van den Berg  break;
11b13b2014-08-16Martin Nilsson  } return res;
ecbab12008-07-27Stephen R. van den Berg  }
3b31fa2014-11-14Stephen R. van den Berg  protected void create(object _pgsqlsess,conxion _c,string query,
bf20922014-11-17Stephen R. van den Berg  int _portalbuffersize,int alltyped,array params,int forcetext) {
a362e32014-11-03Stephen R. van den Berg  pgsqlsess = _pgsqlsess;
cff3a32014-09-12Stephen R. van den Berg  c = _c;
11b13b2014-08-16Martin Nilsson  _query = query;
bf20922014-11-17Stephen R. van den Berg  datarows = Thread.Queue();
fb8eb42014-10-31Stephen R. van den Berg  _ddescribe=Thread.Condition(); _ddescribemux=Thread.Mutex();
cff3a32014-09-12Stephen R. van den Berg  closemux=Thread.Mutex();
a362e32014-11-03Stephen R. van den Berg  prepbufferready=Thread.Condition(); prepbuffermux=Thread.Mutex();
bf20922014-11-17Stephen R. van den Berg  portalbuffersize=_portalbuffersize;
15d4e12014-11-18Stephen R. van den Berg  alltext = !alltyped;
11b13b2014-08-16Martin Nilsson  _params = params; _forcetext = forcetext;
0e7f332014-11-15Stephen R. van den Berg  _state = PORTALINIT;
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.
cff3a32014-09-12Stephen R. van den Berg  string status_command_complete() {
bf20922014-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.
cff3a32014-09-12Stephen R. van den Berg  int affected_rows() {
11b13b2014-08-16Martin Nilsson  int rows;
bf20922014-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 
15d4e12014-11-18Stephen R. van den Berg  final void _storetiming() { if(_tprepared) { _tprepared.trun=gethrtime()-_tprepared.trunstart; m_delete(_tprepared,"trunstart"); _tprepared = UNDEFINED; } }
6eb5942014-11-12Stephen R. van den Berg  private void waitfordescribe() {
fb8eb42014-10-31Stephen R. van den Berg  Thread.MutexKey lock=_ddescribemux->lock();
15d4e12014-11-18Stephen R. van den Berg  if(!datarowdesc)
fb8eb42014-10-31Stephen R. van den Berg  _ddescribe->wait(lock); lock=0; }
11b13b2014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->num_fields()]
cff3a32014-09-12Stephen R. van den Berg  int num_fields() {
15d4e12014-11-18Stephen R. van den Berg  if(!datarowdesc)
fb8eb42014-10-31Stephen R. van den Berg  waitfordescribe(); trydelayederror();
15d4e12014-11-18Stephen R. van den Berg  return sizeof(datarowdesc);
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
11b13b2014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->num_rows()]
cff3a32014-09-12Stephen R. van den Berg  int num_rows() {
fb8eb42014-10-31Stephen R. van den Berg  trydelayederror();
bf20922014-11-17Stephen R. van den Berg  return rowsreceived;
11b13b2014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
6eb5942014-11-12Stephen R. van den Berg  private inline void trydelayederror() {
cff3a32014-09-12Stephen R. van den Berg  if(_delayederror) throwdelayederror(this); }
11b13b2014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->eof()]
cff3a32014-09-12Stephen R. van den Berg  int eof() { trydelayederror();
11b13b2014-08-16Martin Nilsson  return eoffound; }
ecbab12008-07-27Stephen R. van den Berg 
11b13b2014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->fetch_fields()]
cff3a32014-09-12Stephen R. van den Berg  array(mapping(string:mixed)) fetch_fields() {
15d4e12014-11-18Stephen R. van den Berg  if(!datarowdesc)
fb8eb42014-10-31Stephen R. van den Berg  waitfordescribe();
cff3a32014-09-12Stephen R. van den Berg  trydelayederror();
15d4e12014-11-18Stephen R. van den Berg  return datarowdesc+({}); } #ifdef PG_DEBUG final int #else final void #endif _decodedata(int msglen,string cenc) { _storetiming(); string serror; bytesreceived+=msglen; int cols=c->read_int16(); array a=allocate(cols,!alltext&&Val.null); #ifdef PG_DEBUG msglen-=2+4*cols; #endif foreach(datarowdesc;int i;mapping m) { int collen=c->read_sint(4); if(collen>0) { #ifdef PG_DEBUG msglen-=collen; #endif mixed value; switch(int typ=m.type) { case FLOAT4OID: #if SIZEOF_FLOAT>=8 case FLOAT8OID: #endif if(!alltext) { value=(float)c->read(collen); break; } default:value=c->read(collen); break; case CHAROID: value=alltext?c->read(1):c->read_int8(); break; case BOOLOID:value=c->read_int8(); switch(value) { case 'f':value=0; break; case 't':value=1; } if(alltext) value=value?"t":"f"; break; case TEXTOID: case BPCHAROID: case VARCHAROID: value=c->read(collen); 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) { value=c->read(collen); if(!alltext) value=(int)value; } else { switch(typ) { case INT8OID:value=c->read_sint(8); break; case INT2OID:value=c->read_sint(2); break; case OIDOID: case INT4OID:value=c->read_sint(4); } 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 
15d4e12014-11-18Stephen R. van den Berg  final void _setrowdesc(array(mapping(string:mixed)) drowdesc) {
fb8eb42014-10-31Stephen R. van den Berg  Thread.MutexKey lock=_ddescribemux->lock();
15d4e12014-11-18Stephen R. van den Berg  datarowdesc=drowdesc;
fb8eb42014-10-31Stephen R. van den Berg  _ddescribe->broadcast(); lock=0; }
42fc8c2014-11-03Stephen R. van den Berg  final void _preparebind(array dtoid) {
fb8eb42014-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)); #ifdef PG_DEBUGMORE PD("ParamValues to bind: %O\n",paramValues); #endif
ab24362014-11-10Stephen R. van den Berg  Stdio.Buffer plugbuffer=Stdio.Buffer();
fb8eb42014-10-31Stephen R. van den Berg  plugbuffer->add(_portalname=
a362e32014-11-03Stephen R. van den Berg  (_unnamedportalkey=pgsqlsess._unnamedportalmux->trylock(1)) ? "" : PORTALPREFIX+int2hex(pgsqlsess._pportalcount++) )->add_int8(0)
7489d72014-11-18Stephen R. van den Berg  ->add(_preparedname)->add_int8(0)->add_int16(sizeof(dtoid));
fb8eb42014-10-31Stephen R. van den Berg  foreach(dtoid;;int textbin) plugbuffer->add_int16(oidformat(textbin));
7489d72014-11-18Stephen R. van den Berg  plugbuffer->add_int16(sizeof(dtoid));
a362e32014-11-03Stephen R. van den Berg  string cenc=pgsqlsess._runtimeparameter[CLIENT_ENCODING];
fb8eb42014-10-31Stephen R. van den Berg  foreach(paramValues;int i;mixed value) {
e17daf2014-11-09Stephen R. van den Berg  if(undefinedp(value) || objectp(value)&&value->is_val_null)
fb8eb42014-10-31Stephen R. van den Berg  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); break; } default: { if(!value) { plugbuffer->add_int32(-1); break; } value=(string)value; if(String.width(value)>8) if(dtoid[i]==BYTEAOID) value=string_to_utf8(value); else { SUSERERROR("Wide string %O not supported for type OID %d\n", value,dtoid[i]); value=""; } plugbuffer->add_hstring(value,4); break; } case BOOLOID:plugbuffer->add_int32(1); do { int tval; if(stringp(value)) tval=value[0]; else if(!intp(value)) { value=!!value; // cast to boolean break; } else tval=value; switch(tval) { case 'o':case 'O': catch { tval=value[1]; value=tval=='n'||tval=='N'; }; break; default: value=1; break; case 0:case '0':case 'f':case 'F':case 'n':case 'N': value=0; break; } } while(0); plugbuffer->add_int8(value); break; case CHAROID: if(intp(value)) plugbuffer->add_hstring(value,4); else { value=(string)value; switch(sizeof(value)) { default: SUSERERROR( "\"char\" types must be 1 byte wide, got %O\n",value); case 0: plugbuffer->add_int32(-1); // NULL break; case 1: plugbuffer->add_hstring(value[0],4); } } break; case 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; } } if(_tprepared) if(_tprepared.datarowdesc)
bf20922014-11-17Stephen R. van den Berg  gotdatarowdesc(plugbuffer);
fb8eb42014-10-31Stephen R. van den Berg  else if(dontcacheprefix->match(_query)) // Don't cache FETCH/COPY
a362e32014-11-03Stephen R. van den Berg  m_delete(pgsqlsess->_prepareds,_query),_tprepared=0;
bf20922014-11-17Stephen R. van den Berg  if(prepbufferready) {
abf5862014-11-17Stephen R. van den Berg  Thread.MutexKey lock=prepbuffermux->lock();
5527d02014-11-17Stephen R. van den Berg  prepbuffer=plugbuffer;
abf5862014-11-17Stephen R. van den Berg  catch(prepbufferready->signal());
a362e32014-11-03Stephen R. van den Berg  lock=0; }
fb8eb42014-10-31Stephen R. van den Berg  } final void _processrowdesc(array(mapping(string:mixed)) datarowdesc) { _setrowdesc(datarowdesc);
bf20922014-11-17Stephen R. van den Berg  if(_tprepared) _tprepared.datarowdesc=datarowdesc; if(prepbufferready)
0fed8c2014-11-10Stephen R. van den Berg  Thread.Thread(gotdatarowdesc); // Do not use callout, it deadlocks
fb8eb42014-10-31Stephen R. van den Berg  }
bf20922014-11-17Stephen R. van den Berg  private void gotdatarowdesc(void|Stdio.Buffer plugbuffer) {
abf5862014-11-17Stephen R. van den Berg  Thread.MutexKey lock=prepbuffermux->lock();
bf20922014-11-17Stephen R. van den Berg  if(!plugbuffer) { if(!prepbuffer) catch(prepbufferready->wait(lock)); plugbuffer=prepbuffer; prepbuffer=0; // Free memory when plugbuffer leaves scope } if(!prepbufferready || _state==CLOSED) lock=_unnamedstatementkey=0; else { destruct(prepbufferready); // Make sure we do this exactly once
d743bf2014-11-13Stephen R. van den Berg  lock=0;
15d4e12014-11-18Stephen R. van den Berg  plugbuffer->add_int16(sizeof(datarowdesc)); if(sizeof(datarowdesc)) foreach(datarowdesc;;mapping col)
bf20922014-11-17Stephen R. van den Berg  plugbuffer->add_int16(oidformat(col.type)); else if(commitprefix->match(_query)) { lock=pgsqlsess->_commitmux->lock(); if(pgsqlsess->_portalsinflight) { pgsqlsess->_waittocommit++; PD("Commit waiting for portals to finish\n"); pgsqlsess->_readyforcommit->wait(lock); pgsqlsess->_waittocommit--; } lock=0; } PD("Bind portal %O statement %O\n",_portalname,_preparedname); _fetchlimit=pgsqlsess->_fetchlimit; _openportal(); conxion bindbuffer=c->start(1); _unnamedstatementkey=0; bindbuffer->add_int8('B')->add_hstring(plugbuffer,4,4);
7489d72014-11-18Stephen R. van den Berg  if(!_tprepared && sizeof(_preparedname))
bf20922014-11-17Stephen R. van den Berg  closestatement(bindbuffer,_preparedname); _sendexecute(pgsqlsess->_fetchlimit && !(cachealways[_query] || sizeof(_query)>=MINPREPARELENGTH && execfetchlimit->match(_query)) && FETCHLIMITLONGRUN,bindbuffer);
d743bf2014-11-13Stephen R. van den Berg  }
fb8eb42014-10-31Stephen R. van den Berg  } final void _openportal() {
a362e32014-11-03Stephen R. van den Berg  pgsqlsess->_portalsinflight++;
cff3a32014-09-12Stephen R. van den Berg  Thread.MutexKey lock=closemux->lock();
0e7f332014-11-15Stephen R. van den Berg  _state=BOUND;
cff3a32014-09-12Stephen R. van den Berg  lock=0;
bf20922014-11-17Stephen R. van den Berg  statuscmdcomplete=UNDEFINED;
cff3a32014-09-12Stephen R. van den Berg  }
fb8eb42014-10-31Stephen R. van den Berg  final void _purgeportal() {
e302b62014-11-14Stephen R. van den Berg  _unnamedportalkey=_unnamedstatementkey=0;
fb8eb42014-10-31Stephen R. van den Berg  Thread.MutexKey lock=closemux->lock(); _fetchlimit=0; // disables further Executes switch(_state) {
0e7f332014-11-15Stephen R. van den Berg  case COPYINPROGRESS: case BOUND:
bf20922014-11-17Stephen R. van den Berg  datarows->write(1); // Signal EOF
a362e32014-11-03Stephen R. van den Berg  --pgsqlsess->_portalsinflight;
fb8eb42014-10-31Stephen R. van den Berg  }
0e7f332014-11-15Stephen R. van den Berg  _state=CLOSED;
fb8eb42014-10-31Stephen R. van den Berg  lock=0;
e302b62014-11-14Stephen R. van den Berg  releaseconditions();
fb8eb42014-10-31Stephen R. van den Berg  }
0e7f332014-11-15Stephen R. van den Berg  final int _closeportal(bufcon plugbuffer) { int retval=KEEP;
cff3a32014-09-12Stephen R. van den Berg  PD("%O Try Closeportal %d\n",_portalname,_state); Thread.MutexKey lock=closemux->lock(); _fetchlimit=0; // disables further Executes
6a5bed2014-11-13Stephen R. van den Berg  int alreadyfilled=sizeof(plugbuffer); /* alreadyfilled 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. */
cff3a32014-09-12Stephen R. van den Berg  switch(_state) {
0e7f332014-11-15Stephen R. van den Berg  case PORTALINIT:
e302b62014-11-14Stephen R. van den Berg  _unnamedstatementkey=0;
0e7f332014-11-15Stephen R. van den Berg  _state=CLOSED;
fb8eb42014-10-31Stephen R. van den Berg  break;
0e7f332014-11-15Stephen R. van den Berg  case COPYINPROGRESS:
fb8eb42014-10-31Stephen R. van den Berg  PD("CopyDone\n"); plugbuffer->add("c\0\0\0\4");
0e7f332014-11-15Stephen R. van den Berg  case BOUND: _state=CLOSED;
cff3a32014-09-12Stephen R. van den Berg  lock=0; PD("Close portal %O\n",_portalname); if(sizeof(_portalname)) {
fb8eb42014-10-31Stephen R. van den Berg  plugbuffer->add_int8('C')->add_hstring(({'P',_portalname,0}),4,4);
0e7f332014-11-15Stephen R. van den Berg  retval=FLUSHSEND;
cff3a32014-09-12Stephen R. van den Berg  } else _unnamedportalkey=0;
d743bf2014-11-13Stephen R. van den Berg  Thread.MutexKey lockc=pgsqlsess->_commitmux->lock(); if(!--pgsqlsess->_portalsinflight) { if(pgsqlsess->_waittocommit) { PD("Signal no portals in flight\n"); pgsqlsess->_readyforcommit->signal(); lockc=0; } else if(!alreadyfilled)
0e7f332014-11-15Stephen R. van den Berg  pgsqlsess->_readyforquerycount++, retval=SYNCSEND;
a362e32014-11-03Stephen R. van den Berg  pgsqlsess->_pportalcount=0;
cff3a32014-09-12Stephen R. van den Berg  }
d743bf2014-11-13Stephen R. van den Berg  lockc=0;
cff3a32014-09-12Stephen R. van den Berg  } lock=0; return retval; }
15d4e12014-11-18Stephen R. van den Berg  final void _processdataready(array datarow,void|int msglen) { bytesreceived+=msglen;
bf20922014-11-17Stephen R. van den Berg  inflight--; datarows->write(datarow);
15d4e12014-11-18Stephen R. van den Berg  if(++rowsreceived==1)
bf20922014-11-17Stephen R. van den Berg  PD("<%O _fetchlimit %d=min(%d||1,%d), inflight %d\n",_portalname,
15d4e12014-11-18Stephen R. van den Berg  _fetchlimit,(portalbuffersize>>1)*rowsreceived/bytesreceived,
bf20922014-11-17Stephen R. van den Berg  pgsqlsess._fetchlimit,inflight);
cff3a32014-09-12Stephen R. van den Berg  if(_fetchlimit) { _fetchlimit=
15d4e12014-11-18Stephen R. van den Berg  min((portalbuffersize>>1)*rowsreceived/bytesreceived||1,
a362e32014-11-03Stephen R. van den Berg  pgsqlsess._fetchlimit);
cff3a32014-09-12Stephen R. van den Berg  Thread.MutexKey lock=closemux->lock();
bf20922014-11-17Stephen R. van den Berg  if(_fetchlimit && inflight<=_fetchlimit-1)
cff3a32014-09-12Stephen R. van den Berg  _sendexecute(_fetchlimit); else if(!_fetchlimit)
bf20922014-11-17Stephen R. van den Berg  PD("<%O _fetchlimit %d, inflight %d, skip execute\n", _portalname,_fetchlimit,inflight);
cff3a32014-09-12Stephen R. van den Berg  lock=0;
ecbab12008-07-27Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  }
e302b62014-11-14Stephen R. van den Berg  private void releaseconditions() { pgsqlsess=0;
cbe52f2014-11-13Stephen R. van den Berg  Thread.MutexKey lock;
abf5862014-11-17Stephen R. van den Berg  if(prepbufferready) {
cbe52f2014-11-13Stephen R. van den Berg  Thread.MutexKey lock=prepbuffermux->lock();
abf5862014-11-17Stephen R. van den Berg  catch(prepbufferready->signal());
cbe52f2014-11-13Stephen R. van den Berg  }
15d4e12014-11-18Stephen R. van den Berg  if(!datarowdesc) {
cbe52f2014-11-13Stephen R. van den Berg  lock=_ddescribemux->lock();
15d4e12014-11-18Stephen R. van den Berg  datarowdesc=({});
5527d02014-11-17Stephen R. van den Berg  _ddescribe->broadcast();
cbe52f2014-11-13Stephen R. van den Berg  } lock=0;
ecbab12008-07-27Stephen R. van den Berg  }
bf20922014-11-17Stephen R. van den Berg  final void _releasesession(void|string statusccomplete) { if(statusccomplete && !statuscmdcomplete) statuscmdcomplete=statusccomplete; inflight=0; datarows->write(1); // Signal EOF
3b31fa2014-11-14Stephen R. van den Berg  conxion plugbuffer=c->start(1);
e302b62014-11-14Stephen R. van den Berg  plugbuffer->sendcmd(_closeportal(plugbuffer)); releaseconditions(); }
cff3a32014-09-12Stephen R. van den Berg  protected void destroy() { catch { // inside destructors, exceptions don't work _releasesession();
11b13b2014-08-16Martin Nilsson  }; }
ecbab12008-07-27Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg  final void _sendexecute(int fetchlimit,void|bufcon plugbuffer) {
cff3a32014-09-12Stephen R. van den Berg  int flushmode; PD("Execute portal %O fetchlimit %d\n",_portalname,fetchlimit); if(!plugbuffer) plugbuffer=c->start(1); plugbuffer->add_int8('E')->add_hstring(_portalname,4,8+1) ->add_int8(0)->add_int32(fetchlimit);
4ef8a32014-10-29Stephen R. van den Berg  if(!fetchlimit)
0e7f332014-11-15Stephen R. van den Berg  flushmode=_closeportal(plugbuffer)==SYNCSEND?SYNCSEND:FLUSHSEND;
4ef8a32014-10-29Stephen R. van den Berg  else
bf20922014-11-17Stephen R. van den Berg  inflight+=fetchlimit, flushmode=FLUSHSEND;
cff3a32014-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. //!
cff3a32014-09-12Stephen R. van den Berg  //! @seealso //! @[eof()], @[send_row()] array(mixed) fetch_row() { int|array datarow;
bf20922014-11-17Stephen R. van den Berg  if(arrayp(datarow=datarows->try_read()))
cff3a32014-09-12Stephen R. van den Berg  return datarow;
fb8eb42014-10-31Stephen R. van den Berg  if(!eoffound) { if(!datarow && (PD("%O Block for datarow\n",_portalname),
bf20922014-11-17Stephen R. van den Berg  arrayp(datarow=datarows->read())))
fb8eb42014-10-31Stephen R. van den Berg  return datarow; eoffound=1;
bf20922014-11-17Stephen R. van den Berg  datarows->write(1); // Signal EOF for other threads
fb8eb42014-10-31Stephen R. van den Berg  }
cff3a32014-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()] array(array(mixed)) fetch_row_array() { if(eoffound) return 0;
bf20922014-11-17Stephen R. van den Berg  array(array|int) datarow=datarows->try_read_array();
cff3a32014-09-12Stephen R. van den Berg  if(!datarow)
bf20922014-11-17Stephen R. van den Berg  datarow=datarows->read_array();
cff3a32014-09-12Stephen R. van den Berg  if(arrayp(datarow[-1])) return datarow; trydelayederror(); eoffound=1;
bf20922014-11-17Stephen R. van den Berg  datarows->write(1); // Signal EOF for other threads
cff3a32014-09-12Stephen R. van den Berg  return (datarow=datarow[..<1]); }
933a762014-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
cff3a32014-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
cff3a32014-09-12Stephen R. van den Berg  //! @[fetch_row()], @[eof()] void send_row(void|string|array(string) copydata) { trydelayederror(); if(copydata) { PD("CopyData\n");
0e7f332014-11-15Stephen R. van den Berg  c->start()->add_int8('d')->add_hstring(copydata,4,4)->sendcmd(SENDOUT);
cff3a32014-09-12Stephen R. van den Berg  } else _releasesession(); }
6eb5942014-11-12Stephen R. van den Berg  private void run_result_cb(
3b31fa2014-11-14Stephen R. van den Berg  function(sql_result, array(mixed), mixed ...:void) callback,
cff3a32014-09-12Stephen R. van den Berg  array(mixed) args) { int|array datarow;
bf20922014-11-17Stephen R. van den Berg  while(arrayp(datarow=datarows->read_array()))
0fed8c2014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow, @args);
cff3a32014-09-12Stephen R. van den Berg  trydelayederror(); eoffound=1;
0fed8c2014-11-10Stephen R. van den Berg  callout(callback, 0, this, 0, @args);
cff3a32014-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()] void set_result_callback(
3b31fa2014-11-14Stephen R. van den Berg  function(sql_result, array(mixed), mixed ...:void) callback,
cff3a32014-09-12Stephen R. van den Berg  mixed ... args) { if(callback) Thread.Thread(run_result_cb,callback,args); }
6eb5942014-11-12Stephen R. van den Berg  private void run_result_array_cb(
3b31fa2014-11-14Stephen R. van den Berg  function(sql_result, array(array(mixed)), mixed ...:void) callback,
cff3a32014-09-12Stephen R. van den Berg  array(mixed) args) { array(array|int) datarow;
bf20922014-11-17Stephen R. van den Berg  while((datarow=datarows->read_array()) && arrayp(datarow[-1]))
0fed8c2014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow, @args);
cff3a32014-09-12Stephen R. van den Berg  trydelayederror(); eoffound=1; if(sizeof(datarow)>1)
0fed8c2014-11-10Stephen R. van den Berg  callout(callback, 0, this, datarow=datarow[..<1], @args); callout(callback, 0, this, 0, @args);
cff3a32014-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()] void set_result_array_callback(
3b31fa2014-11-14Stephen R. van den Berg  function(sql_result, array(array(mixed)), mixed ...:void) callback,
cff3a32014-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 
11b13b2014-08-16Martin Nilsson }