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
863cf32014-08-16Martin Nilsson { 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)
863cf32014-08-16Martin Nilsson  { portal=newportal;
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  inline int(-1..1) bpeek(int timeout)
863cf32014-08-16Martin Nilsson  { return peek(timeout);
ecbab12008-07-27Stephen R. van den Berg  } int flushed=-1;
c0f2b72009-04-10Stephen R. van den Berg  inline final int getbyte()
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { String.Buffer acc=String.Buffer();
7692302014-08-15Martin Nilsson  if(!undefinedp(len))
863cf32014-08-16Martin Nilsson  { string res;
c0f2b72009-04-10Stephen R. van den Berg  do
863cf32014-08-16Martin Nilsson  { if(!flushed && !bpeek(0))
c0f2b72009-04-10Stephen R. van den Berg  sendflush(); res=read(len,!flushed); if(res)
863cf32014-08-16Martin Nilsson  { if(!sizeof(res))
c0f2b72009-04-10Stephen R. van den Berg  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()
863cf32014-08-16Martin Nilsson  { 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()
863cf32014-08-16Martin Nilsson  { 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()
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { return sprintf("%c%c%c%c%c%c%c%c",x>>56&255,x>>48&255,x>>40&255,x>>32&255, 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  final void sendflush()
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { if(arrayp(data))
4741cd2008-07-31Stephen R. van den Berg  cmdbuf+=data; else cmdbuf+=({data});
c0f2b72009-04-10Stephen R. van den Berg  switch(flush)
863cf32014-08-16Martin Nilsson  { case 3: cmdbuf+=({FLUSH}); flushed=1; break; default: flushed=0; break; case 1: cmdbuf+=({FLUSH}); PD("Flush\n"); case 2: flushed=1; { int i=write(cmdbuf); if(portal && portal._pgsqlsess) { portal._pgsqlsess._packetssent++; portal._pgsqlsess._bytessent+=i; } } cmdbuf=({});
ecbab12008-07-27Stephen R. van den Berg  } }
c0f2b72009-04-10Stephen R. van den Berg  final void sendterminate()
863cf32014-08-16Martin Nilsson  { 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
863cf32014-08-16Martin Nilsson { 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()
863cf32014-08-16Martin Nilsson  { 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()
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { return std::write(data);
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  int close()
863cf32014-08-16Martin Nilsson  { return std::close();
ecbab12008-07-27Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  void create(Stdio.File stream,object t)
863cf32014-08-16Martin Nilsson  { 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  } }
5c32022014-06-01Martin Nilsson #if constant(SSL.File)
c0f2b72009-04-10Stephen R. van den Berg class PGconnS
863cf32014-08-16Martin Nilsson { inherit SSL.File: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)
863cf32014-08-16Martin Nilsson  { return rawstream.peek(timeout); // This is a kludge
5c32022014-06-01Martin Nilsson  } // Actually SSL.File 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)
863cf32014-08-16Martin Nilsson  { 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)
863cf32014-08-16Martin Nilsson  { return std::write(data);
ecbab12008-07-27Stephen R. van den Berg  }
3909e62014-05-15Martin Nilsson  void create(Stdio.File stream, SSL.Context ctx)
863cf32014-08-16Martin Nilsson  { rawstream=stream;
4435d32014-05-22Martin Nilsson  std::create(stream,ctx);
ad67812014-05-17Henrik Grubbström (Grubba)  std::set_blocking(); if (!std::connect()) { error("Secure connection failed.\n"); }
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 {
863cf32014-08-16Martin Nilsson  object _pgsqlsess; private int numrows; private int eoffound; private mixed delayederror; private int copyinprogress; int _fetchlimit; int _alltext; int _forcetext;
ecbab12008-07-27Stephen R. van den Berg  #ifdef NO_LOCKING
863cf32014-08-16Martin Nilsson  int _qmtxkey;
ecbab12008-07-27Stephen R. van den Berg #else
863cf32014-08-16Martin Nilsson  Thread.MutexKey _qmtxkey;
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  string _portalname; int _bytesreceived; int _rowsreceived; int _interruptable; int _inflight; int _portalbuffersize; array _params; string _statuscmdcomplete; string _query; array(array(mixed)) _datarows; array(mapping(string:mixed)) _datarowdesc=({}); array(int) _datatypeoid;
ecbab12008-07-27Stephen R. van den Berg #ifdef USEPGsql
863cf32014-08-16Martin Nilsson  int _buffer;
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  private object fetchmutex;
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  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"
863cf32014-08-16Martin Nilsson  " inflight: %d\nportalname: %O datarows: %d" " laststatus: %s\n", numrows,eoffound,!!_qmtxkey,_inflight, _portalname,sizeof(_datarowdesc), _statuscmdcomplete||"");
ecbab12008-07-27Stephen R. van den Berg  break;
863cf32014-08-16Martin Nilsson  } return res;
ecbab12008-07-27Stephen R. van den Berg  }
863cf32014-08-16Martin Nilsson  void create(object pgsqlsess,string query,int fetchlimit, int portalbuffersize,int alltyped,array params,int forcetext) { _pgsqlsess = pgsqlsess; _query = query; _datarows = ({ }); numrows = UNDEFINED; fetchmutex = Thread.Mutex(); _fetchlimit=forcetext?0:fetchlimit; _portalbuffersize=portalbuffersize; _alltext = !alltyped; _params = params; _forcetext = forcetext; steallock(); }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-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. string status_command_complete() { return _statuscmdcomplete; }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-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. int affected_rows() { int rows; if(_statuscmdcomplete) sscanf(_statuscmdcomplete,"%*s %d",rows); return rows; }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->num_fields()] int num_fields() { return sizeof(_datarowdesc); }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->num_rows()] int num_rows() { int numrows; if(_statuscmdcomplete) sscanf(_statuscmdcomplete,"%*s %d",numrows); return numrows; }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->eof()] int eof() { return eoffound; }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  //! @seealso //! @[Sql.sql_result()->fetch_fields()] array(mapping(string:mixed)) fetch_fields() { return _datarowdesc+({}); }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  private void releasesession() { if(_pgsqlsess) { if(copyinprogress) { PD("CopyDone\n"); _pgsqlsess._c.sendcmd("c\0\0\0\4",1); } if(_pgsqlsess.is_open()) _pgsqlsess.resync(2);
ecbab12008-07-27Stephen R. van den Berg  }
863cf32014-08-16Martin Nilsson  _qmtxkey=UNDEFINED; _pgsqlsess=UNDEFINED;
ecbab12008-07-27Stephen R. van den Berg  }
863cf32014-08-16Martin Nilsson  private void destroy() { catch // inside destructors, exceptions don't work { releasesession(); }; }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  inline private array(mixed) getdatarow() { array(mixed) datarow=_datarows[0]; _datarows=_datarows[1..]; return datarow; }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  private void steallock() {
ecbab12008-07-27Stephen R. van den Berg #ifndef NO_LOCKING
863cf32014-08-16Martin Nilsson  PD("Going to steal oldportal %d\n",!!_pgsqlsess._c.portal); Thread.MutexKey stealmtxkey = _pgsqlsess._stealmutex.lock(); do 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) error("COPY needs to be finished first\n"); error("Driver bug, please report, " "conflict while interleaving SQL-operations\n"); } PD("Got the querymutex\n"); } _pgsqlsess._nextportal--; } break;
ecbab12008-07-27Stephen R. van den Berg  }
863cf32014-08-16Martin Nilsson  while(!(_qmtxkey=_pgsqlsess._querymutex.trylock()));
ecbab12008-07-27Stephen R. van den Berg #else
863cf32014-08-16Martin Nilsson  PD("Skipping lock\n"); _qmtxkey=1;
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  _pgsqlsess._c.setportal(this); PD("Stealing successful\n"); }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson  //! @decl array(mixed) fetch_row() //! @decl void fetch_row(string|array(string) copydatasend) //! //! @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. //! //! @param copydatasend //! 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 //! @[eof()] array(mixed) fetch_row(void|int|string|array(string) buffer) {
ecbab12008-07-27Stephen R. van den Berg #ifndef NO_LOCKING
863cf32014-08-16Martin Nilsson  Thread.MutexKey fetchmtxkey = fetchmutex.lock();
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  if(!buffer && sizeof(_datarows)) return getdatarow(); if(copyinprogress) { fetchmtxkey = UNDEFINED; if(stringp(buffer) || arrayp(buffer)) { int totalsize=4; if(arrayp(buffer)) foreach(buffer;;string value) totalsize+=sizeof(value); else totalsize+=sizeof(buffer),buffer=({buffer}); PD("CopyData\n"); _pgsqlsess._c.sendcmd( ({"d",_pgsqlsess._c.plugint32(totalsize)})+buffer,2); }
39b0622008-09-02Stephen R. van den Berg  else
863cf32014-08-16Martin Nilsson  releasesession(); return UNDEFINED;
ecbab12008-07-27Stephen R. van den Berg  }
863cf32014-08-16Martin Nilsson  mixed err; if(buffer!=2 && (err=delayederror)) { delayederror=UNDEFINED; throw(err); } err = catch
c0f2b72009-04-10Stephen R. van den Berg  {
863cf32014-08-16Martin Nilsson  if(_portalname) { if(buffer!=2 && !_qmtxkey) { steallock(); if(_fetchlimit) _pgsqlsess._sendexecute(_fetchlimit); } while(_pgsqlsess._closesent) _pgsqlsess._decodemsg(); // Flush previous portal sequence for(;;) {
78059a2014-03-01Martin Nilsson #ifdef PG_DEBUGMORE
863cf32014-08-16Martin Nilsson  PD("buffer: %d nextportal: %d lock: %d\n", buffer,_pgsqlsess._nextportal,!!_qmtxkey);
ecbab12008-07-27Stephen R. van den Berg #endif #ifdef USEPGsql
863cf32014-08-16Martin Nilsson  _buffer=buffer;
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  switch(_pgsqlsess._decodemsg()) { case copyinresponse: copyinprogress=1; return UNDEFINED; case dataready: _pgsqlsess._mstate=dataprocessed; _rowsreceived++; switch(buffer) { case 0: case 1: if(_fetchlimit) _fetchlimit= min(_portalbuffersize/2*_rowsreceived/_bytesreceived || 1, _pgsqlsess._fetchlimit); } switch(buffer) { case 2: case 3: continue; case 1: _interruptable=1; if(_pgsqlsess._nextportal) continue;
ecbab12008-07-27Stephen R. van den Berg #if STREAMEXECUTES
863cf32014-08-16Martin Nilsson  if(_fetchlimit && _inflight<=_fetchlimit-1) _pgsqlsess._sendexecute(_fetchlimit);
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  return UNDEFINED; }
ecbab12008-07-27Stephen R. van den Berg #if STREAMEXECUTES
863cf32014-08-16Martin Nilsson  if(_fetchlimit && _inflight<=_fetchlimit-1) _pgsqlsess._sendexecute(_fetchlimit); // Overlap Executes
ecbab12008-07-27Stephen R. van den Berg #endif
863cf32014-08-16Martin Nilsson  return getdatarow(); case commandcomplete: _inflight=0; releasesession(); switch(buffer) { case 1: case 2: return UNDEFINED; case 3: if(sizeof(_datarows)) return getdatarow(); } break; case portalsuspended: if(_inflight) continue; if(_pgsqlsess._nextportal) { switch(buffer) { case 1: case 2: _qmtxkey = UNDEFINED; return UNDEFINED; case 3: _qmtxkey = UNDEFINED; return getdatarow(); } _fetchlimit=FETCHLIMITLONGRUN; if(sizeof(_datarows)) { _qmtxkey = UNDEFINED; return getdatarow(); } buffer=3; } _pgsqlsess._sendexecute(_fetchlimit); default: continue; } break; } } eoffound=1; return UNDEFINED; }; PD("Exception %O\n",err); _pgsqlsess.resync(); if(buffer!=2) throw(err); if(!delayederror) delayederror=err;
ecbab12008-07-27Stephen R. van den Berg  return UNDEFINED;
863cf32014-08-16Martin Nilsson  }
ecbab12008-07-27Stephen R. van den Berg 
863cf32014-08-16Martin Nilsson }