pike.git/lib/modules/Sql.pmod/pgsql.pike:262:
//! and that the other end hasn't closed it yet. No data is
//! sent over the connection.
//!
//! For a more reliable check of whether the connection
//! is alive, please use @[ping()].
//!
//! @seealso
//! @[ping()]
int is_open() {
catch {
- return c->socket->query_fd()>=0;
+ return c->socket->is_open();
};
return 0;
}
//! @decl int ping()
//!
//! Check whether the connection is alive.
//!
//! @returns
//! Returns one of the following:
pike.git/lib/modules/Sql.pmod/pgsql.pike:312:
PD("CancelRequest\n");
object lcon=getsocket(1);
lcon->add_int32(16)->add_int32(PG_PROTOCOL(1234,5678))
->add_int32(backendpid)->add(cancelsecret)->sendcmd(flushsend);
lcon->close();
#ifdef PG_DEBUGMORE
PD("Closetrace %O\n",backtrace());
#endif
object plugbuffer=c->start(1);
foreach(qportals->peek_array();;object portal)
+ if(objectp(portal))
portal->_closeportal(plugbuffer);
plugbuffer->sendcmd(sendout);
}
//! Changes the connection charset. When set to @expr{"UTF8"@}, the query,
//! parameters and results can be Pike-native wide strings.
//!
//! @param charset
//! A PostgreSQL charset name.
//!
pike.git/lib/modules/Sql.pmod/pgsql.pike:569:
}
protected void storetiming(object portal) {
mapping(string:mixed) tp=portal._tprepared;
tp.trun=gethrtime()-tp.trunstart;
m_delete(tp,"trunstart");
portal._tprepared = UNDEFINED;
}
final void _processloop(object ci) {
- int die=0,terminating=0;
+ int terminating=0;
int|.pgsql_util.pgsql_result portal;
mixed err;
{
object plugbuffer=Stdio.Buffer()->add_int32(PG_PROTOCOL(3,0));
if(user)
plugbuffer->add("user\0")->add(user)->add_int8(0);
if(database)
plugbuffer->add("database\0")->add(database)->add_int8(0);
options->reconnect=undefinedp(options->reconnect) || options->reconnect;
foreach(options
pike.git/lib/modules/Sql.pmod/pgsql.pike:783: Inside #if defined(PG_DEBUG)
msglen-=4+1;
PD("<ReadyForQuery %c\n",backendstatus);
#endif
for(;objectp(portal);portal->read()) {
#ifdef PG_DEBUG
showportal(msgtype);
#endif
portal->_purgeportal();
}
foreach(qportals->peek_array();;.pgsql_util.pgsql_result qp) {
+ if(objectp(qp) && qp._synctransact && qp._synctransact<=portal) {
PD("Checking portal %O %d<=%d\n",
qp._portalname,qp._synctransact,portal);
- if(qp._synctransact && qp._synctransact<=portal)
+
qp->_purgeportal();
}
-
+ }
portal=0;
_readyforquerycount--;
if(readyforquery_cb)
readyforquery_cb(),readyforquery_cb=0;
if(waitforauthready)
destruct(waitforauthready);
break;
case '1':
#ifdef PG_DEBUG
PD("<ParseComplete\n");
pike.git/lib/modules/Sql.pmod/pgsql.pike:815: Inside #if defined(PG_DEBUG)
PD("<%O ParameterDescription %d values\n",portal._query,cols);
msglen-=4+2+4*cols;
#endif
foreach(a=allocate(cols);int i;)
a[i]=ci->read_int32();
#ifdef PG_DEBUGMORE
PD("%O\n",a);
#endif
if(portal._tprepared)
portal._tprepared.datatypeoid=a;
- portal->_preparebind(a);
+ Thread.Thread(portal->_preparebind,a);
break;
}
case 'T': {
array a;
#ifdef PG_DEBUG
int cols=ci->read_int16();
PD("<RowDescription %d columns %O\n",cols,portal._query);
msglen-=4+2;
foreach(a=allocate(cols);int i;)
#else
pike.git/lib/modules/Sql.pmod/pgsql.pike:858: Inside #if defined(PG_DEBUG)
*/
res.formatcode=ci->read_int16();
#else
ci->consume(8);
#endif
a[i]=res;
}
#ifdef PG_DEBUGMORE
PD("%O\n",a);
#endif
+ if(portal._forcetext)
+ portal->_setrowdesc(a); // Do not consume queued portal
+ else {
portal->_processrowdesc(a);
portal=0;
-
+ }
break;
}
case 'n': {
#ifdef PG_DEBUG
msglen-=4;
PD("<NoData %O\n",portal._query);
#endif
portal._fetchlimit=0; // disables subsequent Executes
portal->_processrowdesc(({}));
portal=0;
pike.git/lib/modules/Sql.pmod/pgsql.pike:908:
tp.trunstart=tend;
}
}
break;
}
case 'D': {
msglen-=4;
string serror;
if(portal._tprepared)
storetiming(portal);
- array a, datarowdesc;
+
portal._bytesreceived+=msglen;
- datarowdesc=portal._datarowdesc;
+ array datarowdesc=portal._datarowdesc;
int cols=ci->read_int16();
#ifdef PG_DEBUG
#ifdef PG_DEBUGMORE
PD("<%O DataRow %d cols %d bytes\n",portal._portalname,cols,msglen);
#endif
datarowdebugcount++;
if(!datarowdebug)
datarowdebug=sprintf(
"<%O DataRow %d cols %d bytes",portal._portalname,cols,msglen);
#endif
int atext = portal._alltext; // cache locally for speed
int forcetext = portal._forcetext; // cache locally for speed
string cenc=_runtimeparameter[CLIENT_ENCODING];
- a=allocate(cols,UNDEFINED);
+ array a=allocate(cols,UNDEFINED);
msglen-=2+4*cols;
foreach(datarowdesc;int i;mapping m) {
int collen=ci->read_sint(4);
if(collen>0) {
msglen-=collen;
mixed value;
switch(int typ=m.type) {
case FLOAT4OID:
#if SIZEOF_FLOAT>=8
case FLOAT8OID:
pike.git/lib/modules/Sql.pmod/pgsql.pike:1081:
mapping(string:string) msgresponse;
msgresponse=getresponse();
warningsdropcount+=warningscollected;
warningscollected=0;
switch(msgresponse.C) {
case "P0001":
lastmessage=({sprintf("%s: %s",msgresponse.S,msgresponse.M)});
USERERROR(a2nls(lastmessage
+({pinpointerror(portal._query,msgresponse.P)})
+showbindings(portal)));
- case "57P01":case "57P02":case "57P03":die=1;
+ case "57P01":case "57P02":case "57P03":
preplastmessage(msgresponse);
- PD(a2nls(lastmessage));USERERROR(a2nls(lastmessage));
+ PD(a2nls(lastmessage));throw(0);
case "08P01":case "42P05":
errtype=protocolerror;
case "XX000":case "42883":case "42P01":
invalidatecache=1;
default:
preplastmessage(msgresponse);
if(msgresponse.D)
lastmessage+=({msgresponse.D});
if(msgresponse.H)
lastmessage+=({msgresponse.H});
pike.git/lib/modules/Sql.pmod/pgsql.pike:1158:
default:
if(msgtype!=-1) {
string s;
PD("Unknown message received %c\n",msgtype);
s=ci->read(msglen-=4);PD("%O\n",s);
#ifdef PG_DEBUG
msglen=0;
#endif
errtype=protocolunsupported;
} else {
- if(!waitforauthready)
- die=1;
+
lastmessage+=({
sprintf("Connection lost to database %s@%s:%d/%s %d\n",
user,_host,_port,database,backendpid)});
-
+ if(!waitforauthready)
+ throw(0);
USERERROR(a2nls(lastmessage));
}
break;
}
#ifdef PG_DEBUG
if(msglen)
errtype=protocolerror;
#endif
{
string msg;
pike.git/lib/modules/Sql.pmod/pgsql.pike:1194:
}
}; // We only get here if there is an error
if(err==MAGICTERMINATE) {
ci->start()->add("X\0\0\0\4")->sendcmd(sendout);
terminating=1;
if(!sizeof(ci))
break;
}
if(stringp(err)) {
.pgsql_util.pgsql_result or;
- if(!(or=portal))
+ if(!objectp(or=portal))
or=this;
if(!or._delayederror)
or._delayederror=err;
- if(portal)
+ if(objectp(portal))
portal->_releasesession();
portal=0;
continue;
}
break;
}
_delayederror=err;
if(!ci->close() && !terminating && options.reconnect)
_connectfail();
-
+ if(err)
throw(err);
}
//! Closes the connection to the database, any running queries are
//! terminated instantly.
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
void close() {
pike.git/lib/modules/Sql.pmod/pgsql.pike:1230:
c->sendterminate();
c=0;
}
void destroy() {
close();
.pgsql_util.unregister_backend();
}
void _connectfail(void|mixed err) {
- if(err)
- _delayederror=err;
+ PD("Connect failed %O reconnectdelay %d\n",err,reconnectdelay);
if(!err || reconnectdelay) {
int tdelay;
switch(tdelay=reconnectdelay) {
case 0:
reconnectdelay=RECONNECTDELAY;
break;
default:
-
+ if(err)
+ _delayederror=err;
if(options.reconnect!=-1)
return;
reconnectdelay=RECONNECTBACKOFF;
break;
}
Thread.MutexKey lock=waitforauth->lock();
if(!waitforauthready)
waitforauthready=Thread.Condition();
lock=0;
-
+ PD("Schedule reconnect in %ds\n",tdelay);
+ _delayederror=0;
.pgsql_util.local_backend->call_out(reconnect,tdelay,1);
-
+ } else if(err)
+ _delayederror=err;
}
- }
+
protected int reconnect(void|int force,void|object tt) {
-
+ PD("(Re)connect\n");
if(!force) {
Thread.MutexKey lock=waitforauth->lock();
if(waitforauthready) {
lock=0;
return 0; // Connect still in progress in other thread
}
waitforauthready=Thread.Condition();
lock=0;
}
if(c) {
-
+ PD("Close old connection\n");
reconnected++;
#ifdef PG_STATS
prepstmtused=0;
#endif
if(!force)
c->sendterminate();
else
c->close();
c=0;
-
+ PD("Flushing old cache\n");
foreach(_prepareds;;mapping tp)
m_delete(tp,"preparedname");
if(!options.reconnect)
return 0;
}
-
+ PD("Actually start to connect\n");
qportals=Thread.Queue();
_readyforquerycount=1;
qportals->write(1);
if(!(c=getsocket())) {
string msg=sprintf("Couldn't connect to database on %s:%d",_host,_port);
if(force) {
if(!sizeof(lastmessage) || lastmessage[sizeof(lastmessage)-1]!=msg)
lastmessage+=({msg});
return 0;
} else
pike.git/lib/modules/Sql.pmod/pgsql.pike:1707:
}
final inline void closestatement(object plugbuffer,string oldprep) {
.pgsql_util.closestatement(plugbuffer,oldprep);
}
protected inline string int2hex(int i) {
return String.int2hex(i);
}
- final void throwdelayederror(object parent) {
+ final inline void throwdelayederror(object parent) {
.pgsql_util.throwdelayederror(parent);
}
//! @decl Sql.pgsql_util.pgsql_result big_query(string query)
//! @decl Sql.pgsql_util.pgsql_result big_query(string query, mapping bindings)
//!
//! This is the only provided interface which allows you to query the
//! database. If you wish to use the simpler @[Sql.Sql()->query()] function,
//! you need to use the @[Sql.Sql] generic SQL-object.
//!
pike.git/lib/modules/Sql.pmod/pgsql.pike:1777:
case UTF8CHARSET:
q=string_to_utf8(q);
break;
default:
if(String.width(q)>8)
ERROR("Don't know how to convert %O to %s encoding\n",q,cenc);
}
array(string|int) paramValues;
array from;
if(bindings) {
+ if(forcetext)
+ q = .sql_util.emulate_bindings(q, bindings, this), paramValues=({});
+ else {
int pi=0,rep=0;
paramValues=allocate(sizeof(bindings));
from=allocate(sizeof(bindings));
array(string) to=allocate(sizeof(bindings));
foreach(bindings; mixed name; mixed value) {
if(stringp(name)) { // Throws if mapping key is empty string
if(name[0]!=':')
name=":"+name;
if(name[1]=='_') { // Special option parameter
switch(name) {
pike.git/lib/modules/Sql.pmod/pgsql.pike:1813:
else {
paramValues[pi++]=value;
rval=sprintf("$%d",pi);
}
to[rep++]=rval;
}
if(rep--)
q=replace(q,from=from[..rep],to=to[..rep]);
paramValues= pi ? paramValues[..pi-1] : ({});
from=({from,to,paramValues});
+ }
} else
- paramValues = ({});
+ paramValues=({});
if(String.width(q)>8)
ERROR("Wide string literals in %O not supported\n",q);
if(has_value(q,"\0"))
ERROR("Querystring %O contains invalid literal nul-characters\n",q);
mapping(string:mixed) tp;
if(waitforauthready) {
Thread.MutexKey lock=waitforauth->lock();
catch(waitforauthready->wait(lock));
lock=0;
}
int tstart;
- if(forcetext) {
- if(bindings)
- q = .sql_util.emulate_bindings(q, bindings, this);
- } else if(forcecache==1
+ if(!forcetext && forcecache==1
|| forcecache!=0
&& (sizeof(q)>=MINPREPARELENGTH || .pgsql_util.cachealways[q])) {
object plugbuffer=c->start();
if(tp=_prepareds[q]) {
if(tp.preparedname) {
#ifdef PG_STATS
prepstmtused++;
#endif
preparedname=tp.preparedname;
} else if((tstart=tp.trun)
pike.git/lib/modules/Sql.pmod/pgsql.pike:1855:
if(totalhits>=cachedepth)
foreach(_prepareds;string ind;tp) {
int oldhits=tp.hits;
totalhits-=oldhits-(tp.hits=oldhits>>1);
if(oldhits<=1) {
closestatement(plugbuffer,tp.preparedname);
m_delete(_prepareds,ind);
}
}
if(forcecache!=1 && .pgsql_util.createprefix->match(q)) {
- invalidatecache=1; // Flush cache on CREATE
+ invalidatecache=1; // Flush cache on CREATE
tp=UNDEFINED;
} else
_prepareds[q]=tp=([]);
}
if(invalidatecache) {
invalidatecache=0;
foreach(_prepareds;;mapping np) {
closestatement(plugbuffer,np.preparedname);
m_delete(np,"preparedname");
}
pike.git/lib/modules/Sql.pmod/pgsql.pike:1883:
} else // pgsql_result autoassigns to portal
tp=UNDEFINED;
object portal;
portal=.pgsql_util.pgsql_result(this,c,q,
portalbuffersize,_alltyped,from,forcetext);
portal._tprepared=tp;
#ifdef PG_STATS
portalsopened++;
#endif
clearmessage=1;
- object plugbuffer=c;
+
if(forcetext) { // FIXME What happens if portals are still open?
portal._unnamedportalkey=_unnamedportalmux->lock(1);
-
+ portal._portalname="";
portal->_openportal();
_readyforquerycount++;
Thread.MutexKey lock=unnamedstatement->lock(1);
- plugbuffer->start(1)->add_int8('Q')->add_hstring(q,4,4+1)->add_int8(0)
+ c->start(1)->add_int8('Q')->add_hstring(q,4,4+1)->add_int8(0)
->sendcmd(flushlogsend,portal);
lock=0;
PD("Simple query: %O\n",q);
} else {
- object parsebuffer;
+ object plugbuffer;
if(!sizeof(preparedname) || !tp || !tp.preparedname) {
if(!sizeof(preparedname))
preparedname=
(portal._unnamedstatementkey=unnamedstatement->trylock(1))
? "" : PTSTMTPREFIX+int2hex(ptstmtcount++);
// Even though the protocol doesn't require the Parse command to be
// followed by a flush, it makes a VERY noticeable difference in
// performance if it is omitted; seems like a flaw in the PostgreSQL
// server v8.3.3
PD("Parse statement %O=%O\n",preparedname,q);
- parsebuffer=plugbuffer->start()->add_int8('P')
+ plugbuffer=c->start()->add_int8('P')
->add_hstring(({preparedname,0,q,"\0\0\0"}),4,4)->add(PGFLUSH);
}
if(!tp || !tp.datatypeoid) {
PD("Describe statement %O\n",preparedname);
- (parsebuffer||plugbuffer->start())->add_int8('D')
+ (plugbuffer||c->start())->add_int8('D')
->add_hstring(({'S',preparedname,0}),4,4)->sendcmd(flushsend,portal);
} else {
- if(parsebuffer)
- parsebuffer->sendcmd();
+ if(plugbuffer)
+ plugbuffer->sendcmd();
#ifdef PG_STATS
skippeddescribe++;
#endif
portal->_setrowdesc(tp.datarowdesc);
}
portal._preparedname=preparedname;
if((portal._tprepared=tp) && tp.datatypeoid) {
mixed e=catch(portal->_preparebind(tp.datatypeoid));
- if(e && !portal._delayederror) {
- if(!stringp(e))
+ if(e && !portal._delayederror)
throw(e);
- portal._delayederror=e;
+
}
}
- }
+
throwdelayederror(portal);
return portal;
}
//! This is an alias for @[big_query()], since @[big_query()] already supports
//! streaming of multiple simultaneous queries through the same connection.
//!
//! @seealso
//! @[big_query()], @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result]
object streaming_query(string q,void|mapping(string|int:mixed) bindings) {