ecbab12008-07-27Stephen R. van den Berg /* * Some pgsql utility functions. * They are kept here to avoid circular references. * */ #pike __REAL_VERSION__ #include "pgsql.h" #define FLUSH "H\0\0\0\4" //! Some pgsql utility functions
c0f2b72009-04-10Stephen R. van den Berg class PGassist { int(-1..1) peek(int timeout) { }
ecbab12008-07-27Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg  string read(int len,void|int(0..1) not_all) { }
ecbab12008-07-27Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg  int write(string|array(string) data) { }
ecbab12008-07-27Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg  int getchar() { }
ecbab12008-07-27Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg  int close() { }
ecbab12008-07-27Stephen R. van den Berg 
4741cd2008-07-31Stephen R. van den Berg  private final array(string) cmdbuf=({});
ecbab12008-07-27Stephen R. van den Berg #ifdef USEPGsql inherit _PGsql.PGsql; #else object portal;
c0f2b72009-04-10Stephen R. van den Berg  void setportal(void|object newportal) { portal=newportal;
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline int(-1..1) bpeek(int timeout) { return peek(timeout);
ecbab12008-07-27Stephen R. van den Berg  } int flushed=-1;
c0f2b72009-04-10Stephen R. van den Berg  inline final int getbyte() { if(!flushed && !bpeek(0))
ecbab12008-07-27Stephen R. van den Berg  sendflush(); return getchar(); }
c0f2b72009-04-10Stephen R. van den Berg  final string getstring(void|int len) { String.Buffer acc=String.Buffer(); if(!zero_type(len)) { string res; do { if(!flushed && !bpeek(0)) sendflush(); res=read(len,!flushed); if(res) { if(!sizeof(res)) return acc->get(); acc->add(res); }
ecbab12008-07-27Stephen R. van den Berg  } while(sizeof(acc)<len&&res);
27738d2009-02-12Stephen R. van den Berg  return sizeof(acc)?acc->get():res;
ecbab12008-07-27Stephen R. van den Berg  } int c; while((c=getbyte())>0)
27738d2009-02-12Stephen R. van den Berg  acc->putchar(c); return acc->get();
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline final int getint16() { int s0=getbyte();
ecbab12008-07-27Stephen R. van den Berg  int r=(s0&0x7f)<<8|getbyte(); return s0&0x80 ? r-(1<<15) : r ; }
c0f2b72009-04-10Stephen R. van den Berg  inline final int getint32() { int r=getint16();
ecbab12008-07-27Stephen R. van den Berg  r=r<<8|getbyte(); return r<<8|getbyte(); }
c0f2b72009-04-10Stephen R. van den Berg  inline final int getint64() { int r=getint32();
ecbab12008-07-27Stephen R. van den Berg  return r<<32|getint32()&0xffffffff; } #endif
c0f2b72009-04-10Stephen R. van den Berg  inline final string plugbyte(int x) { return String.int2char(x&255);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline final string plugint16(int x) { return sprintf("%c%c",x>>8&255,x&255);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline final string plugint32(int x) { return sprintf("%c%c%c%c",x>>24&255,x>>16&255,x>>8&255,x&255);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline final string plugint64(int x) { return sprintf("%c%c%c%c%c%c%c%c",x>>56&255,x>>48&255,x>>40&255,x>>32&255,
ecbab12008-07-27Stephen R. van den Berg  x>>24&255,x>>16&255,x>>8&255,x&255); }
c0f2b72009-04-10Stephen R. van den Berg  final void sendflush() { sendcmd(({}),1);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  final void sendcmd(string|array(string) data,void|int flush) { if(arrayp(data))
4741cd2008-07-31Stephen R. van den Berg  cmdbuf+=data; else cmdbuf+=({data});
c0f2b72009-04-10Stephen R. van den Berg  switch(flush) { case 3: cmdbuf+=({FLUSH}); flushed=1; break;
4741cd2008-07-31Stephen R. van den Berg  default: flushed=0;
c0f2b72009-04-10Stephen R. van den Berg  break;
4741cd2008-07-31Stephen R. van den Berg  case 1:
c0f2b72009-04-10Stephen R. van den Berg  cmdbuf+=({FLUSH}); PD("Flush\n");
4741cd2008-07-31Stephen R. van den Berg  case 2:
c0f2b72009-04-10Stephen R. van den Berg  flushed=1; { int i=write(cmdbuf); if(portal && portal._pgsqlsess) { portal._pgsqlsess._packetssent++;
cb26232008-08-04Stephen R. van den Berg  portal._pgsqlsess._bytessent+=i; }
c0f2b72009-04-10Stephen R. van den Berg  } cmdbuf=({});
ecbab12008-07-27Stephen R. van den Berg  } }
c0f2b72009-04-10Stephen R. van den Berg  final void sendterminate() { PD("Terminate\n");
4741cd2008-07-31Stephen R. van den Berg  sendcmd(({"X",plugint32(4)}),2);
ecbab12008-07-27Stephen R. van den Berg  close(); }
d432822008-07-31Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg  void create() {
d432822008-07-31Stephen R. van den Berg #ifdef USEPGsql ::create(); #endif }
ecbab12008-07-27Stephen R. van den Berg }
c0f2b72009-04-10Stephen R. van den Berg class PGconn { inherit PGassist:pg;
ecbab12008-07-27Stephen R. van den Berg #ifdef UNBUFFEREDIO inherit Stdio.File:std;
c0f2b72009-04-10Stephen R. van den Berg  inline int getchar() { return std::read(1)[0];
ecbab12008-07-27Stephen R. van den Berg  } #else inherit Stdio.FILE:std;
c0f2b72009-04-10Stephen R. van den Berg  inline int getchar() { return std::getchar();
ecbab12008-07-27Stephen R. van den Berg  } #endif
c0f2b72009-04-10Stephen R. van den Berg  inline int(-1..1) peek(int timeout) { return std::peek(timeout);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline string read(int len,void|int(0..1) not_all) { return std::read(len,not_all);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline int write(string|array(string) data) { return std::write(data);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  int close() { return std::close();
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  void create(Stdio.File stream,object t) { std::create();
ecbab12008-07-27Stephen R. van den Berg  std::assign(stream);
d432822008-07-31Stephen R. van den Berg  pg::create();
ecbab12008-07-27Stephen R. van den Berg  } } #if constant(SSL.sslfile)
c0f2b72009-04-10Stephen R. van den Berg class PGconnS { inherit SSL.sslfile:std;
ecbab12008-07-27Stephen R. van den Berg  inherit PGassist:pg; Stdio.File rawstream;
c0f2b72009-04-10Stephen R. van den Berg  inline int(-1..1) peek(int timeout) { return rawstream.peek(timeout); // This is a kludge
17b7532008-07-30Stephen R. van den Berg  } // Actually SSL.sslfile should provide a peek() method
ecbab12008-07-27Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg  inline string read(int len,void|int(0..1) not_all) { return std::read(len,not_all);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline int write(string|array(string) data) { return std::write(data);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  void create(Stdio.File stream, SSL.context ctx) { rawstream=stream;
ecbab12008-07-27Stephen R. van den Berg  std::create(stream,ctx,1,1);
d432822008-07-31Stephen R. van den Berg  pg::create();
ecbab12008-07-27Stephen R. van den Berg  } } #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()]
ecbab12008-07-27Stephen R. van den Berg class pgsql_result { object _pgsqlsess; private int numrows; private int eoffound; private mixed delayederror; private int copyinprogress; int _fetchlimit;
b156722008-08-25Stephen R. van den Berg int _alltext;
dc97bc2012-04-06Stephen R. van den Berg int _forcetext;
ecbab12008-07-27Stephen R. van den Berg  #ifdef NO_LOCKING int _qmtxkey; #else Thread.MutexKey _qmtxkey; #endif string _portalname; int _bytesreceived; int _rowsreceived; int _interruptable; int _inflight; int _portalbuffersize;
c8942c2009-04-10Stephen R. van den Berg array _params;
ecbab12008-07-27Stephen R. van den Berg string _statuscmdcomplete;
c8942c2009-04-10Stephen R. van den Berg string _query;
ecbab12008-07-27Stephen R. van den Berg array(array(mixed)) _datarows;
6feb862009-01-06Stephen R. van den Berg array(mapping(string:mixed)) _datarowdesc=({});
ecbab12008-07-27Stephen R. van den Berg array(int) _datatypeoid; #ifdef USEPGsql int _buffer; #endif
e064e92008-08-05Stephen R. van den Berg private object fetchmutex;
ecbab12008-07-27Stephen R. van den Berg 
c0f2b72009-04-10Stephen R. van den Berg protected string _sprintf(int type, void|mapping flags) { string res=UNDEFINED; switch(type) { case 'O':
ecbab12008-07-27Stephen R. van den Berg  res=sprintf("pgsql_result numrows: %d eof: %d querylock: %d"
c8942c2009-04-10Stephen R. van den Berg  " inflight: %d\nportalname: %O datarows: %d laststatus: %s\n",
17b7532008-07-30Stephen R. van den Berg  numrows,eoffound,!!_qmtxkey,_inflight, _portalname,sizeof(_datarowdesc), _statuscmdcomplete||"");
ecbab12008-07-27Stephen R. van den Berg  break; } return res; }
c8942c2009-04-10Stephen R. van den Berg void create(object pgsqlsess,string query,int fetchlimit,
dc97bc2012-04-06Stephen R. van den Berg  int portalbuffersize,int alltyped,array params,int forcetext)
c0f2b72009-04-10Stephen R. van den Berg { _pgsqlsess = pgsqlsess;
c8942c2009-04-10Stephen R. van den Berg  _query = query;
ecbab12008-07-27Stephen R. van den Berg  _datarows = ({ }); numrows = UNDEFINED; fetchmutex = Thread.Mutex();
dc97bc2012-04-06Stephen R. van den Berg  _fetchlimit=forcetext?0:fetchlimit;
ecbab12008-07-27Stephen R. van den Berg  _portalbuffersize=portalbuffersize;
b156722008-08-25Stephen R. van den Berg  _alltext = !alltyped;
c8942c2009-04-10Stephen R. van den Berg  _params = params;
dc97bc2012-04-06Stephen R. van den Berg  _forcetext = forcetext;
ecbab12008-07-27Stephen R. van den Berg  steallock(); } //! Returns the command-complete status for this query. //!
2c228b2009-02-15Stephen R. van den Berg //! @seealso //! @[affected_rows()] //! //! @note
ecbab12008-07-27Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
c0f2b72009-04-10Stephen R. van den Berg string status_command_complete() { return _statuscmdcomplete;
ecbab12008-07-27Stephen R. van den Berg } //! Returns the number of affected rows by this query. //!
2c228b2009-02-15Stephen R. van den Berg //! @seealso //! @[status_command_complete()] //! //! @note
ecbab12008-07-27Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
c0f2b72009-04-10Stephen R. van den Berg int affected_rows() { int rows;
ecbab12008-07-27Stephen R. van den Berg  if(_statuscmdcomplete) sscanf(_statuscmdcomplete,"%*s %d",rows); return rows; }
f4c9d62009-02-15Stephen R. van den Berg //! @seealso //! @[Sql.sql_result()->num_fields()]
c0f2b72009-04-10Stephen R. van den Berg int num_fields() { return sizeof(_datarowdesc);
ecbab12008-07-27Stephen R. van den Berg }
f4c9d62009-02-15Stephen R. van den Berg //! @seealso //! @[Sql.sql_result()->num_rows()]
c0f2b72009-04-10Stephen R. van den Berg int num_rows() { int numrows;
a941d32010-03-24Stephen R. van den Berg  if(_statuscmdcomplete) sscanf(_statuscmdcomplete,"%*s %d",numrows);
ecbab12008-07-27Stephen R. van den Berg  return numrows; }
f4c9d62009-02-15Stephen R. van den Berg //! @seealso //! @[Sql.sql_result()->eof()]
c0f2b72009-04-10Stephen R. van den Berg int eof() { return eoffound;
ecbab12008-07-27Stephen R. van den Berg }
f4c9d62009-02-15Stephen R. van den Berg //! @seealso //! @[Sql.sql_result()->fetch_fields()]
c0f2b72009-04-10Stephen R. van den Berg array(mapping(string:mixed)) fetch_fields() { return _datarowdesc+({});
ecbab12008-07-27Stephen R. van den Berg }
c0f2b72009-04-10Stephen R. van den Berg private void releasesession() { if(_pgsqlsess) { if(copyinprogress) { PD("CopyDone\n");
ecbab12008-07-27Stephen R. van den Berg  _pgsqlsess._c.sendcmd("c\0\0\0\4",1); }
6b81f42012-04-10Stephen R. van den Berg  if(_pgsqlsess.is_open()) _pgsqlsess.resync(2);
ecbab12008-07-27Stephen R. van den Berg  } _qmtxkey=UNDEFINED; _pgsqlsess=UNDEFINED; }
c0f2b72009-04-10Stephen R. van den Berg void destroy() { catch // inside destructors, exceptions don't work { releasesession();
ecbab12008-07-27Stephen R. van den Berg  }; }
c0f2b72009-04-10Stephen R. van den Berg inline private array(mixed) getdatarow() { array(mixed) datarow=_datarows[0];
ecbab12008-07-27Stephen R. van den Berg  _datarows=_datarows[1..]; return datarow; }
c0f2b72009-04-10Stephen R. van den Berg private void steallock() {
ecbab12008-07-27Stephen R. van den Berg #ifndef NO_LOCKING PD("Going to steal oldportal %d\n",!!_pgsqlsess._c.portal); Thread.MutexKey stealmtxkey = _pgsqlsess._stealmutex.lock(); do
c0f2b72009-04-10Stephen R. van den Berg  if(_qmtxkey = _pgsqlsess._querymutex.current_locking_key()) { pgsql_result portalb; if(portalb=_pgsqlsess._c.portal) { _pgsqlsess._nextportal++; if(portalb->_interruptable) portalb->fetch_row(2); else { PD("Waiting for the querymutex\n"); if((_qmtxkey=_pgsqlsess._querymutex.lock(2))) { if(copyinprogress)
ecbab12008-07-27Stephen R. van den Berg  error("COPY needs to be finished first\n"); error("Driver bug, please report, "
c0f2b72009-04-10Stephen R. van den Berg  "conflict while interleaving SQL-operations\n");
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  PD("Got the querymutex\n"); } _pgsqlsess._nextportal--;
ecbab12008-07-27Stephen R. van den Berg  } break; } while(!(_qmtxkey=_pgsqlsess._querymutex.trylock())); #else PD("Skipping lock\n"); _qmtxkey=1; #endif _pgsqlsess._c.setportal(this); PD("Stealing successful\n"); }
f4c9d62009-02-15Stephen R. van den Berg //! @decl array(mixed) fetch_row() //! @decl void fetch_row(string|array(string) copydatasend) //! //! @returns //! One result row at a time.
39b0622008-09-02Stephen R. van den Berg //! //! When using COPY FROM STDOUT, this method returns one row at a time //! as a single string containing the entire row.
f4c9d62009-02-15Stephen R. van den Berg //! //! @param copydatasend
39b0622008-09-02Stephen R. van den Berg //! 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 //! explicitly or implicitly destroying the result object, or by passing a //! zero argument to this method. //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[eof()]
c0f2b72009-04-10Stephen R. van den Berg array(mixed) fetch_row(void|int|string|array(string) buffer) {
ecbab12008-07-27Stephen R. van den Berg #ifndef NO_LOCKING Thread.MutexKey fetchmtxkey = fetchmutex.lock(); #endif if(!buffer && sizeof(_datarows)) return getdatarow();
c0f2b72009-04-10Stephen R. van den Berg  if(copyinprogress) { fetchmtxkey = UNDEFINED; if(stringp(buffer) || arrayp(buffer)) { int totalsize=4;
39b0622008-09-02Stephen R. van den Berg  if(arrayp(buffer))
c0f2b72009-04-10Stephen R. van den Berg  foreach(buffer;;string value) totalsize+=sizeof(value);
39b0622008-09-02Stephen R. van den Berg  else
c0f2b72009-04-10Stephen R. van den Berg  totalsize+=sizeof(buffer),buffer=({buffer});
ecbab12008-07-27Stephen R. van den Berg  PD("CopyData\n");
39b0622008-09-02Stephen R. van den Berg  _pgsqlsess._c.sendcmd( ({"d",_pgsqlsess._c.plugint32(totalsize)})+buffer,2);
ecbab12008-07-27Stephen R. van den Berg  } else releasesession(); return UNDEFINED; } mixed err;
c0f2b72009-04-10Stephen R. van den Berg  if(buffer!=2 && (err=delayederror)) { delayederror=UNDEFINED;
ecbab12008-07-27Stephen R. van den Berg  throw(err); }
c0f2b72009-04-10Stephen R. van den Berg  err = catch { if(_portalname) { if(buffer!=2 && !_qmtxkey) { steallock(); if(_fetchlimit) _pgsqlsess._sendexecute(_fetchlimit);
ecbab12008-07-27Stephen R. van den Berg  } while(_pgsqlsess._closesent)
c0f2b72009-04-10Stephen R. van den Berg  _pgsqlsess._decodemsg(); // Flush previous portal sequence for(;;) {
78059a2014-03-01Martin Nilsson #ifdef PG_DEBUGMORE
c0f2b72009-04-10Stephen R. van den Berg  PD("buffer: %d nextportal: %d lock: %d\n",
ecbab12008-07-27Stephen R. van den Berg  buffer,_pgsqlsess._nextportal,!!_qmtxkey); #endif #ifdef USEPGsql
c0f2b72009-04-10Stephen R. van den Berg  _buffer=buffer;
ecbab12008-07-27Stephen R. van den Berg #endif
c0f2b72009-04-10Stephen R. van den Berg  switch(_pgsqlsess._decodemsg()) { case copyinresponse: copyinprogress=1;
ecbab12008-07-27Stephen R. van den Berg  return UNDEFINED;
c0f2b72009-04-10Stephen R. van den Berg  case dataready: _pgsqlsess._mstate=dataprocessed; _rowsreceived++; switch(buffer) { case 0:
ecbab12008-07-27Stephen R. van den Berg  case 1:
c0f2b72009-04-10Stephen R. van den Berg  if(_fetchlimit) _fetchlimit=
ecbab12008-07-27Stephen R. van den Berg  min(_portalbuffersize/2*_rowsreceived/_bytesreceived || 1,
c0f2b72009-04-10Stephen R. van den Berg  _pgsqlsess._fetchlimit);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  switch(buffer) { case 2: case 3: continue; case 1: _interruptable=1; if(_pgsqlsess._nextportal)
ecbab12008-07-27Stephen R. van den Berg  continue; #if STREAMEXECUTES
c0f2b72009-04-10Stephen R. van den Berg  if(_fetchlimit && _inflight<=_fetchlimit-1) _pgsqlsess._sendexecute(_fetchlimit);
ecbab12008-07-27Stephen R. van den Berg #endif
c0f2b72009-04-10Stephen R. van den Berg  return UNDEFINED; }
ecbab12008-07-27Stephen R. van den Berg #if STREAMEXECUTES if(_fetchlimit && _inflight<=_fetchlimit-1)
c0f2b72009-04-10Stephen R. van den Berg  _pgsqlsess._sendexecute(_fetchlimit); // Overlap Executes
ecbab12008-07-27Stephen R. van den Berg #endif
c0f2b72009-04-10Stephen R. van den Berg  return getdatarow(); case commandcomplete: _inflight=0; releasesession(); switch(buffer) { case 1:
ecbab12008-07-27Stephen R. van den Berg  case 2:
c0f2b72009-04-10Stephen R. van den Berg  return UNDEFINED;
ecbab12008-07-27Stephen R. van den Berg  case 3:
c0f2b72009-04-10Stephen R. van den Berg  if(sizeof(_datarows)) return getdatarow();
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  break; case portalsuspended:
ecbab12008-07-27Stephen R. van den Berg  if(_inflight) continue;
c0f2b72009-04-10Stephen R. van den Berg  if(_pgsqlsess._nextportal) { switch(buffer) { case 1: case 2: _qmtxkey = UNDEFINED; return UNDEFINED; case 3: _qmtxkey = UNDEFINED; return getdatarow();
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  _fetchlimit=FETCHLIMITLONGRUN; if(sizeof(_datarows)) { _qmtxkey = UNDEFINED; return getdatarow();
ecbab12008-07-27Stephen R. van den Berg  } buffer=3; }
c0f2b72009-04-10Stephen R. van den Berg  _pgsqlsess._sendexecute(_fetchlimit); default: continue; } break;
ecbab12008-07-27Stephen R. van den Berg  } } eoffound=1; return UNDEFINED; }; PD("Exception %O\n",err);
e7621f2009-02-02Stephen R. van den Berg  _pgsqlsess.resync();
ecbab12008-07-27Stephen R. van den Berg  if(buffer!=2) throw(err); if(!delayederror) delayederror=err; return UNDEFINED; } };