|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pike __REAL_VERSION__ |
|
#include "pgsql.h" |
|
#define ERROR(X ...) predef::error(X) |
|
protected int fetchlimit=FETCHLIMIT; |
protected Thread.Mutex unnamedportalmux,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=([]); |
protected int pstmtcount; |
protected int ptstmtcount; |
int _pportalcount; |
protected int totalhits; |
protected int cachedepth=STATEMENTCACHEDEPTH; |
protected int timeout=QUERYTIMEOUT; |
protected int portalbuffersize=PORTALBUFFERSIZE; |
protected int reconnected; |
protected int reconnectdelay; |
#ifdef PG_STATS |
protected int skippeddescribe; |
protected int portalsopened; |
#endif |
int _msgsreceived; |
int _bytesreceived; |
protected int warningsdropcount; |
protected int prepstmtused; |
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; |
|
#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" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void create(void|string host, void|string database, |
void|string user, void|string pass, |
void|mapping(string:mixed) options) { |
this::pass = pass; |
if(pass) { |
String.secure(pass); |
pass = "CENSORED"; |
} |
this::user = user; |
this::database = database; |
this::options = options || ([]); |
|
if(!host) host = PGSQL_DEFAULT_HOST; |
if(has_value(host,":") && sscanf(host,"%s:%d",host,_port)!=2) |
ERROR("Error in parsing the hostname argument\n"); |
this::_host = host; |
|
if(!_port) |
_port = PGSQL_DEFAULT_PORT; |
.pgsql_util.register_backend(); |
waitforauth=Thread.Mutex(); |
reconnect(); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string error(void|int clear) { |
throwdelayederror(this); |
string s=lastmessage*"\n"; |
if(clear) |
lastmessage=({}); |
warningscollected=0; |
return sizeof(s) && s; |
} |
|
|
|
|
|
|
string host_info() { |
return sprintf("fd:%d TCP/IP %s:%d PID %d", |
c?c->socket->query_fd():-1,_host,_port,backendpid); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
int is_open() { |
catch { |
return c->socket->query_fd()>=0; |
}; |
return 0; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ping() { |
return is_open() && !catch(c->start()->sendcmd(flushsend)) |
? !!reconnected : -1; |
} |
|
final protected object getsocket(void|int nossl) { |
return .pgsql_util.PGassist(this,qportals,(int)nossl); |
} |
|
|
|
|
|
|
|
|
|
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(); |
} |
|
|
|
|
|
|
|
|
|
|
void set_charset(string charset) { |
big_query(sprintf("SET CLIENT_ENCODING TO '%s'",quote(charset))); |
} |
|
|
|
|
|
|
|
string get_charset() { |
return _runtimeparameter[CLIENT_ENCODING]; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mapping(string:string) getruntimeparameters() { |
return _runtimeparameter+([]); |
} |
|
|
|
|
|
|
|
|
#ifdef PG_STATS |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#ifdef PG_STATS |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mapping(string:mixed) getstatistics() { |
mapping(string:mixed) stats=([ |
"warnings_dropped":warningsdropcount, |
"used_prepared_statements":prepstmtused, |
"current_prepared_statements":sizeof(prepareds), |
"current_prepared_statement_hits":totalhits, |
"prepared_statement_count":pstmtcount, |
#ifdef PG_STATS |
"skipped_describe_count":skippeddescribe, |
"portals_opened_count":portalsopened, |
#endif |
"messages_received":_msgsreceived, |
"bytes_received":_bytesreceived, |
"reconnect_count":reconnected, |
"portals_in_flight":_portalsinflight, |
]); |
return stats; |
} |
|
|
|
|
|
|
|
|
|
|
int setcachedepth(void|int newdepth) { |
int olddepth=cachedepth; |
if(!undefinedp(newdepth) && newdepth>=0) |
cachedepth=newdepth; |
return olddepth; |
} |
|
|
|
|
|
|
|
|
|
|
int settimeout(void|int newtimeout) { |
int oldtimeout=timeout; |
if(!undefinedp(newtimeout) && newtimeout>0) { |
timeout=newtimeout; |
if(c) |
c->timeout=timeout; |
} |
return oldtimeout; |
} |
|
|
|
|
|
|
|
|
|
|
int setportalbuffersize(void|int newportalbuffersize) { |
int oldportalbuffersize=portalbuffersize; |
if(!undefinedp(newportalbuffersize) && newportalbuffersize>0) |
portalbuffersize=newportalbuffersize; |
return oldportalbuffersize; |
} |
|
|
|
|
|
|
|
|
|
|
int setfetchlimit(void|int newfetchlimit) { |
int oldfetchlimit=fetchlimit; |
if(!undefinedp(newfetchlimit) && newfetchlimit>=0) |
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) { |
return msg*"\n"+"\n"; |
} |
|
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() { |
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 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; |
int i; |
string fmt=sprintf("%%%ds %%3s %%.61s",max(@map(from,sizeof))); |
foreach(paramValues;i;val) |
msgs+=({sprintf(fmt,from[i],to[i],sprintf("%O",val))}); |
} |
} |
return msgs; |
} |
|
protected void preplastmessage(mapping(string:string) msgresponse) { |
lastmessage=({ |
sprintf("%s %s:%s %s\n (%s:%s:%s)", |
msgresponse.S,msgresponse.C,msgresponse.P||"", |
msgresponse.M,msgresponse.F||"",msgresponse.R||"", |
msgresponse.L||"")}); |
} |
|
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; |
.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 |
-(<"use_ssl","force_ssl","cache_autoprepared_statements","reconnect", |
"text_query","is_superuser","server_encoding","server_version", |
"integer_datetimes","session_authorization">); |
string name;mixed value) |
plugbuffer->add(name)->add_int8(0)->add((string)value)->add_int8(0); |
plugbuffer->add_int8(0); |
PD("%O\n",(string)plugbuffer); |
ci->start()->add_hstring(plugbuffer,4,4)->sendcmd(flushsend); |
} |
cancelsecret=0; |
PD("Processloop\n"); |
#ifdef PG_DEBUG |
string datarowdebug; |
int datarowdebugcount; |
#endif |
for(;;) { |
err=catch { |
#ifdef PG_DEBUG |
if(!portal && datarowdebug) { |
PD("%s rows %d\n",datarowdebug,datarowdebugcount); |
datarowdebug=0; datarowdebugcount=0; |
} |
#endif |
int msgtype=ci->read_int8(); |
if(!portal) { |
portal=qportals->try_read(); |
if(portal) |
PD("<%O %d %c switch portal ===================\n", |
portal._portalname,++ci->queueinidx,msgtype); |
} |
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) { |
array(mapping) getcols() { |
int bintext=ci->read_int8(); |
int cols=ci->read_int16(); |
#ifdef PG_DEBUG |
array a; |
msglen-=4+1+2+2*cols; |
foreach(a=allocate(cols,([]));;mapping m) |
m.type=ci->read_int16(); |
#else |
ci->consume(cols<<1); |
#endif // Discard column info, and make it line oriented |
return ({(["type":bintext?BYTEAOID:TEXTOID,"name":"line"])}); |
}; |
array(string) reads() { |
#ifdef PG_DEBUG |
if(msglen<1) |
errtype=protocolerror; |
#endif |
array ret=({}),aw=({0}); |
do { |
string w=ci->read_cstring(); |
msglen-=sizeof(w)+1; aw[0]=w; ret+=aw; |
} while(msglen); |
return ret; |
}; |
mapping(string:string) getresponse() { |
mapping(string:string) msgresponse=([]); |
msglen-=4; |
foreach(reads();;string f) |
if(sizeof(f)) |
msgresponse[f[..0]]=f[1..]; |
PD("%O\n",msgresponse); |
return msgresponse; |
}; |
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->timeout=timeout; |
reconnectdelay=0; |
cancelsecret=""; |
break; |
case 2: |
PD("KerberosV5\n"); |
errtype=protocolunsupported; |
break; |
case 3: |
PD("ClearTextPassword\n"); |
sendpass=pass; |
break; |
case 4: |
PD("CryptPassword\n"); |
errtype=protocolunsupported; |
break; |
case 5: |
PD("MD5Password\n"); |
#ifdef PG_DEBUG |
if(msglen<4) |
errtype=protocolerror; |
#endif |
#define md5hex(x) String.string2hex(Crypto.MD5.hash(x)) |
sendpass=md5hex(pass+user); |
sendpass="md5"+md5hex(sendpass+ci->read(msglen)); |
#ifdef PG_DEBUG |
msglen=0; |
#endif |
break; |
case 6: |
PD("SCMCredential\n"); |
errtype=protocolunsupported; |
break; |
case 7: |
PD("GSS\n"); |
errtype=protocolunsupported; |
break; |
case 9: |
PD("SSPI\n"); |
errtype=protocolunsupported; |
break; |
case 8: |
PD("GSSContinue\n"); |
errtype=protocolunsupported; |
#ifdef PG_DEBUG |
if(msglen<1) |
errtype=protocolerror; |
#endif |
cancelsecret=ci->read(msglen); |
#ifdef PG_DEBUG |
msglen=0; |
#endif |
break; |
default: |
PD("Unknown Authentication Method %c\n",authtype); |
errtype=protocolunsupported; |
break; |
} |
switch(errtype) { |
case noerror: |
if(cancelsecret!="") |
ci->start()->add_int8('p')->add_hstring(sendpass,4,5) |
->add_int8(0)->sendcmd(flushsend); |
break; |
default: |
case protocolunsupported: |
ERROR("Unsupported authenticationmethod %c\n",authtype); |
break; |
} |
break; |
} |
case 'K': |
msglen-=4+4;backendpid=ci->read_int32(); |
cancelsecret=ci->read(msglen); |
PD("<BackendKeyData %O\n",cancelsecret); |
#ifdef PG_DEBUG |
msglen=0; |
#endif |
break; |
case 'S': { |
PD("<ParameterStatus "); |
msglen-=4; |
array(string) ts=reads(); |
#ifdef PG_DEBUG |
if(sizeof(ts)==2) { |
#endif |
_runtimeparameter[ts[0]]=ts[1]; |
PD("%O=%O\n",ts[0],ts[1]); |
#ifdef PG_DEBUG |
} else |
errtype=protocolerror; |
#endif |
break; |
} |
case '3': |
PD("<CloseComplete\n"); |
#ifdef PG_DEBUG |
msglen-=4; |
#endif |
break; |
case 'Z': |
#ifdef PG_DEBUG |
msglen-=4+1; |
#endif |
backendstatus=ci->read_int8(); |
PD("<ReadyForQuery %c\n",backendstatus); |
if(readyforquery_cb) |
readyforquery_cb(),readyforquery_cb=0; |
qstate=queryidle; |
if(waitforauthready) { |
Thread.MutexKey lock=waitforauth->lock(); |
waitforauthready->broadcast(); |
waitforauthready=0; |
lock=0; |
} |
break; |
case '1': |
PD("<ParseComplete\n"); |
#ifdef PG_DEBUG |
msglen-=4; |
#endif |
break; |
case 't': { |
array a; |
int cols=ci->read_int16(); |
PD("<%O ParameterDescription %d values\n",portal._query,cols); |
#ifdef PG_DEBUG |
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); |
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 |
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(); |
#ifdef PG_DEBUG |
{ |
int len=ci->read_sint(2); |
res.length=len>=0?len:"variable"; |
} |
res.atttypmod=ci->read_int32(); |
|
|
|
|
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=0; |
break; |
} |
case 'n': { |
#ifdef PG_DEBUG |
msglen-=4; |
#endif |
PD("<NoData %O\n",portal._query); |
portal._datarowdesc=({}); |
portal._fetchlimit=0; |
processrowdescription(portal); |
portal=0; |
break; |
} |
case 'H': |
portal._datarowdesc=getcols(); |
PD("<CopyOutResponse %d %O\n", |
sizeof(portal._datarowdesc),portal._query); |
processrowdescription(portal); |
break; |
case '2': { |
mapping tp; |
#ifdef PG_DEBUG |
msglen-=4; |
#endif |
PD("<%O BindComplete\n",portal._portalname); |
if(tp=portal._tprepared) { |
int tend=gethrtime(); |
int tstart=tp.trun; |
if(tend==tstart) |
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; |
} |
tp.trunstart=tend; |
} |
} |
break; |
} |
case 'D': { |
msglen-=4; |
string serror; |
if(portal._tprepared) |
storetiming(portal); |
#ifdef USEPGsql |
ci->decodedatarow(msglen);msglen=0; |
#else |
array a, datarowdesc; |
portal._bytesreceived+=msglen; |
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; |
int forcetext = portal._forcetext; |
string cenc=_runtimeparameter[CLIENT_ENCODING]; |
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: |
#endif |
if(!atext) { |
value=(float)ci->read(collen); |
break; |
} |
default:value=ci->read(collen); |
break; |
case CHAROID: |
value=atext?ci->read(1):ci->read_int8(); |
break; |
case BOOLOID:value=ci->read_int8(); |
switch(value) { |
case 'f':value=0; |
break; |
case 't':value=1; |
} |
if(atext) |
value=value?"t":"f"; |
break; |
case TEXTOID: |
case BPCHAROID: |
case VARCHAROID: |
value=ci->read(collen); |
if(cenc==UTF8CHARSET && catch(value=utf8_to_string(value)) |
&& !serror) |
serror=SERROR("%O contains non-%s characters\n", |
value,UTF8CHARSET); |
break; |
case INT8OID:case INT2OID: |
case OIDOID:case INT4OID: |
if(forcetext) { |
value=ci->read(collen); |
if(!atext) |
value=(int)value; |
} else { |
switch(typ) { |
case INT8OID:value=ci->read_sint(8); |
break; |
case INT2OID:value=ci->read_sint(2); |
break; |
case OIDOID: |
case INT4OID:value=ci->read_sint(4); |
} |
if(atext) |
value=(string)value; |
} |
} |
a[i]=value; |
} else if(!collen) |
a[i]=""; |
} |
portal._inflight--; |
portal._datarows->write(a); |
if(serror) |
ERROR(serror); |
#endif // USEPGsql |
portal->_processdataready(fetchlimit); |
break; |
} |
case 's': |
PD("<%O PortalSuspended\n",portal._portalname); |
#if !STREAMEXECUTES |
portal->_sendexecute(portal._fetchlimit); |
#endif |
#ifdef PG_DEBUG |
msglen-=4; |
#endif |
portal=0; |
break; |
case 'C': { |
msglen-=4; |
#ifdef PG_DEBUG |
if(msglen<1) |
errtype=protocolerror; |
#endif |
string s=ci->read(msglen-1); |
if(portal._tprepared) |
storetiming(portal); |
PD("<%O CommandComplete %O\n",portal._portalname,s); |
if(!portal._statuscmdcomplete) |
portal._statuscmdcomplete=s; |
#ifdef PG_DEBUG |
if(ci->read_int8()) |
errtype=protocolerror; |
msglen=0; |
#else |
ci->consume(1); |
#endif |
portal->_releasesession(); |
portal=0; |
break; |
} |
case 'I': |
PD("<EmptyQueryResponse %O\n",portal._portalname); |
#ifdef PG_DEBUG |
msglen-=4; |
#endif |
portal->_releasesession(); |
portal=0; |
break; |
case 'd': |
PD("<%O CopyData\n",portal._portalname); |
if(portal._tprepared) |
storetiming(portal); |
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); |
break; |
case 'G': |
portal._datarowdesc=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': |
#ifdef PG_DEBUG |
PD("<%O CopyDone\n",portal._portalname); |
msglen-=4; |
#endif |
portal=0; |
break; |
case 'E': { |
if(portal) |
portal->_releasesession(); |
PD("<%O ErrorResponse %O\n", |
portal&&portal._portalname,portal&&portal._query); |
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; |
preplastmessage(msgresponse); |
PD(a2nls(lastmessage));USERERROR(a2nls(lastmessage)); |
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}); |
lastmessage+=({ |
pinpointerror(portal&&portal._query,msgresponse.P)+ |
pinpointerror(msgresponse.q,msgresponse.p)}); |
if(msgresponse.W) |
lastmessage+=({msgresponse.W}); |
lastmessage+=showbindings(portal); |
switch(msgresponse.S) { |
case "PANIC":werror(a2nls(lastmessage)); |
} |
USERERROR(a2nls(lastmessage)); |
} |
break; |
} |
case 'N': { |
PD("<NoticeResponse\n"); |
mapping(string:string) msgresponse; |
msgresponse=getresponse(); |
if(clearmessage) { |
warningsdropcount+=warningscollected; |
clearmessage=warningscollected=0; |
lastmessage=({}); |
} |
warningscollected++; |
lastmessage=({sprintf("%s %s: %s", |
msgresponse.S,msgresponse.C,msgresponse.M)}); |
break; |
} |
case 'A': { |
PD("<NotificationResponse\n"); |
msglen-=4+4; |
int pid=ci->read_int32(); |
string condition,extrainfo=UNDEFINED; |
{ |
array(string) ts=reads(); |
switch(sizeof(ts)) { |
#if PG_DEBUG |
case 0: |
errtype=protocolerror; |
break; |
default: |
errtype=protocolerror; |
#endif |
case 2: |
extrainfo=ts[1]; |
case 1: |
condition=ts[0]; |
} |
} |
PD("%d %s\n%s\n",pid,condition,extrainfo); |
runcallback(pid,condition,extrainfo); |
break; |
} |
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)}); |
USERERROR(a2nls(lastmessage)); |
} |
break; |
} |
#ifdef PG_DEBUG |
if(msglen) |
errtype=protocolerror; |
#endif |
{ |
string msg; |
switch(errtype) { |
case protocolunsupported: |
msg=sprintf("Unsupported servermessage received %c\n",msgtype); |
break; |
case protocolerror: |
msg=sprintf("Protocol error with database %s",host_info()); |
break; |
case noerror: |
continue; |
} |
ERROR(a2nls(lastmessage+=({msg}))); |
} |
}; |
if(err==MAGICTERMINATE) { |
ci->start()->add("X\0\0\0\4")->sendcmd(sendout); |
terminating=1; |
if(!sizeof(ci)) |
break; |
} |
if(stringp(err)) { |
object to=portal?portal:this; |
if(!to._delayederror) |
to._delayederror=err; |
continue; |
} |
break; |
} |
_delayederror=err; |
if(!ci->close() && !terminating && options.reconnect) |
_connectfail(); |
} |
|
|
|
|
|
|
|
void close() { |
cancelquery(); |
if(c) |
c->sendterminate(); |
c=0; |
} |
|
void destroy() { |
close(); |
.pgsql_util.unregister_backend(); |
} |
|
void _connectfail(void|mixed err) { |
if(err) |
_delayederror=err; |
if(!err || reconnectdelay) { |
int tdelay; |
switch(tdelay=reconnectdelay) { |
case 0: |
reconnectdelay=RECONNECTDELAY; |
break; |
default: |
if(options.reconnect!=-1) |
return; |
reconnectdelay=RECONNECTBACKOFF; |
break; |
} |
Thread.MutexKey lock=waitforauth->lock(); |
if(!waitforauthready) |
waitforauthready=Thread.Condition(); |
lock=0; |
.pgsql_util.local_backend->call_out(reconnect,tdelay,1); |
} |
} |
|
protected int reconnect(void|int force,void|object tt) { |
if(!force) { |
Thread.MutexKey lock=waitforauth->lock(); |
if(waitforauthready) { |
lock=0; |
return 0; |
} |
waitforauthready=Thread.Condition(); |
lock=0; |
} |
if(c) { |
reconnected++; |
prepstmtused=0; |
if(!force) |
c->sendterminate(); |
else |
c->close(); |
c=0; |
foreach(prepareds;;mapping tp) |
m_delete(tp,"preparedname"); |
if(!options.reconnect) |
return 0; |
} |
qportals=Thread.Queue(); |
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(); |
unnamedstatement=Thread.Mutex(); |
qstate=force?reconnectforce:streconnect; |
readyforquery_cb=reconnect_cb; |
_portalsinflight=0; |
return 1; |
} |
|
|
|
|
|
|
|
void reload() { |
resync(); |
} |
|
protected void resync_cb() { |
switch(backendstatus) { |
case 'T':case 'E': |
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"); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void resync(void|int|object portal) { |
mixed err; |
if(!is_open()&&!reconnect()) |
ERROR(a2nls(lastmessage)); |
err = catch { |
PD("Portalsinflight: %d\n",_portalsinflight); |
readyforquery_cb=resync_cb; |
c->start()->add(PGSYNC)->sendcmd(sendout); |
return; |
}; |
PD("%O\n",err); |
if(!reconnect()) |
ERROR(a2nls(lastmessage)); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
void select_db(string dbname) { |
database=dbname; |
reconnect(); |
reconnected=0; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_notify_callback(string condition, |
void|function(int,string,string,mixed ...:void) notify_cb,void|int selfnotify, |
mixed ... args) { |
if(!notify_cb) |
m_delete(notifylist,condition); |
else { |
array old=notifylist[condition]; |
if(!old) |
old=({notify_cb}); |
if(selfnotify||args) |
old+=({selfnotify}); |
if(args) |
old+=args; |
notifylist[condition]=old; |
} |
} |
|
final protected void runcallback(int pid,string condition,string extrainfo) { |
array cb; |
if((cb=notifylist[condition]||notifylist[""]) |
&& (pid!=backendpid || sizeof(cb)>1 && cb[1])) |
cb[0](pid,condition,extrainfo,@cb[2..]); |
} |
|
|
|
|
|
|
|
|
|
|
|
string quote(string s) { |
string r=_runtimeparameter.standard_conforming_strings; |
if(r && r=="on") |
return replace(s, "'", "''"); |
return replace(s, ({ "'", "\\" }), ({ "''", "\\\\" }) ); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string quotebinary(string s) { |
return replace(s, ({ "'", "\\", "\0" }), ({ "''", "\\\\", "\\000" }) ); |
} |
|
|
|
|
|
|
|
|
|
void create_db(string db) { |
big_query(sprintf("CREATE DATABASE %s",db)); |
} |
|
|
|
|
|
|
|
|
|
|
|
void drop_db(string db) { |
big_query(sprintf("DROP DATABASE %s",db)); |
} |
|
|
|
|
|
|
|
|
|
string server_info () { |
return DRIVERNAME"/"+(_runtimeparameter.server_version||"unknown"); |
} |
|
|
|
|
|
|
array(string) list_dbs (void|string glob) { |
array row,ret=({}); |
object res=big_query("SELECT d.datname " |
"FROM pg_database d " |
"WHERE d.datname ILIKE :glob " |
"ORDER BY d.datname", |
([":glob":glob2reg(glob)])); |
while(row=res->fetch_row()) |
ret+=({row[0]}); |
return ret; |
} |
|
|
|
|
|
|
|
array(string) list_tables (void|string glob) { |
array row,ret=({}); |
object res=big_query( |
"SELECT CASE WHEN 'public'=n.nspname THEN '' ELSE n.nspname||'.' END " |
" ||c.relname AS name " |
"FROM pg_catalog.pg_class c " |
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace " |
"WHERE c.relkind IN ('r','v') AND n.nspname<>'pg_catalog' " |
" AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) " |
" AND c.relname ILIKE :glob " |
" ORDER BY 1", |
([":glob":glob2reg(glob)])); |
while(row=res->fetch_row()) |
ret+=({row[0]}); |
return ret; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(mapping(string:mixed)) list_fields(void|string table, void|string glob) { |
array row, ret=({}); |
string schema=UNDEFINED; |
|
sscanf(table||"*", "%s.%s", schema, table); |
|
object res = big_typed_query( |
"SELECT a.attname, a.atttypid, t.typname, a.attlen, " |
" c.relhasindex, c.relhaspkey, CAST(c.reltuples AS BIGINT) AS reltuples, " |
" (c.relpages " |
" +COALESCE( " |
" (SELECT SUM(tst.relpages) " |
" FROM pg_catalog.pg_class tst " |
" WHERE tst.relfilenode=c.reltoastrelid) " |
" ,0) " |
" )*8192::BIGINT AS datasize, " |
" (COALESCE( " |
" (SELECT SUM(pin.relpages) " |
" FROM pg_catalog.pg_index pi " |
" JOIN pg_catalog.pg_class pin ON pin.relfilenode=pi.indexrelid " |
" WHERE pi.indrelid IN (c.relfilenode,c.reltoastrelid)) " |
" ,0) " |
" )*8192::BIGINT AS indexsize, " |
" c.relisshared, t.typdefault, " |
" n.nspname, c.relname, " |
" CASE c.relkind " |
" WHEN 'r' THEN 'table' " |
" WHEN 'v' THEN 'view' " |
" WHEN 'i' THEN 'index' " |
" WHEN 'S' THEN 'sequence' " |
" WHEN 's' THEN 'special' " |
" WHEN 't' THEN 'toastable' " |
" WHEN 'c' THEN 'composite' " |
" ELSE c.relkind::TEXT END AS relkind, " |
" r.rolname " |
"FROM pg_catalog.pg_class c " |
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace " |
" JOIN pg_catalog.pg_roles r ON r.oid=c.relowner " |
" JOIN pg_catalog.pg_attribute a ON c.oid=a.attrelid " |
" JOIN pg_catalog.pg_type t ON a.atttypid=t.oid " |
"WHERE c.relname ILIKE :table AND " |
" (n.nspname ILIKE :schema OR " |
" :schema IS NULL " |
" AND n.nspname<>'pg_catalog' AND n.nspname !~ '^pg_toast') " |
" AND a.attname ILIKE :glob " |
" AND (a.attnum>0 OR '*'=:realglob) " |
"ORDER BY n.nspname,c.relname,a.attnum,a.attname", |
([":schema":glob2reg(schema),":table":glob2reg(table), |
":glob":glob2reg(glob),":realglob":glob])); |
|
array colnames=res->fetch_fields(); |
{ |
mapping(string:string) renames=([ |
"attname":"name", |
"nspname":"schema", |
"relname":"table", |
"rolname":"owner", |
"typname":"type", |
"attlen":"length", |
"typdefault":"default", |
"relisshared":"is_shared", |
"atttypid":"typeoid", |
"relkind":"kind", |
"relhasindex":"has_index", |
"relhaspkey":"has_primarykey", |
"reltuples":"rowcount", |
]); |
foreach(colnames;int i;mapping m) { |
string nf,field=m.name; |
if(nf=renames[field]) |
field=nf; |
colnames[i]=field; |
} |
} |
|
#define delifzero(m,field) if(!(m)[field]) m_delete(m,field) |
|
while(row=res->fetch_row()) { |
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; |
} |
return 0; |
} |
|
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 ""; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
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 void throwdelayederror(object parent) { |
.pgsql_util.throwdelayederror(parent); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
object big_query(string q,void|mapping(string|int:mixed) bindings, |
void|int _alltyped) { |
throwdelayederror(this); |
string preparedname=""; |
int forcecache=-1; |
int forcetext=options.text_query; |
string cenc=_runtimeparameter[CLIENT_ENCODING]; |
switch(cenc) { |
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) { |
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)) { |
if(name[0]!=':') |
name=":"+name; |
if(name[1]=='_') { |
switch(name) { |
case ":_cache": |
forcecache=(int)value; |
break; |
case ":_text": |
forcetext=(int)value; |
break; |
} |
continue; |
} |
if(!has_value(q,name)) |
continue; |
} |
from[rep]=name; |
string rval; |
if(multisetp(value)) |
rval=indices(value)*","; |
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 = ({}); |
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 |
|| forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || cachealways[q])) { |
object plugbuffer=c->start(); |
if(tp=prepareds[q]) { |
if(tp.preparedname) |
prepstmtused++, 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++; |
} else { |
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 && createprefix->match(q)) { |
invalidatecache=1; |
tp=UNDEFINED; |
} else |
prepareds[q]=tp=([]); |
} |
if(invalidatecache) { |
invalidatecache=0; |
foreach(prepareds;;mapping np) { |
closestatement(plugbuffer,np.preparedname); |
m_delete(np,"preparedname"); |
} |
} |
if(sizeof(plugbuffer)) { |
PD("%O\n",(string)plugbuffer); |
plugbuffer->sendcmd(flushsend); |
} else |
plugbuffer->sendcmd(); |
tstart=gethrtime(); |
} else |
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) { |
portal._unnamedportalkey=unnamedportalmux->lock(1); |
portal->_openportal(); |
plugbuffer->start()->add_int8('Q')->add_hstring(q,4,4+1)->add_int8(0) |
->sendcmd(flushsend,portal); |
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++; |
|
|
|
|
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._preparedname=preparedname; |
if((portal._tprepared=tp) && tp.datatypeoid) { |
mixed e=catch(preparebind(portal)); |
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); |
else if(stringp(value) && !sizeof(value)) { |
int k=0; |
switch(dtoid[i]) { |
default: |
k=-1; |
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; |
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); |
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)) |
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); |
} |
|
|
|
|
|
|
object streaming_query(string q,void|mapping(string|int:mixed) bindings) { |
return big_query(q,bindings); |
} |
|
|
|
|
|
|
object big_typed_query(string q,void|mapping(string|int:mixed) bindings) { |
return big_query(q,bindings,1); |
} |
|
|