pike.git/lib/modules/Sql.pmod/pgsql.pike:53:
//!
//! @seealso
//! @[Sql.Sql], @[Sql.postgres], @url{http://www.postgresql.org/docs/@}
#pike __REAL_VERSION__
#include "pgsql.h"
#define ERROR(X ...) predef::error(X)
- protected int fetchlimit=FETCHLIMIT;
- protected Thread.Mutex unnamedportalmux,unnamedstatement;
+ int _fetchlimit=FETCHLIMIT;
+ Thread.Mutex _unnamedportalmux;
+ protected Thread.Mutex unnamedstatement;
int _portalsinflight;
protected .pgsql_util.PGassist c;
protected string cancelsecret;
protected int backendpid;
protected int backendstatus;
mapping(string:mixed) options;
protected array(string) lastmessage=({});
protected int clearmessage;
protected mapping(string:array(mixed)) notifylist=([]);
mapping(string:string) _runtimeparameter;
- protected enum querystate {
- streconnect,reconnectforce,queryidle,inquery,cancelpending,canceled};
- protected querystate qstate;
- protected mapping(string:mapping(string:mixed)) prepareds=([]);
+ mapping(string:mapping(string:mixed)) _prepareds=([]);
protected int pstmtcount;
protected int ptstmtcount; // Periodically one would like to reset this
// but checking when this is safe to do
// probably is more costly than the gain
int _pportalcount;
protected int totalhits;
protected int cachedepth=STATEMENTCACHEDEPTH;
protected int timeout=QUERYTIMEOUT;
protected int portalbuffersize=PORTALBUFFERSIZE;
protected int reconnected; // Number of times the connection was reset
pike.git/lib/modules/Sql.pmod/pgsql.pike:98:
protected int warningsdropcount; // Number of uncollected warnings
protected int warningscollected;
protected int invalidatecache;
protected Thread.Queue qportals;
mixed _delayederror;
protected function (:void) readyforquery_cb;
string _host;
protected string database, user, pass;
int _port;
- protected multiset cachealways=(<"BEGIN","begin","END","end","COMMIT","commit">);
- protected object createprefix
- =Regexp("^[ \t\f\r\n]*[Cc][Rr][Ee][Aa][Tt][Ee][ \t\f\r\n]");
- protected object dontcacheprefix
- =Regexp("^[ \t\f\r\n]*([Ff][Ee][Tt][Cc][Hh]|[Cc][Oo][Pp][Yy])[ \t\f\r\n]");
- protected object execfetchlimit
- =Regexp("^[ \t\f\r\n]*(([Uu][Pp][Dd][Aa]|[Dd][Ee][Ll][Ee])[Tt][Ee]|\
- [Ii][Nn][Ss][Ee][Rr][Tt])[ \t\f\r\n]|\
- [ \t\f\r\n][Ll][Ii][Mm][Ii][Tt][ \t\f\r\n]+[12][; \t\f\r\n]*$");
+
protected Thread.Mutex waitforauth;
protected Thread.Condition waitforauthready;
int _readyforquerycount;
- #define DERROR(msg ...) ({sprintf(msg),backtrace()})
- #define SERROR(msg ...) (sprintf(msg))
- #define USERERROR(msg) throw(msg)
- #define SUSERERROR(msg ...) USERERROR(SERROR(msg))
-
+
protected string _sprintf(int type, void|mapping flags) {
string res=UNDEFINED;
switch(type) {
case 'O':
res=sprintf(DRIVERNAME"(%s@%s:%d/%s,%d)",
user,_host,_port,database,backendpid);
break;
}
return res;
}
- #define BOOLOID 16
- #define BYTEAOID 17
- #define CHAROID 18
- #define INT8OID 20
- #define INT2OID 21
- #define INT4OID 23
- #define TEXTOID 25
- #define OIDOID 26
- #define XMLOID 142
- #define FLOAT4OID 700
- #define FLOAT8OID 701
- #define MACADDROID 829
- #define INETOID 869 /* Force textmode */
- #define BPCHAROID 1042
- #define VARCHAROID 1043
- #define CTIDOID 1247
- #define UUIDOID 2950
-
- #define UTF8CHARSET "UTF8"
- #define CLIENT_ENCODING "client_encoding"
-
+
//! @decl void create()
//! @decl void create(string host, void|string database, void|string user,@
//! void|string password, void|mapping(string:mixed) options)
//!
//! With no arguments, this function initialises (reinitialises if a
//! connection has been set up previously) a connection to the
//! PostgreSQL backend. Since PostgreSQL requires a database to be
//! selected, it will try to connect to the default database. The
//! connection may fail however, for a variety of reasons; in this case
//! the most likely reason is because you don't have sufficient privileges
pike.git/lib/modules/Sql.pmod/pgsql.pike:339:
//! Cancels all currently running queries in this session.
//!
//! @seealso
//! @[reload()], @[resync()]
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
void cancelquery() {
- qstate=cancelpending;
+
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)
+ 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.
//!
//! @seealso
//! @[get_charset()], @[create()],
pike.git/lib/modules/Sql.pmod/pgsql.pike:454:
//! @member int "portals_in_flight"
//! Currently still open portals, i.e. running statements.
//! @endmapping
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
mapping(string:mixed) getstatistics() {
mapping(string:mixed) stats=([
"warnings_dropped":warningsdropcount,
- "current_prepared_statements":sizeof(prepareds),
+ "current_prepared_statements":sizeof(_prepareds),
"current_prepared_statement_hits":totalhits,
"prepared_statement_count":pstmtcount,
#ifdef PG_STATS
"used_prepared_statements":prepstmtused,
"skipped_describe_count":skippeddescribe,
"portals_opened_count":portalsopened,
#endif
"messages_received":_msgsreceived,
"bytes_received":_bytesreceived,
"reconnect_count":reconnected,
pike.git/lib/modules/Sql.pmod/pgsql.pike:531:
//! @param newfetchlimit
//! Sets the new fetchlimit to interleave queries.
//!
//! @returns
//! The previous fetchlimit.
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
int setfetchlimit(void|int newfetchlimit) {
- int oldfetchlimit=fetchlimit;
+ int oldfetchlimit=_fetchlimit;
if(!undefinedp(newfetchlimit) && newfetchlimit>=0)
- fetchlimit=newfetchlimit;
+ _fetchlimit=newfetchlimit;
return oldfetchlimit;
}
final protected string glob2reg(string glob) {
if(!glob||!sizeof(glob))
return "%";
return replace(glob,({"*","?","\\","%","_"}),({"%","_","\\\\","\\%","\\_"}));
}
final protected string a2nls(array(string) msg) {
pike.git/lib/modules/Sql.pmod/pgsql.pike:556:
final protected string pinpointerror(void|string query,void|string offset) {
if(!query)
return "";
int k=(int)offset;
if(k<=0)
return MARKSTART+query+MARKEND;
return MARKSTART+(k>1?query[..k-2]:"")+MARKERROR+query[k-1..]+MARKEND;
}
- protected void reconnect_cb() {
+ protected void connect_cb() {
PD("%O\n",_runtimeparameter);
- if(qstate==reconnectforce) {
- lastmessage+=
- ({sprintf("Reconnected to database %s",host_info())});
- runcallback(backendpid,"_reconnect","");
+
}
- }
+
- protected void processrowdescription(object portal) {
- mapping(string:mixed) tp=portal._tprepared;
- if(!tp || !tp.datarowdesc)
- Thread.Thread(dodatarows,portal);
- if(tp)
- tp.datarowdesc=portal._datarowdesc;
+ protected void reconnect_cb() {
+ lastmessage+=({sprintf("Reconnected to database %s",host_info())});
+ runcallback(backendpid,"_reconnect","");
}
protected array(string) showbindings(object portal) {
array(string) msgs=({});
array from;
if(portal && (from = portal._params)) {
array to,paramValues;
[from,to,paramValues] = from;
if(sizeof(paramValues)) {
string val;
pike.git/lib/modules/Sql.pmod/pgsql.pike:655: Inside #if defined(PG_DEBUG)
datarowdebug=0; datarowdebugcount=0;
}
#endif
int msgtype=ci->read_int8();
if(!portal) {
portal=qportals->try_read();
#ifdef PG_DEBUG
showportal(msgtype);
#endif
}
- if(qstate==cancelpending)
- qstate=canceled,sendclose();
+
int msglen=ci->read_int32();
_msgsreceived++;
_bytesreceived+=1+msglen;
enum errortype {
noerror=0,
protocolerror,
protocolunsupported
};
errortype errtype=noerror;
switch(msgtype) {
pike.git/lib/modules/Sql.pmod/pgsql.pike:710:
};
case 'R': {
PD("<Authentication ");
string sendpass;
int authtype;
msglen-=4+4;
switch(authtype=ci->read_int32()) {
case 0:
PD("Ok\n");
.pgsql_util.local_backend->remove_call_out(reconnect);
- ci->gottimeout=gottimeout;
+ ci->gottimeout=cancelquery;
ci->timeout=timeout;
reconnectdelay=0;
cancelsecret="";
break;
case 2:
PD("KerberosV5\n");
errtype=protocolunsupported;
break;
case 3:
PD("ClearTextPassword\n");
pike.git/lib/modules/Sql.pmod/pgsql.pike:819:
case 'Z':
backendstatus=ci->read_int8();
#ifdef PG_DEBUG
msglen-=4+1;
PD("<ReadyForQuery %c\n",backendstatus);
#endif
for(;objectp(portal);portal->read()) {
#ifdef PG_DEBUG
showportal(msgtype);
#endif
- portal->_closeportal();
+ portal->_purgeportal();
}
foreach(qportals->peek_array();;.pgsql_util.pgsql_result qp) {
PD("Checking portal %O %d<=%d\n",
qp._portalname,qp._synctransact,portal);
if(qp._synctransact && qp._synctransact<=portal)
- qp->_closeportal();
+ qp->_purgeportal();
}
portal=0;
_readyforquerycount--;
if(readyforquery_cb)
readyforquery_cb(),readyforquery_cb=0;
- qstate=queryidle;
- if(waitforauthready) {
- Thread.MutexKey lock=waitforauth->lock();
- waitforauthready->broadcast();
- waitforauthready=0;
- lock=0;
- }
+ if(waitforauthready)
+ destruct(waitforauthready);
break;
case '1':
#ifdef PG_DEBUG
PD("<ParseComplete\n");
msglen-=4;
#endif
break;
case 't': {
array a;
int cols=ci->read_int16();
pike.git/lib/modules/Sql.pmod/pgsql.pike:859: 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;
- preparebind(portal);
+ portal->_preparebind();
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;) {
+ foreach(a=allocate(cols);int i;)
#else
- foreach(a=allocate(ci->read_int16());int i;) {
+ foreach(a=allocate(ci->read_int16());int i;)
#endif
-
+ {
string s=ci->read_cstring();
mapping(string:mixed) res=(["name":s]);
#ifdef PG_DEBUG
msglen-=sizeof(s)+1+4+2+4+2+4+2;
res.tableoid=ci->read_int32()||UNDEFINED;
res.tablecolattr=ci->read_int16()||UNDEFINED;
#else
ci->consume(6);
#endif
res.type=ci->read_int32();
pike.git/lib/modules/Sql.pmod/pgsql.pike:901: 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
- portal._datarowdesc=a;
- processrowdescription(portal);
+ portal->_processrowdesc(a);
portal=0;
break;
}
case 'n': {
#ifdef PG_DEBUG
msglen-=4;
PD("<NoData %O\n",portal._query);
#endif
- portal._datarowdesc=({});
+
portal._fetchlimit=0; // disables subsequent Executes
- processrowdescription(portal);
+ portal->_processrowdesc(({}));
portal=0;
break;
}
case 'H':
- portal._datarowdesc=getcols();
+ portal->_processrowdesc(getcols());
PD("<CopyOutResponse %d %O\n",
sizeof(portal._datarowdesc),portal._query);
- processrowdescription(portal);
+
break;
case '2': {
mapping tp;
#ifdef PG_DEBUG
msglen-=4;
PD("<%O BindComplete\n",portal._portalname);
#endif
if(tp=portal._tprepared) {
int tend=gethrtime();
int tstart=tp.trun;
if(tend==tstart)
- m_delete(prepareds,portal._query);
+ m_delete(_prepareds,portal._query);
else {
tp.hits++;
totalhits++;
if(!tp.preparedname) {
if(sizeof(portal._preparedname))
tp.preparedname=portal._preparedname;
tstart=tend-tstart;
if(!tp.tparse || tp.tparse>tstart)
tp.tparse=tstart;
}
pike.git/lib/modules/Sql.pmod/pgsql.pike:1036:
}
}
a[i]=value;
} else if(!collen)
a[i]="";
}
portal._inflight--;
portal._datarows->write(a);
if(serror)
ERROR(serror);
- portal->_processdataready(fetchlimit);
+ portal->_processdataready();
break;
}
case 's':
#ifdef PG_DEBUG
PD("<%O PortalSuspended\n",portal._portalname);
msglen-=4;
#endif
portal=0;
break;
case 'C': {
pike.git/lib/modules/Sql.pmod/pgsql.pike:1091: Inside #if defined(PG_DEBUG)
msglen-=4;
#ifdef PG_DEBUG
if(msglen<0)
errtype=protocolerror;
#endif
portal._bytesreceived+=msglen;
portal._datarows->write(({ci->read(msglen)}));
#ifdef PG_DEBUG
msglen=0;
#endif
- portal->_processdataready(fetchlimit);
+ portal->_processdataready();
break;
case 'G':
- portal._datarowdesc=getcols();
+ portal->_setrowdesc(getcols());
PD("<%O CopyInResponse %d columns\n",
portal._portalname,sizeof(portal._datarowdesc));
portal._state=copyinprogress;
{
Thread.MutexKey resultlock=portal._resultmux->lock();
portal._newresult.signal();
resultlock=0;
}
break;
case 'c':
pike.git/lib/modules/Sql.pmod/pgsql.pike:1254:
if(portal)
portal->_releasesession();
portal=0;
continue;
}
break;
}
_delayederror=err;
if(!ci->close() && !terminating && options.reconnect)
_connectfail();
+ 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() {
cancelquery();
pike.git/lib/modules/Sql.pmod/pgsql.pike:1317:
if(c) {
reconnected++;
#ifdef PG_STATS
prepstmtused=0;
#endif
if(!force)
c->sendterminate();
else
c->close();
c=0;
- foreach(prepareds;;mapping tp)
+ foreach(_prepareds;;mapping tp)
m_delete(tp,"preparedname");
if(!options.reconnect)
return 0;
}
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
ERROR(msg+"\n");
}
_runtimeparameter=([]);
- unnamedportalmux=Thread.Mutex();
+ _unnamedportalmux=Thread.Mutex();
unnamedstatement=Thread.Mutex();
- qstate=force?reconnectforce:streconnect;
- readyforquery_cb=reconnect_cb;
+ readyforquery_cb=force?reconnect_cb:connect_cb;
_portalsinflight=0;
return 1;
}
//! @decl void reload()
//!
//! For PostgreSQL this function performs the same function as @[resync()].
//!
//! @seealso
//! @[resync()], @[cancelquery()]
void reload() {
resync();
}
protected void resync_cb() {
switch(backendstatus) {
case 'T':case 'E':
- foreach(prepareds;;mapping tp) {
+ foreach(_prepareds;;mapping tp) {
m_delete(tp,"datatypeoid");
m_delete(tp,"datarowdesc");
}
big_query("ROLLBACK");
big_query("RESET ALL");
big_query("CLOSE ALL");
big_query("DISCARD TEMP");
}
}
pike.git/lib/modules/Sql.pmod/pgsql.pike:1721:
mapping m=mkmapping(colnames,row);
delifzero(m,"is_shared");
delifzero(m,"has_index");
delifzero(m,"has_primarykey");
delifzero(m,"default");
ret+=({m});
}
return ret;
}
- protected int oidformat(int oid) {
- switch(oid) {
- case BOOLOID:
- case BYTEAOID:
- case CHAROID:
- case INT8OID:
- case INT2OID:
- case INT4OID:
- case TEXTOID:
- case OIDOID:
- case XMLOID:
- case MACADDROID:
- case BPCHAROID:
- case VARCHAROID:
- case CTIDOID:
- case UUIDOID:
- return 1; //binary
- }
- return 0; // text
- }
-
- final protected void sendclose() {
- #ifdef PG_DEBUGMORE
- PD("Closetrace %O\n",backtrace());
- #endif
- object plugbuffer=c->start(1);
- foreach(qportals->peek_array();;object portal)
- portal->_closeportal(plugbuffer);
- plugbuffer->sendcmd(sendout);
- }
-
- protected void gottimeout() {
- sendclose();cancelquery();
- }
-
+
final protected string trbackendst(int c) {
switch(c) {
case 'I': return "idle";
case 'T': return "intransaction";
case 'E': return "infailedtransaction";
}
return "";
}
//! @returns
pike.git/lib/modules/Sql.pmod/pgsql.pike:1780:
//! @value infailedtransaction
//! @endstring
//!
//! @note
//! This function is PostgreSQL-specific, and thus it is not available
//! through the generic SQL-interface.
final string status_commit() {
return trbackendst(backendstatus);
}
- final protected void closestatement(object plugbuffer,string oldprep) {
- if(oldprep) {
- PD("Close statement %s\n",oldprep);
- plugbuffer->add_int8('C')->add_hstring(({'S',oldprep,0}),4,4);
+ 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) {
.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
pike.git/lib/modules/Sql.pmod/pgsql.pike:1910:
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
- || forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || cachealways[q])) {
+ || forcecache!=0
+ && (sizeof(q)>=MINPREPARELENGTH || .pgsql_util.cachealways[q])) {
object plugbuffer=c->start();
- if(tp=prepareds[q]) {
+ if(tp=_prepareds[q]) {
if(tp.preparedname) {
#ifdef PG_STATS
prepstmtused++;
#endif
preparedname=tp.preparedname;
} else if((tstart=tp.trun)
&& tp.tparse*FACTORPLAN>=tstart
&& (undefinedp(options.cache_autoprepared_statements)
|| options.cache_autoprepared_statements))
- preparedname=PREPSTMTPREFIX+(string)pstmtcount++;
+ preparedname=PREPSTMTPREFIX+int2hex(pstmtcount++);
} else {
if(totalhits>=cachedepth)
- foreach(prepareds;string ind;tp) {
+ 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);
+ m_delete(_prepareds,ind);
}
}
- if(forcecache!=1 && createprefix->match(q)) { // Flush cache on CREATE
- invalidatecache=1;
+ if(forcecache!=1 && .pgsql_util.createprefix->match(q)) {
+ invalidatecache=1; // Flush cache on CREATE
tp=UNDEFINED;
} else
- prepareds[q]=tp=([]);
+ _prepareds[q]=tp=([]);
}
if(invalidatecache) {
invalidatecache=0;
- foreach(prepareds;;mapping np) {
+ foreach(_prepareds;;mapping np) {
closestatement(plugbuffer,np.preparedname);
m_delete(np,"preparedname");
}
}
if(sizeof(plugbuffer)) {
PD("%O\n",(string)plugbuffer);
plugbuffer->sendcmd(flushsend); // close expireds
} else
plugbuffer->sendcmd(); // close start()
tstart=gethrtime();
} 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;
- qstate=inquery;
+
#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._unnamedportalkey=_unnamedportalmux->lock(1);
portal->_openportal();
_readyforquerycount++;
Thread.MutexKey lock=unnamedstatement->lock(1);
plugbuffer->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;
if(!sizeof(preparedname) || !tp || !tp.preparedname) {
if(!sizeof(preparedname))
preparedname=
(portal._unnamedstatementkey=unnamedstatement->trylock(1))
- ? "" : PTSTMTPREFIX+(string)ptstmtcount++;
+ ? "" : 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')
->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')
->add_hstring(({'S',preparedname,0}),4,4)->sendcmd(flushsend,portal);
} else {
if(parsebuffer)
parsebuffer->sendcmd();
#ifdef PG_STATS
skippeddescribe++;
#endif
- portal._datarowdesc=tp.datarowdesc;
+ portal->_setrowdesc(tp.datarowdesc);
}
portal._preparedname=preparedname;
if((portal._tprepared=tp) && tp.datatypeoid) {
- mixed e=catch(preparebind(portal));
+ mixed e=catch(portal->_preparebind());
if(e && !portal._delayederror) {
if(!stringp(e))
throw(e);
portal._delayederror=e;
}
}
}
throwdelayederror(portal);
return portal;
}
- protected void preparebind(object portal) {
- array dtoid=portal._tprepared.datatypeoid;
- array(string|int) paramValues=portal._params?portal._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(portal._portalname=
- (portal._unnamedportalkey=unnamedportalmux->trylock(1))
- ? "" : PORTALPREFIX+(string)_pportalcount++ )->add_int8(0)
- ->add(portal._preparedname)->add_int8(0)->add_int16(sizeof(paramValues));
- foreach(dtoid;;int textbin)
- plugbuffer->add_int16(oidformat(textbin));
- plugbuffer->add_int16(sizeof(paramValues));
- string cenc=_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:
- case XMLOID:
- case BPCHAROID:
- case VARCHAROID:;
- }
- plugbuffer->add_int32(k);
- } else
- switch(dtoid[i]) {
- case TEXTOID:
- case BPCHAROID:
- case VARCHAROID: {
- if(!value) {
- plugbuffer->add_int32(-1);
- break;
- }
- value=(string)value;
- switch(cenc) {
- case UTF8CHARSET:
- value=string_to_utf8(value);
- break;
- default:
- if(String.width(value)>8) {
- SUSERERROR("Don't know how to convert %O to %s encoding\n",
- value,cenc);
- value="";
- }
- }
- plugbuffer->add_hstring(value,4);
- break;
- }
- default: {
- if(!value) {
- plugbuffer->add_int32(-1);
- break;
- }
- value=(string)value;
- if(String.width(value)>8)
- if(dtoid[i]==BYTEAOID)
- value=string_to_utf8(value);
- else {
- SUSERERROR("Wide string %O not supported for type OID %d\n",
- value,dtoid[i]);
- value="";
- }
- plugbuffer->add_hstring(value,4);
- break;
- }
- case BOOLOID:plugbuffer->add_int32(1);
- do {
- int tval;
- if(stringp(value))
- tval=value[0];
- else if(!intp(value)) {
- value=!!value; // cast to boolean
- break;
- } else
- tval=value;
- switch(tval) {
- case 'o':case 'O':
- catch {
- tval=value[1];
- value=tval=='n'||tval=='N';
- };
- break;
- default:
- value=1;
- break;
- case 0:case '0':case 'f':case 'F':case 'n':case 'N':
- value=0;
- break;
- }
- } while(0);
- plugbuffer->add_int8(value);
- break;
- case CHAROID:
- if(intp(value))
- plugbuffer->add_hstring(value,4);
- else {
- value=(string)value;
- switch(sizeof(value)) {
- default:
- SUSERERROR(
- "\"char\" types must be 1 byte wide, got %O\n",value);
- case 0:
- plugbuffer->add_int32(-1); // NULL
- break;
- case 1:
- plugbuffer->add_hstring(value[0],4);
- }
- }
- break;
- case INT8OID:
- plugbuffer->add_int32(8)->add_int((int)value,8);
- 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;
- }
- }
- portal._plugbuffer=plugbuffer;
- if(portal._tprepared)
- if(portal._tprepared.datarowdesc)
- dodatarows(portal);
- else if(dontcacheprefix->match(portal._query)) // Don't cache FETCH/COPY
- m_delete(prepareds,portal._query),portal._tprepared=0;
- }
-
- protected void dodatarows(object portal) {
- object plugbuffer=portal._plugbuffer;
- portal._plugbuffer=0;
- {
- array a;
- plugbuffer->add_int16(sizeof(a=portal._datarowdesc));
- foreach(a;;mapping col)
- plugbuffer->add_int16(oidformat(col.type));
- }
- PD("Bind portal %O statement %O\n",portal._portalname,portal._preparedname);
- portal._fetchlimit=fetchlimit;
- portal->_openportal();
- object bindbuffer=c->start(1);
- portal._unnamedstatementkey=0;
- bindbuffer->add_int8('B')->add_hstring(plugbuffer,4,4);
- if(!portal._tprepared)
- closestatement(bindbuffer,portal._preparedname);
- portal->_sendexecute(fetchlimit
- && !(cachealways[portal._query]
- || sizeof(portal._query)>=MINPREPARELENGTH &&
- execfetchlimit->match(portal._query))
- && FETCHLIMITLONGRUN,bindbuffer);
- }
-
+
//! 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) {
return big_query(q,bindings);
}
//! This function returns an object that allows streaming and typed
//! results.
//!
//! @seealso
//! @[big_query()], @[Sql.Sql], @[Sql.sql_result]
object big_typed_query(string q,void|mapping(string|int:mixed) bindings) {
return big_query(q,bindings,1);
}