pike.git / lib / modules / Sql.pmod / pgsql_util.pmod

version» Context lines:

pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:403:   }      //! The result object returned by @[Sql.pgsql()->big_query()], except for   //! the noted differences it behaves the same as @[Sql.sql_result].   //!   //! @seealso   //! @[Sql.sql_result], @[Sql.pgsql], @[Sql.Sql], @[Sql.pgsql()->big_query()]   class sql_result {       private object pgsqlsess; -  private int numrows; +     private int eoffound;    private conxion c;    final mixed _delayederror;    final int _state;    final int _fetchlimit;    final int _alltext;    final int _forcetext;       final string _portalname;    -  +  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; +  private string statuscmdcomplete;    final int _bytesreceived; -  final int _rowsreceived; -  final int _inflight; -  final int _portalbuffersize; +     final int _synctransact;    final Thread.Condition _ddescribe;    final Thread.Mutex _ddescribemux;    final Thread.MutexKey _unnamedportalkey,_unnamedstatementkey; -  private Thread.Mutex closemux; +     final array _params; -  final string _statuscmdcomplete; +     final string _query; -  final Thread.Queue _datarows; +     final array(mapping(string:mixed)) _datarowdesc; -  final int _oldpbpos; -  private Stdio.Buffer prepbuffer; -  private Thread.Condition prepbufferready; -  private Thread.Mutex prepbuffermux; +     final string _preparedname;    final mapping(string:mixed) _tprepared;       private string _sprintf(int type, void|mapping flags) {    string res=UNDEFINED;    switch(type) {    case 'O': -  res=sprintf("sql_result numrows: %d eof: %d inflight: %d\n" +  res=sprintf("sql_result state: %d numrows: %d eof: %d inflight: %d\n"    "query: %O\n"    "portalname: %O datarows: %d"    " laststatus: %s\n", -  numrows,eoffound,_inflight, +  _state,rowsreceived,eoffound,inflight,    _query,    _portalname,_datarowdesc&&sizeof(_datarowdesc), -  _statuscmdcomplete||""); +  statuscmdcomplete||(_unnamedstatementkey?"*parsing*":""));    break;    }    return res;    }       protected void create(object _pgsqlsess,conxion _c,string query, -  int portalbuffersize,int alltyped,array params,int forcetext) { +  int _portalbuffersize,int alltyped,array params,int forcetext) {    pgsqlsess = _pgsqlsess;    c = _c;    _query = query; -  _datarows = Thread.Queue(); numrows = UNDEFINED; +  datarows = Thread.Queue();    _ddescribe=Thread.Condition();    _ddescribemux=Thread.Mutex();    closemux=Thread.Mutex();    prepbufferready=Thread.Condition();    prepbuffermux=Thread.Mutex(); -  _portalbuffersize=portalbuffersize; +  portalbuffersize=_portalbuffersize;    _alltext = !alltyped;    _params = params;    _forcetext = forcetext;    _state = PORTALINIT;    }       //! 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; +  return statuscmdcomplete;    }       //! 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); +  if(statuscmdcomplete) +  sscanf(statuscmdcomplete,"%*s %d",rows);    return rows;    }       private void waitfordescribe() {    Thread.MutexKey lock=_ddescribemux->lock();    if(!_datarowdesc)    _ddescribe->wait(lock);    lock=0;    }   
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:517:    if(!_datarowdesc)    waitfordescribe();    trydelayederror();    return sizeof(_datarowdesc);    }       //! @seealso    //! @[Sql.sql_result()->num_rows()]    int num_rows() {    trydelayederror(); -  return _rowsreceived; +  return rowsreceived;    }       private inline void trydelayederror() {    if(_delayederror)    throwdelayederror(this);    }       //! @seealso    //! @[Sql.sql_result()->eof()]    int eof() {
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:679:    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) -  prepbuffer=plugbuffer, gotdatarowdesc(); +  gotdatarowdesc(plugbuffer);    else if(dontcacheprefix->match(_query)) // Don't cache FETCH/COPY    m_delete(pgsqlsess->_prepareds,_query),_tprepared=0; -  if(!prepbuffer) { +  if(prepbufferready) {    Thread.MutexKey lock=prepbuffermux->lock();    prepbuffer=plugbuffer;    catch(prepbufferready->signal());    lock=0;    }    }       final void _processrowdesc(array(mapping(string:mixed)) datarowdesc) {    _setrowdesc(datarowdesc); -  mapping(string:mixed) tp=_tprepared; // FIXME Is caching this worthwhile? -  if(!tp || !tp.datarowdesc) +  if(_tprepared) +  _tprepared.datarowdesc=datarowdesc; +  if(prepbufferready)    Thread.Thread(gotdatarowdesc); // Do not use callout, it deadlocks -  if(tp) -  tp.datarowdesc=datarowdesc; +     }    -  private void gotdatarowdesc() { +  private void gotdatarowdesc(void|Stdio.Buffer plugbuffer) {    Thread.MutexKey lock=prepbuffermux->lock(); -  +  if(!plugbuffer) {    if(!prepbuffer)    catch(prepbufferready->wait(lock)); -  destruct(prepbufferready); +  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    lock=0; -  if(_state==CLOSED) -  return; -  Stdio.Buffer plugbuffer=prepbuffer; -  prepbuffer=0; // Free memory early +     plugbuffer->add_int16(sizeof(_datarowdesc));    if(sizeof(_datarowdesc))    foreach(_datarowdesc;;mapping col)    plugbuffer->add_int16(oidformat(col.type));    else if(commitprefix->match(_query)) { -  Thread.MutexKey lock=pgsqlsess->_commitmux->lock(); +  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;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:737:    _unnamedstatementkey=0;    bindbuffer->add_int8('B')->add_hstring(plugbuffer,4,4);    if(!_tprepared)    closestatement(bindbuffer,_preparedname);    _sendexecute(pgsqlsess->_fetchlimit    && !(cachealways[_query]    || sizeof(_query)>=MINPREPARELENGTH &&    execfetchlimit->match(_query))    && FETCHLIMITLONGRUN,bindbuffer);    } +  }       final void _openportal() {    pgsqlsess->_portalsinflight++;    Thread.MutexKey lock=closemux->lock();    _state=BOUND;    lock=0; -  _statuscmdcomplete=UNDEFINED; +  statuscmdcomplete=UNDEFINED;    }       final void _purgeportal() {    _unnamedportalkey=_unnamedstatementkey=0;    Thread.MutexKey lock=closemux->lock();    _fetchlimit=0; // disables further Executes    switch(_state) {    case COPYINPROGRESS:    case BOUND: -  _datarows->write(1); // Signal EOF +  datarows->write(1); // Signal EOF    --pgsqlsess->_portalsinflight;    }    _state=CLOSED;    lock=0;    releaseconditions();    }       final int _closeportal(bufcon plugbuffer) {    int retval=KEEP;    PD("%O Try Closeportal %d\n",_portalname,_state);
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:804:    } else if(!alreadyfilled)    pgsqlsess->_readyforquerycount++, retval=SYNCSEND;    pgsqlsess->_pportalcount=0;    }    lockc=0;    }    lock=0;    return retval;    }    -  final void _processdataready() { -  _rowsreceived++; -  if(_rowsreceived==1) -  PD("<%O _fetchlimit %d=min(%d||1,%d), _inflight %d\n",_portalname, -  _fetchlimit,(_portalbuffersize>>1)*_rowsreceived/_bytesreceived, -  pgsqlsess._fetchlimit,_inflight); +  final void _processdataready(array datarow) { +  inflight--; +  datarows->write(datarow); +  rowsreceived++; +  if(rowsreceived==1) +  PD("<%O _fetchlimit %d=min(%d||1,%d), inflight %d\n",_portalname, +  _fetchlimit,(portalbuffersize>>1)*rowsreceived/_bytesreceived, +  pgsqlsess._fetchlimit,inflight);    if(_fetchlimit) {    _fetchlimit= -  min((_portalbuffersize>>1)*_rowsreceived/_bytesreceived||1, +  min((portalbuffersize>>1)*rowsreceived/_bytesreceived||1,    pgsqlsess._fetchlimit);    Thread.MutexKey lock=closemux->lock(); -  if(_fetchlimit && _inflight<=_fetchlimit-1) +  if(_fetchlimit && inflight<=_fetchlimit-1)    _sendexecute(_fetchlimit);    else if(!_fetchlimit) -  PD("<%O _fetchlimit %d, _inflight %d, skip execute\n", -  _portalname,_fetchlimit,_inflight); +  PD("<%O _fetchlimit %d, inflight %d, skip execute\n", +  _portalname,_fetchlimit,inflight);    lock=0;    }    }       private void releaseconditions() {    pgsqlsess=0;    Thread.MutexKey lock;    if(prepbufferready) {    Thread.MutexKey lock=prepbuffermux->lock();    catch(prepbufferready->signal());    }    if(!_datarowdesc) {    lock=_ddescribemux->lock();    _datarowdesc=({});    _ddescribe->broadcast();    }    lock=0;    }    -  final void _releasesession() { -  _inflight=0; -  _datarows->write(1); // Signal EOF +  final void _releasesession(void|string statusccomplete) { +  if(statusccomplete && !statuscmdcomplete) +  statuscmdcomplete=statusccomplete; +  inflight=0; +  datarows->write(1); // Signal EOF    conxion plugbuffer=c->start(1);    plugbuffer->sendcmd(_closeportal(plugbuffer));    releaseconditions();    }       protected void destroy() {    catch { // inside destructors, exceptions don't work    _releasesession();    };    }
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:863:    final void _sendexecute(int fetchlimit,void|bufcon plugbuffer) {    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);    if(!fetchlimit)    flushmode=_closeportal(plugbuffer)==SYNCSEND?SYNCSEND:FLUSHSEND;    else -  _inflight+=fetchlimit, flushmode=FLUSHSEND; +  inflight+=fetchlimit, flushmode=FLUSHSEND;    plugbuffer->sendcmd(flushmode,this);    }       //! @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.    //!    //! @seealso    //! @[eof()], @[send_row()]    array(mixed) fetch_row() {    int|array datarow; -  if(arrayp(datarow=_datarows->try_read())) +  if(arrayp(datarow=datarows->try_read()))    return datarow;    if(!eoffound) {    if(!datarow    && (PD("%O Block for datarow\n",_portalname), -  arrayp(datarow=_datarows->read()))) +  arrayp(datarow=datarows->read())))    return datarow;    eoffound=1; -  _datarows->write(1); // Signal EOF for other threads +  datarows->write(1); // Signal EOF for other threads    }    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; -  array(array|int) datarow=_datarows->try_read_array(); +  array(array|int) datarow=datarows->try_read_array();    if(!datarow) -  datarow=_datarows->read_array(); +  datarow=datarows->read_array();    if(arrayp(datarow[-1]))    return datarow;    trydelayederror();    eoffound=1; -  _datarows->write(1); // Signal EOF for other threads +  datarows->write(1); // Signal EOF for other threads    return (datarow=datarow[..<1]);    }       //! @param copydata    //! 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
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:938:    PD("CopyData\n");    c->start()->add_int8('d')->add_hstring(copydata,4,4)->sendcmd(SENDOUT);    } else    _releasesession();    }       private void run_result_cb(    function(sql_result, array(mixed), mixed ...:void) callback,    array(mixed) args) {    int|array datarow; -  while(arrayp(datarow=_datarows->read_array())) +  while(arrayp(datarow=datarows->read_array()))    callout(callback, 0, this, datarow, @args);    trydelayederror();    eoffound=1;    callout(callback, 0, this, 0, @args);    }       //! 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).    //!
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:962:    function(sql_result, array(mixed), mixed ...:void) callback,    mixed ... args) {    if(callback)    Thread.Thread(run_result_cb,callback,args);    }       private void run_result_array_cb(    function(sql_result, array(array(mixed)), mixed ...:void) callback,    array(mixed) args) {    array(array|int) datarow; -  while((datarow=_datarows->read_array()) && arrayp(datarow[-1])) +  while((datarow=datarows->read_array()) && arrayp(datarow[-1]))    callout(callback, 0, this, datarow, @args);    trydelayederror();    eoffound=1;    if(sizeof(datarow)>1)    callout(callback, 0, this, datarow=datarow[..<1], @args);    callout(callback, 0, this, 0, @args);    }       //! Sets up a callback for sets of rows returned from the database.    //! First argument passed is the resultobject itself, second argument