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:67:
private int portalsinflight; object _c; private string SSauthdata,cancelsecret; private int backendpid; private int backendstatus; private mapping(string:mixed) options; private array(string) lastmessage=({}); private int clearmessage; private int earlyclose;
-
private int reconnectp;
+
private mapping(string:array(mixed)) notifylist=([]); mapping(string:string) _runtimeparameter; state _mstate; private enum querystate {queryidle,inquery,cancelpending,canceled}; private querystate qstate; private mapping(string:mapping(string:mixed)) prepareds=([]); private mapping(string:mixed) tprepared; private int pstmtcount; private int pportalcount; private int totalhits;
pike.git/lib/modules/Sql.pmod/pgsql.pike:183:
//! @member int "reconnect" //! Set it to zero to disable automatic reconnects upon losing //! the connection to the database //! @member int "use_ssl" //! If the database supports and allows SSL connections, the session //! will be SSL encrypted, if not, the connection will fallback //! to plain unencrypted //! @member int "force_ssl" //! If the database supports and allows SSL connections, the session //! will be SSL encrypted, if not, the connection will abort
+
//! @member int "text_query"
+
//! Send queries to and retrieve results from the database using text
+
//! instead of the, generally more efficient, default native binary method.
+
//! Turning this on will allow multiple statements per query separated
+
//! by semicolons
//! @member int "cache_autoprepared_statements" //! If set to zero, it disables the automatic statement prepare and //! cache logic; caching prepared statements can be problematic //! when stored procedures and tables are redefined which leave stale //! references in the already cached prepared statements //! @member string "client_encoding" //! Character encoding for the client side, it defaults to using //! the default encoding specified by the database, e.g. //! @expr{"UTF8"@} or @expr{"SQL_ASCII"@}. //! @member string "standard_conforming_strings"
pike.git/lib/modules/Sql.pmod/pgsql.pike:757:
{ string s; msglen-=sizeof(s=_c.getstring())+1; mapping(string:mixed) res=(["name":s]); msglen-=4+2+4+2+4+2; res->tableoid=_c.getint32()||UNDEFINED; res->tablecolattr=_c.getint16()||UNDEFINED; res->type=_c.getint32(); { int len=_c.getint16(); res->length=len>=0?len:"variable"; }
-
res->atttypmod=_c.getint32();res->formatcode=_c.getint16();
+
res->atttypmod=_c.getint32();
+
res->formatcode=_c.getint16();
// Currently broken in Postgres
a[i]=res; } #ifdef DEBUGMORE PD("%O\n",a); #endif if(_c.portal) _c.portal->_datarowdesc=a; _mstate=gotrowdescription; break; }
pike.git/lib/modules/Sql.pmod/pgsql.pike:791:
{ if(tprepared) storetiming(); #ifdef USEPGsql _c.decodedatarow(msglen);msglen=0; #else array a, datarowdesc; _c.portal->_bytesreceived+=msglen; datarowdesc=_c.portal->_datarowdesc; int cols=_c.getint16(); int atext = _c.portal->_alltext; // cache locally for speed
+
int forcetext = _c.portal->_forcetext; // cache locally for speed
string cenc=_runtimeparameter[CLIENT_ENCODING]; a=allocate(cols,UNDEFINED); msglen-=2+4*cols;
-
foreach(
a
;int i;)
+
foreach(
datarowdesc
;int i;
mapping m
)
{ int collen=_c.getint32(); if(collen>0) { msglen-=collen; mixed value;
-
switch(
datarowdesc[i]
->type)
-
{
default
:value=_c.getstring(collen);
+
switch(
int typ=m
->type)
+
{
case FLOAT4OID
:
+
#if SIZEOF_FLOAT>=8
+
case FLOAT8OID:
+
#endif
+
if(!atext)
+
{
value=
(float)
_c.getstring(collen);
break;
-
case
TEXTOID:
-
case BPCHAROID:
-
case VARCHAROID
:
-
value=_c.getstring(collen);
-
if(cenc==UTF8CHARSET && catch(value=utf8_to_string(value)))
-
ERROR("%O contains non-%s characters\n",value,UTF8CHARSET);
+
}
+
default
:value=_c.getstring(collen);
break;
-
case CHAROID:value=atext?_c.getstring(1):_c.getbyte();
+
case CHAROID:
+
value=atext?_c.getstring(1):_c.getbyte();
break; case BOOLOID:value=_c.getbyte();
-
+
switch(value)
+
{ case 'f':value=0;
+
break;
+
case 't':value=1;
+
}
if(atext) value=value?"t":"f"; break;
-
case
INT8OID
:
value=_c.getint64();
-
break;
-
#if SIZEOF_FLOAT>=8
-
case
FLOAT8OID
:
-
#endif
-
case
FLOAT4OID
:
+
case
TEXTOID
:
+
case
BPCHAROID
:
+
case
VARCHAROID
:
value=_c.getstring(collen);
-
+
if(cenc==UTF8CHARSET && catch(value=utf8_to_string(value)))
+
ERROR("%O contains non-%s characters\n",value,UTF8CHARSET);
+
break;
+
case INT8OID:case INT2OID:
+
case OIDOID:case INT4OID:
+
if(forcetext)
+
{ value=_c.getstring(collen);
if(!atext)
-
value=(
float
)value;
+
value=(
int
)value;
+
}
+
else
+
{ switch(typ)
+
{ case INT8OID:value=_c.getint64();
break; case INT2OID:value=_c.getint16(); break; case OIDOID: case INT4OID:value=_c.getint32(); }
-
if(atext
&&!stringp(value
)
)
+
if(atext)
value=(string)value;
-
+
}
+
}
a[i]=value; } else if(!collen) a[i]=""; } a=({a}); _c.portal->_datarows+=a; _c.portal->_inflight-=sizeof(a); #endif }
pike.git/lib/modules/Sql.pmod/pgsql.pike:1047:
if(_c) { reconnected++; prepstmtused=0; if(!force) _c.sendterminate(); else _c.close(); _c=0; foreach(prepareds;;mapping tp) m_delete(tp,"preparedname");
-
if(!
reconnectp
|| !(connectmtxkey = _stealmutex.trylock(2)))
+
if(!
options->reconnect
|| !(connectmtxkey = _stealmutex.trylock(2)))
return 0; // Recursive reconnect, bailing out } if(!(_c=getsocket())) { string msg=sprintf("Couldn't connect to database on %s:%d",host,port); if(force) { lastmessage+=({msg}); return 0; } else ERROR(msg+"\n"); } _closesent=0; _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"});
-
if
(
intp(
options->reconnect)
)
-
reconnectp=
options->reconnect;
+
options->reconnect=zero_type
(options->reconnect)
||
options->reconnect;
foreach(options
-
-(<"use_ssl","force_ssl","cache_autoprepared_statements","reconnect">);
+
-(<"use_ssl","force_ssl","cache_autoprepared_statements","reconnect"
,
+
"text_query"
>);
string name;mixed value) plugbuf+=({name,"\0",(string)value,"\0"}); plugbuf+=({"\0"}); int len=4; foreach(plugbuf;;string s) len+=sizeof(s); plugbuf[0]=_c.plugint32(len); _c.write(plugbuf); PD("%O\n",plugbuf); { mixed err=catch(_decodemsg(readyforquery));
pike.git/lib/modules/Sql.pmod/pgsql.pike:1594:
//! //! 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. //! //! Bindings are supported natively straight across the network. //! Special bindings supported are: //! @mapping //! @member int ":_cache" //! Forces caching on or off for the query at hand.
+
//! @member int ":_text"
+
//! Forces text mode in communication with the database for queries on or off
+
//! for the query at hand. Potentially more efficient than the default
+
//! binary method for simple queries with small or no result sets.
//! @endmapping //! //! @returns //! A @[Sql.pgsql_util.pgsql_result] object (which conforms to the //! @[Sql.sql_result] standard interface for accessing data). It is //! recommended to use @[Sql.Sql()->query()] for simpler queries (because //! it is easier to handle, but stores all the result in memory), and //! @[Sql.Sql()->big_query()] for queries you expect to return huge amounts of //! data (it's harder to handle, but fetches results on demand). //! //! @note //! This function @b{can@} raise exceptions. //! //! @note //! This function supports multiple simultaneous queries (portals) on a single //! database connection. This is a feature not commonly supported by other //! database backends. //! //! @note
-
//! This function does not support multiple queries in one querystring.
+
//! This function
,
by default,
does not support multiple queries in one
+
//!
querystring.
//! I.e. it allows for but does not require a trailing semicolon, but it //! simply ignores any commands after the first unquoted semicolon. This can //! be viewed as a limited protection against SQL-injection attacks.
-
+
//! To make it support multiple queries in one querystring, use the
+
//! @ref{:_text@} option.
//! //! @seealso //! @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result], //! @[Sql.Sql()->query()], @[Sql.pgsql_util.pgsql_result] object big_query(string q,void|mapping(string|int:mixed) bindings, void|int _alltyped) { string preparedname=""; string portalname=""; 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;
pike.git/lib/modules/Sql.pmod/pgsql.pike:1650:
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) { 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)) // multisets are taken literally rval=indices(value)*","; // and bypass the encoding logic
pike.git/lib/modules/Sql.pmod/pgsql.pike:1679:
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; int tstart;
-
if(forcecache==1
+
if(
forcetext)
+
{ if(bindings)
+
q = .sql_util.emulate_bindings(q, bindings, this);
+
if(unnamedportalinuse)
+
throw("Unnamed portal in use, needed for simple query");
+
}
+
else if(
forcecache==1
|| forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || cachealways[q])) { array(string) plugbuf=({}); if(tp=prepareds[q]) { if(tp->preparedname) prepstmtused++, preparedname=tp->preparedname; else if((tstart=tp->trun) && tp->tparse*FACTORPLAN>=tstart && (zero_type(options->cache_autoprepared_statements) || options->cache_autoprepared_statements)) preparedname=PREPSTMTPREFIX+(string)pstmtcount++;
pike.git/lib/modules/Sql.pmod/pgsql.pike:1724:
if(sizeof(plugbuf)) { _c.sendcmd(plugbuf,1); // close expireds PD("%O\n",plugbuf); } tstart=gethrtime(); } // pgsql_result autoassigns to portal else tp=UNDEFINED; connectionclosed=0; for(;;)
-
{ .pgsql_util.pgsql_result(this,q,_fetchlimit,
portalbuffersize,_alltyped,
-
from);
+
{ .pgsql_util.pgsql_result(this,q,_fetchlimit,
+
portalbuffersize,_alltyped,
from
,forcetext
);
if(unnamedportalinuse) portalname=PORTALPREFIX+(string)pportalcount++; else unnamedportalinuse++; _c.portal->_portalname=portalname; qstate=inquery; portalsinflight++; portalsopened++; clearmessage=1; mixed err; if(!(err = catch
-
+
{ if(forcetext)
+
{ _c.sendcmd(({"Q",_c.plugint32(4+sizeof(q)+1),q,"\0"}),1);
+
PD("Simple query: %O\n",q);
+
}
+
else
{ if(!sizeof(preparedname) || !tp || !tp->preparedname) { PD("Parse statement %s\n",preparedname); // 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 _c.sendcmd(({"P",_c.plugint32(4+sizeof(preparedname)+1+sizeof(q)+1+2), preparedname,"\0",q,"\0",_c.plugint16(0)}),3); PD("Query: %O\n",q); } // sends Parameter- and RowDescription for 'S'
pike.git/lib/modules/Sql.pmod/pgsql.pike:1905:
} { array a;int i; len+=(i=sizeof(a=_c.portal->_datarowdesc))*2; plugbuf+=({_c.plugint16(i)}); foreach(a;;mapping col) plugbuf+=({_c.plugint16(oidformat(col->type))}); } plugbuf[1]=_c.plugint32(len); PD("Bind portal %s statement %s\n",portalname,preparedname); _c.sendcmd(plugbuf);
-
#ifdef DEBUGMORE
+
#ifdef
DEBUGMORE
PD("%O\n",plugbuf); #endif } _c.portal->_statuscmdcomplete=UNDEFINED; _sendexecute(_fetchlimit && !(cachealways[q] || sizeof(q)>=MINPREPARELENGTH && execfetchlimit->match(q)) && FETCHLIMITLONGRUN); if(tp) { _decodemsg(bindcomplete);
pike.git/lib/modules/Sql.pmod/pgsql.pike:1933:
{ if(sizeof(preparedname)) tp->preparedname=preparedname; tstart=tend-tstart; if(!tp->tparse || tp->tparse>tstart) tp->tparse=tstart; } tp->trunstart=tend; } tprepared=tp; }
+
}
})) break; PD("%O\n",err); resync(1); backendstatus=UNDEFINED; if(!connectionclosed) throw(err); tp=UNDEFINED; } { object tportal=_c.portal; // Make copy, because it might dislodge