pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:42:
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);
}
- protected void create() {
- }
-
+
final void register_backend() {
if(!clientsregistered++)
Thread.Thread(run_local_backend);
}
final void unregister_backend() {
--clientsregistered;
}
final void throwdelayederror(object parent) {
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:97:
return realbuffer->stashflushmode;
}
//! Some pgsql utility functions
class PGplugbuffer {
inherit Stdio.Buffer;
protected PGassist realbuffer;
- final void create(PGassist _realbuffer) {
+ protected void create(PGassist _realbuffer) {
realbuffer=_realbuffer;
}
final PGplugbuffer start(void|int waitforreal) {
realbuffer->stashcount++;
#ifdef PG_DEBUG
if(waitforreal)
error("pgsql.PGplugbuffer not allowed here\n");
#endif
return this;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:137:
}
class PGassist {
inherit Stdio.Buffer:i;
inherit Stdio.Buffer:o;
protected Thread.Condition fillread;
protected Thread.Mutex fillreadmux;
protected Thread.Queue qportals;
final Stdio.File socket;
+ protected object pgsqlsess;
protected int towrite;
final function(:void) gottimeout;
final int timeout;
- Thread.Mutex nostash;
- Thread.MutexKey started;
- Thread.Mutex stashupdate;
- Thread.Queue stashqueue;
- Thread.Condition stashavail;
- Stdio.Buffer stash;
- int stashflushmode;
- int stashcount;
- int synctransact;
+ 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;
#ifdef PG_DEBUG
- int queueoutidx;
- int queueinidx;
+ final int queueoutidx;
+ final int queueinidx;
#endif
inline void queueup(pgsql_result portal) {
qportals->write(portal); portal->_synctransact=synctransact;
PD(">%O %d %d Queue portal %d bytes\n",portal._portalname,++queueoutidx,
synctransact,sizeof(this));
}
final PGassist|PGplugbuffer start(void|int waitforreal) {
Thread.MutexKey lock;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:241:
Thread.MutexKey lock=stashupdate->lock();
if(sizeof(stash)) {
add(stash); stash->clear();
foreach(stashqueue->try_read_array();;pgsql_result portal)
queueup(portal);
}
mode=mergemode(this,mode);
stashflushmode=keep;
lock=0;
}
+ catch {
outer:
do {
switch(mode) {
default:
break outer;
case syncsend:
PD(">Sync %d %d Queue\n",synctransact,++queueoutidx);
qportals->write(synctransact++); add(PGSYNC);
break;
case flushsend:
PD("Flush\n");
add(PGFLUSH);
case sendout:;
}
if(towrite=sizeof(this)) {
PD(">Sendcmd %O\n",((string)this)[..towrite-1]);
towrite-=output_to(socket,towrite);
}
} while(0);
started=0;
-
+ return;
+ };
+ pgsqlsess->_connectfail();
}
final void sendterminate() {
destruct(fillread); // Delayed close() after flushing the output buffer
}
final int close() {
return socket->close();
}
final void destroy() {
catch(close()); // Exceptions don't work inside destructors
-
+ pgsqlsess=0;
}
- final void connectloop(object pgsqlsess,int nossl) {
+ final void connectloop(int nossl) {
mixed err=catch {
for(;;clear()) {
socket->connect(pgsqlsess._host,pgsqlsess._port);
#if constant(SSL.File)
if(!nossl && !pgsqlsess->nossl
&& (pgsqlsess.options.use_ssl || pgsqlsess.options.force_ssl)) {
PD("SSLRequest\n");
start()->add_int32(8)->add_int32(PG_PROTOCOL(1234,5679))
->sendcmd(sendout);
switch(read_int8()) {
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:309: Inside #if constant(SSL.File)
}
}
#else
if(pgsqlsess.options.force_ssl)
error("Encryption library missing,"
" cannot establish connection to %s:%d\n",
pgsqlsess.host,pgsqlsess.port);
#endif
break;
}
+ if(!socket->is_open())
+ error(strerror(socket->errno()));
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;
};
pgsqlsess->_connectfail(err);
}
- void create(object pgsqlsess,Thread.Queue _qportals,int nossl) {
+ protected void create(object _pgsqlsess,Thread.Queue _qportals,int nossl) {
i::create(); o::create();
qportals = _qportals;
synctransact = 1;
fillread=Thread.Condition();
fillreadmux=Thread.Mutex();
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();
- Thread.Thread(connectloop,pgsqlsess,nossl);
+ pgsqlsess=_pgsqlsess;
+ Thread.Thread(connectloop,nossl);
}
}
//! 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 pgsql_result {
- object _pgsqlsess;
+ protected object pgsqlsess;
protected int numrows;
protected int eoffound;
protected PGassist c;
- mixed _delayederror;
- portalstate _state;
- int _fetchlimit;
- int _alltext;
- int _forcetext;
+ final mixed _delayederror;
+ final portalstate _state;
+ final int _fetchlimit;
+ final int _alltext;
+ final int _forcetext;
- string _portalname;
+ final string _portalname;
- int _bytesreceived;
- int _rowsreceived;
- int _inflight;
- int _portalbuffersize;
- int _synctransact;
- Thread.Condition _ddescribe;
- Thread.Mutex _ddescribemux;
- Thread.MutexKey _unnamedportalkey,_unnamedstatementkey;
+ 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;
protected Thread.Mutex closemux;
- array _params;
- string _statuscmdcomplete;
- string _query;
- Thread.Queue _datarows;
- array(mapping(string:mixed)) _datarowdesc;
- int _oldpbpos;
- object _plugbuffer;
- string _preparedname;
- mapping(string:mixed) _tprepared;
+ final array _params;
+ final string _statuscmdcomplete;
+ final string _query;
+ final Thread.Queue _datarows;
+ final array(mapping(string:mixed)) _datarowdesc;
+ final int _oldpbpos;
+ protected object prepbuffer;
+ protected Thread.Condition prepbufferready;
+ protected Thread.Mutex prepbuffermux;
+ final string _preparedname;
+ final mapping(string:mixed) _tprepared;
protected string _sprintf(int type, void|mapping flags) {
string res=UNDEFINED;
switch(type) {
case 'O':
res=sprintf("pgsql_result numrows: %d eof: %d inflight: %d\n"
#ifdef PG_DEBUGMORE
"query: %O\n"
#endif
"portalname: %O datarows: %d"
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:395: Inside #if defined(PG_DEBUGMORE)
#ifdef PG_DEBUGMORE
_query,
#endif
_portalname,_datarowdesc&&sizeof(_datarowdesc),
_statuscmdcomplete||"");
break;
}
return res;
}
- void create(object pgsqlsess,PGassist _c,string query,
+ protected void create(object _pgsqlsess,PGassist _c,string query,
int portalbuffersize,int alltyped,array params,int forcetext) {
- _pgsqlsess = pgsqlsess;
+ pgsqlsess = _pgsqlsess;
c = _c;
_query = query;
_datarows = Thread.Queue(); numrows = UNDEFINED;
_ddescribe=Thread.Condition();
_ddescribemux=Thread.Mutex();
closemux=Thread.Mutex();
-
+ prepbufferready=Thread.Condition();
+ prepbuffermux=Thread.Mutex();
_portalbuffersize=portalbuffersize;
_alltext = !alltyped;
_params = params;
_forcetext = forcetext;
_state = portalinit;
}
//! Returns the command-complete status for this query.
//!
//! @seealso
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:499:
final void _preparebind(array dtoid) {
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
object plugbuffer=Stdio.Buffer();
plugbuffer->add(_portalname=
- (_unnamedportalkey=_pgsqlsess._unnamedportalmux->trylock(1))
- ? "" : PORTALPREFIX+int2hex(_pgsqlsess._pportalcount++) )->add_int8(0)
+ (_unnamedportalkey=pgsqlsess._unnamedportalmux->trylock(1))
+ ? "" : PORTALPREFIX+int2hex(pgsqlsess._pportalcount++) )->add_int8(0)
->add(_preparedname)->add_int8(0)->add_int16(sizeof(paramValues));
foreach(dtoid;;int textbin)
plugbuffer->add_int16(oidformat(textbin));
plugbuffer->add_int16(sizeof(paramValues));
- string cenc=_pgsqlsess._runtimeparameter[CLIENT_ENCODING];
+ string cenc=pgsqlsess._runtimeparameter[CLIENT_ENCODING];
foreach(paramValues;int i;mixed value) {
if(undefinedp(value))
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:
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:618:
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;
}
}
- _plugbuffer=plugbuffer;
+ prepbuffer=plugbuffer;
+ int skipsignal=0;
if(_tprepared)
if(_tprepared.datarowdesc)
- _dodatarows();
+ skipsignal=1, gotdatarowdesc();
else if(dontcacheprefix->match(_query)) // Don't cache FETCH/COPY
- m_delete(_pgsqlsess->_prepareds,_query),_tprepared=0;
+ m_delete(pgsqlsess->_prepareds,_query),_tprepared=0;
+ if(!skipsignal) {
+ Thread.MutexKey lock=prepbuffermux->lock();
+ 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)
- Thread.Thread(_dodatarows,this);
+ Thread.Thread(gotdatarowdesc);
if(tp)
tp.datarowdesc=datarowdesc;
}
- final void _dodatarows() {
- object plugbuffer=_plugbuffer;
- _plugbuffer=0;
+ protected void gotdatarowdesc() {
+ if(!prepbuffer) {
+ Thread.MutexKey lock=prepbuffermux->lock();
+ prepbufferready->wait(lock);
+ lock=0;
+ }
+ object plugbuffer=prepbuffer;
+ prepbuffer=0;
plugbuffer->add_int16(sizeof(_datarowdesc));
foreach(_datarowdesc;;mapping col)
plugbuffer->add_int16(oidformat(col.type));
PD("Bind portal %O statement %O\n",_portalname,_preparedname);
- _fetchlimit=_pgsqlsess->_fetchlimit;
+ _fetchlimit=pgsqlsess->_fetchlimit;
_openportal();
object bindbuffer=c->start(1);
_unnamedstatementkey=0;
bindbuffer->add_int8('B')->add_hstring(plugbuffer,4,4);
if(!_tprepared)
closestatement(bindbuffer,_preparedname);
- _sendexecute(_pgsqlsess->_fetchlimit
+ _sendexecute(pgsqlsess->_fetchlimit
&& !(cachealways[_query]
|| sizeof(_query)>=MINPREPARELENGTH &&
execfetchlimit->match(_query))
&& FETCHLIMITLONGRUN,bindbuffer);
}
final void _openportal() {
- _pgsqlsess->_portalsinflight++;
+ pgsqlsess->_portalsinflight++;
Thread.MutexKey lock=closemux->lock();
_state=bound;
lock=0;
_statuscmdcomplete=UNDEFINED;
}
final void _purgeportal() {
_unnamedportalkey=0;
Thread.MutexKey lock=closemux->lock();
_fetchlimit=0; // disables further Executes
switch(_state) {
case copyinprogress:
case bound:
- --_pgsqlsess->_portalsinflight;
+ --pgsqlsess->_portalsinflight;
}
_state=closed;
lock=0;
}
final sctype _closeportal(PGplugbuffer plugbuffer) {
sctype retval=keep;
PD("%O Try Closeportal %d\n",_portalname,_state);
Thread.MutexKey lock=closemux->lock();
_fetchlimit=0; // disables further Executes
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:698:
plugbuffer->add("c\0\0\0\4");
case bound:
_state=closed;
lock=0;
PD("Close portal %O\n",_portalname);
if(sizeof(_portalname)) {
plugbuffer->add_int8('C')->add_hstring(({'P',_portalname,0}),4,4);
retval=flushsend;
} else
_unnamedportalkey=0;
- if(!--_pgsqlsess->_portalsinflight) {
- _pgsqlsess->_readyforquerycount++;
- _pgsqlsess->_pportalcount=0;
+ if(!--pgsqlsess->_portalsinflight) {
+ pgsqlsess->_readyforquerycount++;
+ pgsqlsess->_pportalcount=0;
retval=syncsend;
}
}
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);
+ pgsqlsess._fetchlimit,_inflight);
if(_fetchlimit) {
_fetchlimit=
min((_portalbuffersize>>1)*_rowsreceived/_bytesreceived||1,
- _pgsqlsess._fetchlimit);
+ pgsqlsess._fetchlimit);
Thread.MutexKey lock=closemux->lock();
if(_fetchlimit && _inflight<=_fetchlimit-1)
_sendexecute(_fetchlimit);
else if(!_fetchlimit)
PD("<%O _fetchlimit %d, _inflight %d, skip execute\n",
_portalname,_fetchlimit,_inflight);
lock=0;
}
}
final void _releasesession() {
_inflight=0;
_datarows->write(1); // Signal EOF
object plugbuffer=c->start(1);
plugbuffer->sendcmd(_closeportal(plugbuffer));
- _pgsqlsess=UNDEFINED;
+ pgsqlsess=UNDEFINED;
}
protected void destroy() {
catch { // inside destructors, exceptions don't work
_releasesession();
};
}
final void _sendexecute(int fetchlimit,void|PGplugbuffer plugbuffer) {
int flushmode;
pike.git/lib/modules/Sql.pmod/pgsql_util.pmod:798:
array(array(mixed)) fetch_row_array() {
if(eoffound)
return 0;
array(array|int) datarow=_datarows->try_read_array();
if(!datarow)
datarow=_datarows->read_array();
if(arrayp(datarow[-1]))
return datarow;
trydelayederror();
eoffound=1;
+ _datarows->write(1); // Signal EOF for other threads
return (datarow=datarow[..<1]);
}
//! @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