pike.git
/
lib
/
modules
/
Sql.pmod
/
pgsql.pike
version
»
Context lines:
10
20
40
80
file
none
3
pike.git/lib/modules/Sql.pmod/pgsql.pike:12:
//! and @[Postgres.postgres] modules. //! //! This module implements the PostgreSQL network protocol version 3. //! Refer to the PostgreSQL documentation for further details. //! //! @seealso //! @[Sql.Sql], @[Sql.postgres] #pike __REAL_VERSION__
-
//
#
define
DEBUG 1
-
//#define DEBUGMORE 1
+
#
include
"pgsql.h"
-
#ifdef DEBUG
-
#define PD(X ...) werror(X)
-
#define UNBUFFEREDIO 1 // Make all IO unbuffered
-
#else
-
#undef DEBUGMORE
-
#define PD(X ...)
-
#endif
-
//#define NO_LOCKING 1 // This breaks the driver, do not enable,
-
// only for benchmarking mutex performance
-
#define USEPGsql 1 // Doesn't use Stdio.FILE, but _PGsql
-
-
#ifdef USEPGsql
-
#define UNBUFFEREDIO 1
-
#endif
-
-
#define FETCHLIMIT 1024 // Initial upper limit on the
-
// number of rows to fetch across the
-
// network at a time
-
// 0 for no chunking
-
// Needs to be >0 for interleaved
-
// portals
-
#define FETCHLIMITLONGRUN 1 // for long running background queries
-
#define STREAMEXECUTES 1 // streams executes if defined
-
#define MINPREPARELENGTH 16 // statements shorter than this will not
-
// be cached
-
#define PGSQL_DEFAULT_PORT 5432
-
#define PGSQL_DEFAULT_HOST "localhost"
-
#define PREPSTMTPREFIX "pike_prep_"
-
#define PORTALPREFIX "pike_portal_"
-
#define FACTORPLAN 8
-
#define DRIVERNAME "pgsql"
-
#define MARKSTART "{""{""{""{\n" // split string to avoid
-
#define MARKERROR ">>>>" // foldeditors from recognising
-
#define MARKEND "\n}""}""}""}" // it as a fold
-
+
#define ERROR(X ...) predef::error(X) int _nextportal; int _closesent; int _fetchlimit=FETCHLIMIT; private int unnamedportalinuse; private int portalsinflight; object _c; private string SSauthdata,cancelsecret; private int backendpid; private int backendstatus; private mapping(string:mixed) options; private string lastmessage; private mapping(string:array(mixed)) notifylist=([]); private mapping(string:string) msgresponse; private mapping(string:string) runtimeparameter;
-
private enum
state
{unauthenticated,authenticated,readyforquery,
-
parsecomplete,bindcomplete,commandcomplete,gotrowdescription,
-
gotparameterdescription,dataready,dataprocessed,portalsuspended,
-
copyinresponse};
-
private state
mstate;
+
state
_
mstate;
private enum querystate {queryidle,inquery,cancelpending,canceled}; private querystate qstate; private mapping(string:mapping(string:mixed)) prepareds=([]); private int pstmtcount; private int pportalcount; private int totalhits;
-
private int cachedepth=
1024
;
// Maximum cachecountsum for prepared statements,
-
// may be tuned by the application
-
private int timeout=
4096
;
// Queries running longer than this number of
-
// seconds are canceled automatically
-
private int portalbuffersize=
32*1024
;
// Approximate buffer per portal
+
private int cachedepth=
STATEMENTCACHEDEPTH
;
+
private int timeout=
QUERYTIMEOUT
;
+
private int portalbuffersize=
PORTALBUFFERSIZE
;
private int reconnected; // Number of times the connection was reset private string host, database, user, pass; private int port; private mapping(string:string) sessiondefaults=([]); // runtime parameters
-
private
Thread.Mutex querymutex;
-
private
Thread.Mutex stealmutex;
+
Thread.Mutex
_
querymutex;
+
Thread.Mutex
_
stealmutex;
protected string _sprintf(int type, void|mapping flags) { string res=UNDEFINED; switch(type) { case 'O': res=sprintf(DRIVERNAME"://%s@%s:%d/%s pid:%d %s reconnected:%d\n" "mstate: %O qstate: %O pstmtcount: %d pportalcount: %d\n" "Last query: %O\n" "Last message: %s\n" "Last error: %O\n" "portal %d %O\n%O\n", user,host,port,database,backendpid,status_commit(),reconnected,
-
mstate,qstate,pstmtcount,pportalcount,
+
_
mstate,qstate,pstmtcount,pportalcount,
_c.portal&&_c.portal->query||"", lastmessage||"", msgresponse, !!_c.portal,runtimeparameter,prepareds); break; } return res; } #define BOOLOID 16
pike.git/lib/modules/Sql.pmod/pgsql.pike:130:
#define XMLOID 142 #define FLOAT4OID 700 #define MACADDROID 829 #define INETOID 869 #define BPCHAROID 1042 #define VARCHAROID 1043 #define CTIDOID 1247 #define UUIDOID 2950 #define PG_PROTOCOL(m,n) (((m)<<16)|(n))
-
#define FLUSH "H\0\0\0\4"
+
//! @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 initializes (reinitializes if a //! connection had been previously set up) 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
pike.git/lib/modules/Sql.pmod/pgsql.pike:174:
//! @[Postgres.postgres], @[Sql.Sql], @[postgres->select_db] protected void create(void|string _host, void|string _database, void|string _user, void|string _pass, void|mapping(string:mixed) _options) { pass = _pass; _pass = "CENSORED"; user = _user; database = _database; host = _host || PGSQL_DEFAULT_HOST; options = _options || ([]); if(search(host,":")>=0 && sscanf(_host,"%s:%d",host,port)!=2) ERROR("Error in parsing the hostname argument\n"); if(!port) port = PGSQL_DEFAULT_PORT;
-
querymutex=Thread.Mutex();
-
stealmutex=Thread.Mutex();
+
_
querymutex=Thread.Mutex();
+
_
stealmutex=Thread.Mutex();
reconnect(); } //! @decl string error() //! //! This function returns the textual description of the last //! server-related error. Returns @expr{0@} if no error has occurred //! yet. It is not cleared upon reading (can be invoked multiple //! times, will return the same result until a new error occurs). //!
pike.git/lib/modules/Sql.pmod/pgsql.pike:206:
//! @decl string host_info() //! //! This function returns a string describing what host are we talking to, //! and how (TCP/IP or UNIX sockets). string host_info() { return sprintf("Via fd:%d over TCP/IP to %s:%d",_c.query_fd(),host,port); }
-
class PGassist {
-
-
int(-1..1) peek(int timeout) {
-
}
-
-
string read(int len,void|int(0..1) not_all) {
-
}
-
-
int write(string|array(string) data) {
-
}
-
-
int getchar() {
-
}
-
-
int close() {
-
}
-
-
#ifdef USEPGsql
-
inherit _PGsql.PGsql;
-
#else
-
object portal;
-
-
void setportal(void|object newportal) {
-
portal=newportal;
-
}
-
-
inline int(-1..1) bpeek(int timeout) {
-
return peek(timeout);
-
}
-
-
int flushed;
-
#endif
-
-
void create(void|object pgsqlsess) {
-
#ifdef USEPGsql
-
if(pgsqlsess)
-
::create(pgsqlsess);
-
#else
-
flushed=-1;
-
#endif
-
}
-
-
#ifndef USEPGsql
-
inline final int getbyte() {
-
if(!flushed && !bpeek(0))
-
sendflush();
-
return getchar();
-
}
-
-
final string getstring(void|int len) {
-
if(!zero_type(len)) {
-
string acc="",res;
-
do {
-
if(!flushed && !bpeek(0))
-
sendflush();
-
res=read(len,!flushed);
-
if(res) {
-
if(!sizeof(res))
-
return acc;
-
acc+=res;
-
}
-
}
-
while(sizeof(acc)<len&&res);
-
return sizeof(acc)?acc:res;
-
}
-
array(int) acc=({});
-
int c;
-
while((c=getbyte())>0)
-
acc+=({c});
-
return `+("",@map(acc,String.int2char));
-
}
-
-
inline final int getint16() {
-
int s0=getbyte();
-
int r=(s0&0x7f)<<8|getbyte();
-
return s0&0x80 ? r-(1<<15) : r ;
-
}
-
-
inline final int getint32() {
-
int r=getint16();
-
r=r<<8|getbyte();
-
return r<<8|getbyte();
-
}
-
-
inline final int getint64() {
-
int r=getint32();
-
return r<<32|getint32()&0xffffffff;
-
}
-
#endif
-
-
inline final string plugbyte(int x) {
-
return String.int2char(x);
-
}
-
-
inline final string plugint16(int x) {
-
return sprintf("%c%c",x>>8&255,x&255);
-
}
-
-
inline final string plugint32(int x) {
-
return sprintf("%c%c%c%c",x>>24&255,x>>16&255,x>>8&255,x&255);
-
}
-
-
inline final string plugint64(int x) {
-
return sprintf("%c%c%c%c%c%c%c%c",x>>56&255,x>>48&255,x>>40&255,x>>32&255,
-
x>>24&255,x>>16&255,x>>8&255,x&255);
-
}
-
-
final void sendflush() {
-
sendcmd(({}),1);
-
}
-
-
final int sendcmd(string|array(string) data,void|int flush) {
-
if(flush) {
-
if(stringp(data))
-
data=({data,FLUSH});
-
else
-
data+=({FLUSH});
-
PD("Flush\n");
-
flushed=1;
-
}
-
else if(flushed!=-1)
-
flushed=0;
-
return write(data);
-
}
-
-
final void sendterminate() {
-
PD("Terminate\n");
-
sendcmd(({"X",plugint32(4)}));
-
close();
-
}
-
}
-
-
class PGconn {
-
-
inherit PGassist:pg;
-
#ifdef UNBUFFEREDIO
-
inherit Stdio.File:std;
-
-
inline int getchar() {
-
return std::read(1)[0];
-
}
-
#else
-
inherit Stdio.FILE:std;
-
-
inline int getchar() {
-
return std::getchar();
-
}
-
#endif
-
-
inline int(-1..1) peek(int timeout) {
-
return std::peek(timeout);
-
}
-
-
inline string read(int len,void|int(0..1) not_all) {
-
return std::read(len,not_all);
-
}
-
-
inline int write(string|array(string) data) {
-
return std::write(data);
-
}
-
-
int close() {
-
return std::close();
-
}
-
-
void create(Stdio.File stream,object pgsqlsess) {
-
std::create();
-
std::assign(stream);
-
pg::create(pgsqlsess);
-
}
-
}
-
-
#if constant(SSL.sslfile)
-
class PGconnS {
-
inherit SSL.sslfile:std;
-
inherit PGassist:pg;
-
-
Stdio.File rawstream;
-
-
inline int(-1..1) peek(int timeout) {
-
return rawstream.peek(timeout);
-
}
-
-
inline string read(int len,void|int(0..1) not_all) {
-
return std::read(len,not_all);
-
}
-
-
inline int write(string|array(string) data) {
-
return std::write(data);
-
}
-
-
void create(Stdio.File stream, SSL.context ctx,object pgsqlsess) {
-
rawstream=stream;
-
std::create(stream,ctx,1,1);
-
pg::create(pgsqlsess);
-
}
-
}
-
#endif
-
+
final private object getsocket(void|int nossl) { object lcon = Stdio.File(); if(!lcon.connect(host,port)) return UNDEFINED; object fcon; #if constant(SSL.sslfile) if(!nossl && (options->use_ssl || options->force_ssl)) { PD("SSLRequest\n");
-
{ object c=PGassist();
+
{ object c=
.pgsql_util.
PGassist();
lcon.write(({c.plugint32(8),c.plugint32(PG_PROTOCOL(1234,5679))})); } switch(lcon.read(1)) { case "S": SSL.context context = SSL.context(); context->random = Crypto.Random.random_string;
-
fcon=PGconnS(lcon, context
, this
);
+
fcon=
.pgsql_util.
PGconnS(lcon, context);
if(fcon) return fcon; default:lcon.close(); if(!lcon.connect(host,port)) return UNDEFINED; case "N": if(options->force_ssl) ERROR("Encryption not supported on connection to %s:%d\n", host,port); } } #else if(options->force_ssl) ERROR("Encryption library missing, cannot establish connection to %s:%d\n", host,port); #endif
-
fcon=PGconn(lcon,
this);
+
fcon=
.pgsql_util.
PGconn(lcon,this);
return fcon; } //! Cancels the currently running query. //! //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. void cancelquery() { if(qstate==inquery) { qstate=cancelpending;
pike.git/lib/modules/Sql.pmod/pgsql.pike:535:
final int _decodemsg(void|state waitforstate) { #ifdef DEBUG { array line; #ifdef DEBUGMORE line=backtrace(); #endif PD("Waiting for state %O %O\n",waitforstate,line&&line[sizeof(line)-2]); } #endif
-
while(mstate!=waitforstate) {
-
if(mstate!=unauthenticated) {
+
while(
_
mstate!=waitforstate) {
+
if(
_
mstate!=unauthenticated) {
if(qstate==cancelpending) qstate=canceled,sendclose(); if(_c.flushed && qstate==inquery && !_c.bpeek(0)) { int tcurr=time(); int told=tcurr+timeout; while(!_c.bpeek(told-tcurr)) if((tcurr=time())-told>=timeout) { sendclose();cancelquery(); break; }
pike.git/lib/modules/Sql.pmod/pgsql.pike:565:
int bintext=_c.getbyte(); array a; int cols=_c.getint16(); msglen-=4+1+2+2*cols; foreach(a=allocate(cols,([]));;mapping m) m->type=_c.getint16(); if(_c.portal) { // Discard column info, and make it line oriented a=({(["type":bintext?BYTEAOID:TEXTOID,"name":"line"])}); _c.portal->_datarowdesc=a; }
-
mstate=gotrowdescription;
+
_
mstate=gotrowdescription;
}; array(string) getstrings() { string s; if(msglen<1) errtype=protocolerror; s=_c.getstring(msglen); if(s[--msglen]) errtype=protocolerror; if(!msglen) return ({});
pike.git/lib/modules/Sql.pmod/pgsql.pike:593:
if(sizeof(f)) msgresponse[f[..0]]=f[1..]; PD("%O\n",msgresponse); }; case 'R':PD("Authentication\n"); { string sendpass; int authtype; msglen-=4+4; switch(authtype=_c.getint32()) { case 0:PD("Ok\n");
-
mstate=authenticated;
+
_
mstate=authenticated;
break; case 2:PD("KerberosV5\n"); errtype=protocolunsupported; break; case 3:PD("ClearTextPassword\n"); sendpass=pass; break; case 4:PD("CryptPassword\n"); if(msglen<2) errtype=protocolerror;
pike.git/lib/modules/Sql.pmod/pgsql.pike:641:
if(msglen<1) errtype=protocolerror; SSauthdata=_c.getstring(msglen);msglen=0; break; default:PD("Unknown Authentication Method %c\n",authtype); errtype=protocolunsupported; break; } switch(errtype) { case noerror:
-
if(mstate==unauthenticated)
+
if(
_
mstate==unauthenticated)
_c.sendcmd(({"p",_c.plugint32(4+sizeof(sendpass)+1), sendpass,"\0"}),1); break; default: case protocolunsupported: ERROR("Unsupported authenticationmethod %c\n",authtype); break; } break; }
pike.git/lib/modules/Sql.pmod/pgsql.pike:670:
runtimeparameter[ts[0]]=ts[1]; PD("%s=%s\n",ts[0],ts[1]); } else errtype=protocolerror; } break; case 'Z':PD("ReadyForQuery\n"); msglen-=4+1; backendstatus=_c.getbyte();
-
mstate=readyforquery;
+
_
mstate=readyforquery;
qstate=queryidle; _closesent=0; break; case '1':PD("ParseComplete\n"); msglen-=4;
-
mstate=parsecomplete;
+
_
mstate=parsecomplete;
break; case 't': PD("ParameterDescription (for %s)\n", _c.portal?_c.portal->_portalname:"DISCARDED"); { array a; int cols=_c.getint16(); msglen-=4+2+4*cols; foreach(a=allocate(cols);int i;) a[i]=_c.getint32(); #ifdef DEBUGMORE PD("%O\n",a); #endif if(_c.portal) _c.portal->_datatypeoid=a;
-
mstate=gotparameterdescription;
+
_
mstate=gotparameterdescription;
break; } case 'T': PD("RowDescription (for %s)\n", _c.portal?_c.portal->_portalname:"DISCARDED"); msglen-=4+2; { array a; foreach(a=allocate(_c.getint16());int i;) { string s; msglen-=sizeof(s=_c.getstring())+1;
pike.git/lib/modules/Sql.pmod/pgsql.pike:718:
res->length=len>=0?len:"variable"; } res->atttypmod=_c.getint32();res->formatcode=_c.getint16(); a[i]=res; } #ifdef DEBUGMORE PD("%O\n",a); #endif if(_c.portal) _c.portal->_datarowdesc=a;
-
mstate=gotrowdescription;
+
_
mstate=gotrowdescription;
break; } case 'n':PD("NoData\n"); msglen-=4; _c.portal->_datarowdesc=({});
-
mstate=gotrowdescription;
+
_
mstate=gotrowdescription;
break; case '2':PD("BindComplete\n"); msglen-=4;
-
mstate=bindcomplete;
+
_
mstate=bindcomplete;
break; case 'D':PD("DataRow\n"); msglen-=4; if(_c.portal) { #ifdef USEPGsql _c.decodedatarow(msglen);msglen=0; #else array a, datarowdesc; _c.portal->_bytesreceived+=msglen; datarowdesc=_c.portal->_datarowdesc;
pike.git/lib/modules/Sql.pmod/pgsql.pike:774:
else if(!collen) a[i]=""; } a=({a}); _c.portal->_datarows+=a; _c.portal->_inflight-=sizeof(a); #endif } else _c.getstring(msglen),msglen=0;
-
mstate=dataready;
+
_
mstate=dataready;
break; case 's':PD("PortalSuspended\n"); msglen-=4;
-
mstate=portalsuspended;
+
_
mstate=portalsuspended;
break; case 'C':PD("CommandComplete\n"); { msglen-=4; if(msglen<1) errtype=protocolerror; string s=_c.getstring(msglen-1); if(_c.portal) _c.portal->_statuscmdcomplete=s; PD("%s\n",s); if(_c.getbyte()) errtype=protocolerror; msglen=0;
-
mstate=commandcomplete;
+
_
mstate=commandcomplete;
break; } case 'I':PD("EmptyQueryResponse\n"); msglen-=4;
-
mstate=commandcomplete;
+
_
mstate=commandcomplete;
break; case '3':PD("CloseComplete\n"); msglen-=4; _closesent=0; break; case 'd':PD("CopyData\n"); msglen-=4; if(msglen<0) errtype=protocolerror; if(_c.portal) { _c.portal->_bytesreceived+=msglen; _c.portal->_datarows+=({({_c.getstring(msglen)})}); } msglen=0;
-
mstate=dataready;
+
_
mstate=dataready;
break; case 'H':PD("CopyOutResponse\n"); getcols(); if(_c.portal) _c.portal->_fetchlimit=0; // disables further Executes break; case 'G':PD("CopyInResponse\n"); getcols();
-
mstate=copyinresponse;
+
_
mstate=copyinresponse;
break; case 'c':PD("CopyDone\n"); msglen-=4; break; case 'E':PD("ErrorResponse\n"); getresponse(); switch(msgresponse->C) { case "P0001": lastmessage=sprintf("%s: %s",msgresponse->S,msgresponse->M); ERROR(lastmessage
pike.git/lib/modules/Sql.pmod/pgsql.pike:894:
reconnect(1); ERROR("Protocol error with databasel %s@%s:%d/%s\n", user,host,port,database); break; case noerror: break; } if(zero_type(waitforstate)) break; }
-
PD("Found state %O\n",mstate);
-
return mstate;
+
PD("Found state %O\n",
_
mstate);
+
return
_
mstate;
} #ifndef UNBUFFEREDIO private int read_cb(mixed foo, string d) { _c.unread(d); do _decodemsg(); while(_c.bpeek(0)==1); return 0; } #endif void destroy() {
-
+
werror("SRB DESTROYING pgsql\n");
if(_c) _c.sendterminate(); } private void reconnect(void|int force) { if(_c) { reconnected++; #ifdef DEBUG ERROR("While debugging, reconnects are forbidden\n"); exit(1); #endif if(!force) _c.sendterminate(); foreach(prepareds;;mapping tprepared) m_delete(tprepared,"preparedname"); } if(!(_c=getsocket())) ERROR("Couldn't connect to database on %s:%d\n",host,port); _closesent=0;
-
mstate=unauthenticated;
+
_
mstate=unauthenticated;
qstate=queryidle; runtimeparameter=([]); array(string) plugbuf=({"",_c.plugint32(PG_PROTOCOL(3,0))}); if(user) plugbuf+=({"user\0",user,"\0"}); if(database) plugbuf+=({"database\0",database,"\0"}); foreach(sessiondefaults;string name;string value) plugbuf+=({name,"\0",value,"\0"}); plugbuf+=({"\0"});
pike.git/lib/modules/Sql.pmod/pgsql.pike:1404:
} } if(sizeof(plugbuf)) _c.sendcmd(plugbuf,1); // close expireds PD("%O\n",plugbuf); } prepareds[q]=tprepared=([]); } tstart=gethrtime(); } // pgsql_result autoassigns to portal
-
pgsql_result(this,tprepared,q,_fetchlimit,portalbuffersize);
+
.
pgsql_
util.pgsql_
result(this,tprepared,q,_fetchlimit,portalbuffersize);
if(unnamedportalinuse) portalname=PORTALPREFIX+(string)pportalcount++; else unnamedportalinuse++; _c.portal->_portalname=portalname; qstate=inquery; portalsinflight++; mixed err; if(err = catch { if(!sizeof(preparedname) || !tprepared || !tprepared->preparedname) {
pike.git/lib/modules/Sql.pmod/pgsql.pike:1541:
} tprepared->trunstart=tend; } } }) { PD("%O\n",err); reload(1); backendstatus=UNDEFINED; throw(err); }
-
{
pgsql_result
tportal=_c.portal; // Make copy, because it might dislodge
+
{
object
tportal=_c.portal;
// Make copy, because it might dislodge
tportal->fetch_row(1); // upon initial fetch_row() return tportal; } } //! This is an alias for @[big_query()], since @[big_query()] already supports //! streaming of multiple simultaneous queries through the same connection. //! //! @seealso //! @[big_query], @[Sql.Sql], @[Sql.sql_result] object streaming_query(string q,void|mapping(string|int:mixed) bindings) { return big_query(q,bindings); }
-
-
class pgsql_result {
-
-
object _pgsqlsess;
-
private int numrows;
-
private int eoffound;
-
private mixed delayederror;
-
private int copyinprogress;
-
int _fetchlimit;
-
-
private mapping tprepared;
-
#ifdef NO_LOCKING
-
int _qmtxkey;
-
#else
-
Thread.MutexKey _qmtxkey;
-
#endif
-
-
string query;
-
string _portalname;
-
-
int _bytesreceived;
-
int _rowsreceived;
-
int _interruptable;
-
int _inflight;
-
int _portalbuffersize;
-
string _statuscmdcomplete;
-
array(array(mixed)) _datarows;
-
array(mapping(string:mixed)) _datarowdesc;
-
array(int) _datatypeoid;
-
#ifdef USEPGsql
-
int _buffer;
-
#endif
-
-
private object fetchmutex;;
-
-
protected string _sprintf(int type, void|mapping flags) {
-
string res=UNDEFINED;
-
switch(type) {
-
case 'O':
-
res=sprintf(DRIVERNAME"_result numrows: %d eof: %d querylock: %d"
-
" inflight: %d portalname: %O\n"
-
"query: %O\n"
-
"laststatus: %s\n"
-
"%O\n"
-
"%O\n",
-
numrows,eoffound,!!_qmtxkey,_inflight,_portalname,
-
query,
-
_statuscmdcomplete||"",
-
_datarowdesc,
-
_pgsqlsess);
-
break;
-
}
-
return res;
-
}
-
-
void create(object pgsqlsess,mapping(string:mixed) _tprepared,
-
string _query,int fetchlimit,int portalbuffersize) {
-
_pgsqlsess = pgsqlsess;
-
tprepared = _tprepared; query = _query;
-
_datarows = ({ }); numrows = UNDEFINED;
-
fetchmutex = Thread.Mutex();
-
_fetchlimit=fetchlimit;
-
_portalbuffersize=portalbuffersize;
-
steallock();
-
}
-
-
//! Returns the command-complete status for this query.
-
//!
-
//! This function is PostgreSQL-specific, and thus it is not available
-
//! through the generic SQL-interface.
-
//!
-
//! @seealso
-
//! @[affected_rows]
-
string status_command_complete() {
-
return _statuscmdcomplete;
-
}
-
-
//! Returns the number of affected rows by this query.
-
//!
-
//! This function is PostgreSQL-specific, and thus it is not available
-
//! through the generic SQL-interface.
-
//!
-
//! @seealso
-
//! @[status_command_complete]
-
int affected_rows() {
-
int rows;
-
if(_statuscmdcomplete)
-
sscanf(_statuscmdcomplete,"%*s %d",rows);
-
return rows;
-
}
-
-
int num_fields() {
-
return sizeof(_c.portal->datarowdesc);
-
}
-
-
int num_rows() {
-
int numrows;
-
sscanf(_statuscmdcomplete,"%*s %d",numrows);
-
return numrows;
-
}
-
-
int eof() {
-
return eoffound;
-
}
-
-
array(mapping(string:mixed)) fetch_fields() {
-
return _datarowdesc+({});
-
}
-
-
private void releasesession() {
-
if(_pgsqlsess) {
-
if(copyinprogress) {
-
PD("CopyDone\n");
-
_pgsqlsess._c.sendcmd("c\0\0\0\4",1);
-
}
-
_pgsqlsess.reload(2);
-
}
-
_qmtxkey=UNDEFINED;
-
_pgsqlsess=UNDEFINED;
-
}
-
-
void destroy() {
-
catch { // inside destructors, exceptions don't work
-
releasesession();
-
};
-
}
-
-
inline private array(mixed) getdatarow() {
-
array(mixed) datarow=_datarows[0];
-
_datarows=_datarows[1..];
-
return datarow;
-
}
-
-
private void steallock() {
-
#ifndef NO_LOCKING
-
PD("Going to steal oldportal %d\n",!!_pgsqlsess._c.portal);
-
Thread.MutexKey stealmtxkey = stealmutex.lock();
-
do
-
if(_qmtxkey = querymutex.current_locking_key()) {
-
pgsql_result portalb;
-
if(portalb=_pgsqlsess._c.portal) {
-
_pgsqlsess._nextportal++;
-
if(portalb->_interruptable)
-
portalb->fetch_row(2);
-
else {
-
PD("Waiting for the querymutex\n");
-
if((_qmtxkey=querymutex.lock(2))) {
-
if(copyinprogress)
-
ERROR("COPY needs to be finished first\n");
-
ERROR("Driver bug, please report, "
-
"conflict while interleaving SQL-operations\n");
-
}
-
PD("Got the querymutex\n");
-
}
-
_pgsqlsess._nextportal--;
-
}
-
break;
-
}
-
while(!(_qmtxkey=querymutex.trylock()));
-
#else
-
PD("Skipping lock\n");
-
_qmtxkey=1;
-
#endif
-
_pgsqlsess._c.setportal(this);
-
PD("Stealing successful\n");
-
}
-
-
int|array(string|int) fetch_row(void|int|string buffer) {
-
#ifndef NO_LOCKING
-
Thread.MutexKey fetchmtxkey = fetchmutex.lock();
-
#endif
-
if(!buffer && sizeof(_datarows))
-
return getdatarow();
-
if(copyinprogress) {
-
fetchmtxkey = UNDEFINED;
-
if(stringp(buffer)) {
-
PD("CopyData\n");
-
_pgsqlsess._c.sendcmd(({"d",_pgsqlsess._c.plugint32(4+sizeof(buffer)),
-
buffer}));
-
}
-
else
-
releasesession();
-
return UNDEFINED;
-
}
-
mixed err;
-
if(buffer!=2 && (err=delayederror)) {
-
delayederror=UNDEFINED;
-
throw(err);
-
}
-
err = catch {
-
if(_portalname) {
-
if(buffer!=2 && !_qmtxkey) {
-
steallock();
-
_pgsqlsess._sendexecute(_fetchlimit);
-
}
-
while(_pgsqlsess._closesent)
-
_pgsqlsess._decodemsg(); // Flush previous portal sequence
-
for(;;) {
-
#ifdef DEBUGMORE
-
PD("buffer: %d nextportal: %d lock: %d\n",
-
buffer,_pgsqlsess._nextportal,!!_qmtxkey);
-
#endif
-
#ifdef USEPGsql
-
_buffer=buffer;
-
#endif
-
switch(_pgsqlsess._decodemsg()) {
-
case copyinresponse:
-
copyinprogress=1;
-
return UNDEFINED;
-
case dataready:
-
if(tprepared) {
-
tprepared->trun=gethrtime()-tprepared->trunstart;
-
m_delete(tprepared,"trunstart");
-
tprepared = UNDEFINED;
-
}
-
mstate=dataprocessed;
-
_rowsreceived++;
-
switch(buffer) {
-
case 0:
-
case 1:
-
if(_fetchlimit)
-
_fetchlimit=
-
min(_portalbuffersize/2*_rowsreceived/_bytesreceived || 1,
-
_pgsqlsess._fetchlimit);
-
}
-
switch(buffer) {
-
case 2:
-
case 3:
-
continue;
-
case 1:
-
_interruptable=1;
-
if(_pgsqlsess._nextportal)
-
continue;
-
#if STREAMEXECUTES
-
if(_fetchlimit && _inflight<=_fetchlimit-1)
-
_pgsqlsess._sendexecute(_fetchlimit);
-
#endif
-
return UNDEFINED;
-
}
-
#if STREAMEXECUTES
-
if(_fetchlimit && _inflight<=_fetchlimit-1)
-
_pgsqlsess._sendexecute(_fetchlimit); // Overlap Executes
-
#endif
-
return getdatarow();
-
case commandcomplete:
-
_inflight=0;
-
releasesession();
-
switch(buffer) {
-
case 1:
-
case 2:
-
return UNDEFINED;
-
case 3:
-
if(sizeof(_datarows))
-
return getdatarow();
-
}
-
break;
-
case portalsuspended:
-
if(_inflight)
-
continue;
-
if(_pgsqlsess._nextportal) {
-
switch(buffer) {
-
case 1:
-
case 2:
-
_qmtxkey = UNDEFINED;
-
return UNDEFINED;
-
case 3:
-
_qmtxkey = UNDEFINED;
-
return getdatarow();
-
}
-
_fetchlimit=FETCHLIMITLONGRUN;
-
if(sizeof(_datarows)) {
-
_qmtxkey = UNDEFINED;
-
return getdatarow();
-
}
-
buffer=3;
-
}
-
_pgsqlsess._sendexecute(_fetchlimit);
-
default:
-
continue;
-
}
-
break;
-
}
-
}
-
eoffound=1;
-
return UNDEFINED;
-
};
-
PD("Exception %O\n",err);
-
_pgsqlsess.reload();
-
if(buffer!=2)
-
throw(err);
-
if(!delayederror)
-
delayederror=err;
-
return UNDEFINED;
-
}
-
-
};
+