pike.git / lib / modules / Sql.pmod / pgsql.pike

version» Context lines:

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)});