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:15:
//! @ul //! @item //! PostgreSQL network protocol version 3, authentication methods //! currently supported are: cleartext and MD5 (recommended). //! @item //! Streaming queries which do not buffer the whole resultset in memory. //! @item //! Automatic binary transfers to and from the database for most common //! datatypes (amongst others: integer, text and bytea types). //! @item
+
//! Automatic character set conversion and native wide string support.
+
//! Supports UTF8/Unicode for multibyte characters, and all single-byte
+
//! character sets supported by the database.
+
//! @item
//! SQL-injection protection by allowing just one statement per query //! and ignoring anything after the first (unquoted) semicolon in the query. //! @item //! COPY support for streaming up- and download. //! @item //! Accurate error messages. //! @item //! Automatic precompilation of complex queries (session cache). //! @item //! Multiple simultaneous queries on the same database connection.
pike.git/lib/modules/Sql.pmod/pgsql.pike:64:
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 mapping(string:array(mixed)) notifylist=([]);
-
private
mapping(string:string) runtimeparameter;
+
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; private int cachedepth=STATEMENTCACHEDEPTH; private int timeout=QUERYTIMEOUT;
pike.git/lib/modules/Sql.pmod/pgsql.pike:131:
#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"
+
#define PG_PROTOCOL(m,n) (((m)<<16)|(n)) //! @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
pike.git/lib/modules/Sql.pmod/pgsql.pike:307:
object lcon; PD("CancelRequest\n"); if(!(lcon=getsocket(1))) ERROR("Cancel connect failed\n"); lcon.write(({_c.plugint32(16),_c.plugint32(PG_PROTOCOL(1234,5678)), _c.plugint32(backendpid),cancelsecret})); lcon.close(); } }
-
//! Changes the connection charset.
+
//! Changes the connection charset.
When set to @ref{UTF8@}, the query,
+
//! parameters and results can be Pike-native wide strings.
//! //! @param charset //! A PostgreSQL charset name. //! //! @seealso //! @[get_charset()], @[create()], //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=character+sets@} void set_charset(string charset)
-
{
-
#if
0
-
// FIXME Do we want to support the "unicode" setting? (see mysql.pike)
-
#endif
-
big_query("SET CLIENT_ENCODING TO :charset",([":charset":charset]));
+
{ big_query("SET CLIENT_ENCODING TO :charset",([":charset":charset]));
} //! @returns //! The PostgreSQL name for the current connection charset. //! //! @seealso //! @[set_charset()], @[getruntimeparameters()], //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=character+sets@} string get_charset()
-
{ return runtimeparameter
->client
_
encoding
;
+
{ return
_
runtimeparameter
[CLIENT
_
ENCODING]
;
} //! @returns //! Currently active runtimeparameters for //! the open session; these are initialised by the @ref{options@} parameter //! during session creation, and then processed and returned by the server. //! Common values are: //! @mapping //! @member string client_encoding //! Character encoding for the client side, e.g.: "SQL_ASCII"
pike.git/lib/modules/Sql.pmod/pgsql.pike:371:
//! The values can be changed during a session using SET commands to the //! database. //! For other runtimeparameters check the PostgreSQL documentation. //! //! @seealso //! @url{http://search.postgresql.org/search?u=%2Fdocs%2F&q=client+connection+defaults@} //! //! @note //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
-
mapping(string:string) getruntimeparameters()
{
-
return runtimeparameter+([]);
+
mapping(string:string) getruntimeparameters()
+
{ return
_
runtimeparameter+([]);
} //! @returns //! A set of statistics for the current session: //! @mapping //! @member int warnings_dropped //! Number of warnings/notices generated by the database but not //! collected by the application by using @[error()] after the statements //! that generated them. //! @member int skipped_describe_count
pike.git/lib/modules/Sql.pmod/pgsql.pike:668:
break; } case 'K':PD("BackendKeyData\n"); msglen-=4+4;backendpid=_c.getint32();cancelsecret=_c.getstring(msglen); msglen=0; break; case 'S':PD("ParameterStatus\n"); msglen-=4; { array(string) ts=getstrings(); if(sizeof(ts)==2) {
-
runtimeparameter[ts[0]]=ts[1];
+
_
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;
pike.git/lib/modules/Sql.pmod/pgsql.pike:752:
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
+
string cenc=_runtimeparameter[CLIENT_ENCODING];
a=allocate(cols,UNDEFINED); msglen-=2+4*cols; foreach(a;int i;) { int collen=_c.getint32(); if(collen>0) { msglen-=collen; mixed value;
-
switch(datarowdesc[i]->type)
{
-
default:value=_c.getstring(collen);
+
switch(datarowdesc[i]->type)
+
{
default:value=_c.getstring(collen);
break;
-
+
case TEXTOID:
+
case XMLOID:
+
case BPCHAROID:
+
case VARCHAROID:
+
value=_c.getstring(collen);
+
if(cenc==UTF8CHARSET)
+
value=utf8_to_string(value);
+
break;
case CHAROID:value=atext?_c.getstring(1):_c.getbyte(); break; case BOOLOID:value=_c.getbyte(); if(atext) value=value?"t":"f"; break; case INT8OID:value=_c.getint64(); break; #if SIZEOF_FLOAT>=8 case FLOAT8OID:
pike.git/lib/modules/Sql.pmod/pgsql.pike:1016:
if(force) { lastmessage+=({msg}); return 0; } else ERROR(msg+"\n"); } _closesent=0; _mstate=unauthenticated; qstate=queryidle;
-
runtimeparameter=([]);
+
_
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(options-(<"use_ssl","force_ssl","cache_autoprepared_statements">); string name;mixed value) plugbuf+=({name,"\0",(string)value,"\0"}); plugbuf+=({"\0"}); int len=4;
pike.git/lib/modules/Sql.pmod/pgsql.pike:1039:
plugbuf[0]=_c.plugint32(len); _c.write(plugbuf); PD("%O\n",plugbuf); { mixed err=catch(_decodemsg(readyforquery)); if(err) if(force) throw(err); else return 0; }
-
PD("%O\n",runtimeparameter);
+
PD("%O\n",
_
runtimeparameter);
if(force) { lastmessage+=({sprintf("Reconnected to database %s",host_info())}); runcallback(backendpid,"_reconnect",""); } return 1; } //! @decl void reload() //! //! For PostgreSQL this function performs the same function as @[resync()].
pike.git/lib/modules/Sql.pmod/pgsql.pike:1205:
//! @returns //! The given string, but escapes/quotes all contained magic characters //! according to the quoting rules of the current session for non-binary //! arguments in textual SQL-queries. //! //! @note //! Quoting must not be done for parameters passed in bindings. //! //! @seealso //! @[big_query()], @[quotebinary()], @[create()]
-
string quote(string s)
{
-
string r=runtimeparameter->standard_conforming_strings;
+
string quote(string s)
+
{ string r=
_
runtimeparameter->standard_conforming_strings;
if(r && r=="on") return replace(s, "'", "''"); return replace(s, ({ "'", "\\" }), ({ "''", "\\\\" }) ); } //! @returns //! The given string, but escapes/quotes all contained magic characters //! for binary (bytea) arguments in textual SQL-queries. //! //! @note
pike.git/lib/modules/Sql.pmod/pgsql.pike:1263:
} //! @returns //! A string describing the server we are //! talking to. It has the form @expr{"servername/serverversion"@} //! (like the HTTP protocol description) and is most useful in //! conjunction with the generic SQL-server module. //! //! @seealso //! @[host_info()]
-
string server_info ()
{
-
return DRIVERNAME"/"+(runtimeparameter->server_version||"unknown");
+
string server_info ()
+
{ return DRIVERNAME"/"+(
_
runtimeparameter->server_version||"unknown");
} //! @returns //! An array of the databases available on the server. //! //! @param glob //! If specified, list only those databases matching it. array(string) list_dbs (void|string glob) { array row,ret=({}); object res=big_query("SELECT d.datname "
pike.git/lib/modules/Sql.pmod/pgsql.pike:1575:
//! be viewed as a limited protection against SQL-injection attacks. //! //! @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;
-
if(stringp(q)
&&
String.width
(
q
)
>8)
+
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(string) from,to; if(bindings) { int pi=0,rep=0; paramValues=allocate(sizeof(bindings)); from=allocate(sizeof(bindings)); to=allocate(sizeof(bindings)); foreach(bindings; mixed name; mixed value) { if(stringp(name)) { // Throws if mapping key is empty string if(name[0]!=':')
pike.git/lib/modules/Sql.pmod/pgsql.pike:1600:
case ":_cache":forcecache=(int)value; break; } continue; } if(!has_value(q,name)) continue; } from[rep]=name; string rval;
-
if(multisetp(value))
-
rval=sizeof(value)
?
indices(value)[0]
:
"";
-
else {
-
if
(
zero_type(
value)
)
-
paramValues[pi++]=UNDEFINED;
//
NULL
-
else {
-
if(stringp(value) && String.width(value)>8)
-
value
=
string_to_utf8(
value
)
;
-
paramValues[pi++]
=
value
;
+
if(multisetp(value))
//
multisets
are
taken
literally
+
rval=indices
(value)
*",";
//
and
bypass
the
encoding
logic
+
else
+
{
paramValues[pi++]
=value;
+
rval
=
sprintf("$%d",pi)
;
}
-
rval="$"+(string)pi;
-
}
+
to[rep++]=rval; } if(rep--) q=replace(q,from=from[..rep],to=to[..rep]); paramValues= pi ? paramValues[..pi-1] : ({}); } 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 || forcecache!=0 && sizeof(q)>=MINPREPARELENGTH) { array(string) plugbuf=({}); if(tp=prepareds[q]) { if(tp->preparedname) prepstmtused++, preparedname=tp->preparedname; else if((tstart=tp->trun)
pike.git/lib/modules/Sql.pmod/pgsql.pike:1732:
k=-1; // cast empty strings to NULL for non-string types case BYTEAOID: case TEXTOID: case XMLOID: case BPCHAROID: case VARCHAROID:; } plugbuf+=({_c.plugint32(k)}); } else
-
switch(dtoid[i]) {
+
switch(dtoid[i])
+
{
case TEXTOID:
+
case XMLOID:
+
case BPCHAROID:
+
case VARCHAROID:
+
{ value=(string)value;
+
switch(cenc)
+
{ case UTF8CHARSET:
+
value=string_to_utf8(value);
+
break;
default:
-
+
if(String.width(value)>8)
+
ERROR("Don't know how to convert %O to %s encoding\n",
+
value,cenc);
+
}
+
int k;
+
len+=k=sizeof(value);
+
plugbuf+=({_c.plugint32(k),value});
+
break;
+
}
+
default:
{ int k;
-
len+=k=sizeof(value
=(string
)
value)
;
+
value=(string)value;
+
if(String.width(value)>8)
+
ERROR("Wide string %O cannot be converted to BYTEA\n",value);
+
len+=k=sizeof(value);
plugbuf+=({_c.plugint32(k),value}); break; } case BOOLOID:plugbuf+=({_c.plugint32(1)});len++;
-
switch
(stringp(value)
?value[0]:value
)
{
-
case
'o':case
'O':
-
_c.plugbyte
(
stringp
(value)
&&sizeof(value
)
>1
-
&&(
value
[1]
=
='n'||
value
[1]=='N'))
;
+
do
+
{ int tval;
+
if
(stringp(value))
+
tval=value[0];
+
else
if
(
!intp
(value))
+
{
value=
!!
value;
// cast to boolean
break;
-
case
0:case
'f':case
'F':
case '
n
':case '
N
':
-
plugbuf+=(
{
_c.plugbyte(0)})
;
+
}
+
else
+
tval=value;
+
switch(tval)
+
{
case '
o
':case '
O
':
+
catch
+
{ tval
=
value[1]
;
+
value=tval=='n'||tval=='N';
break;
-
+
};
default:
-
plugbuf+
=
({_c.plugbyte(
1
)})
;
+
value
=1;
break;
-
+
case 0:case 'f':case 'F':case 'n':case 'N':
+
value=0;
+
break;
}
-
+
}
+
while(0);
+
plugbuf+=({_c.plugbyte(value)});
break;
-
case CHAROID:
plugbuf+=({_c.plugint32(1)});len++;
+
case CHAROID:
if(intp(value))
-
plugbuf+=({_c.plugbyte(value)});
+
len++,
plugbuf+=({_c.
plugint32(1),_c.
plugbyte(value)});
else { value=(string)value;
-
if
(sizeof(value)
!=1
)
-
ERROR("\"char\" types must be 1 byte wide, got %
d\n
",
-
sizeof(
value));
-
plugbuf+=(
{value}
);
+
switch
(sizeof(value))
+
{
default:
+
ERROR("\"char\" types must be 1 byte wide, got %
O\n
",
+
value)
;
+
case 0:
+
plugbuf+=({_c.plugint32(-1
)
})
;
// NULL
+
break;
+
case 1:len++;
+
plugbuf+=(
{_c.plugint32(1
)
,_c.plugbyte(value[0])})
;
}
-
+
}
break; case INT8OID:len+=8; plugbuf+=({_c.plugint32(8),_c.plugint64((int)value)}); break; case OIDOID: case INT4OID:len+=4; plugbuf+=({_c.plugint32(4),_c.plugint32((int)value)}); break; case INT2OID:len+=2; plugbuf+=({_c.plugint32(2),_c.plugint16((int)value)});