a2014e2008-07-14Stephen R. van den Berg /* * This is the PostgreSQL direct network module for Pike. */ //! This is an interface to the PostgreSQL database
0412962009-01-19Stephen R. van den Berg //! server. This module is independent of any external libraries.
a2014e2008-07-14Stephen R. van den Berg //! Note that you @b{do not@} need to have a //! PostgreSQL server running on your host to use this module: you can //! connect to the database over a TCP/IP socket.
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! This module replaces the functionality of the older @[Sql.postgres] //! and @[Postgres.postgres] modules. //!
4e1d1f2008-08-01Stephen R. van den Berg //! This module supports the following features:
2c228b2009-02-15Stephen R. van den Berg //! @ul //! @item //! PostgreSQL network protocol version 3, authentication methods
4e1d1f2008-08-01Stephen R. van den Berg //! currently supported are: cleartext and MD5 (recommended).
2c228b2009-02-15Stephen R. van den Berg //! @item //! Streaming queries which do not buffer the whole resultset in memory. //! @item //! Automatic binary transfers to and from the database for most common
4e1d1f2008-08-01Stephen R. van den Berg //! datatypes (amongst others: integer, text and bytea types).
2c228b2009-02-15Stephen R. van den Berg //! @item
26c0ad2009-02-28Stephen R. van den Berg //! 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
2c228b2009-02-15Stephen R. van den Berg //! SQL-injection protection by allowing just one statement per query
4e1d1f2008-08-01Stephen R. van den Berg //! and ignoring anything after the first (unquoted) semicolon in the query.
2c228b2009-02-15Stephen R. van den Berg //! @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. //! @item //! Cancelling of long running queries by force or by timeout. //! @item //! Event driven NOTIFY. //! @item //! SSL encrypted connections (optional or forced). //! @endul
0412962009-01-19Stephen R. van den Berg //! Check the PostgreSQL documentation for further details.
a2014e2008-07-14Stephen R. van den Berg //!
4e1d1f2008-08-01Stephen R. van den Berg //! @note //! Multiple simultaneous queries on the same database connection is a //! feature that none of the other database drivers for Pike support. //! So, although it's efficient, its use will make switching database drivers //! difficult. //!
a2014e2008-07-14Stephen R. van den Berg //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[Sql.Sql], @[Sql.postgres], @url{http://www.postgresql.org/docs/@}
a2014e2008-07-14Stephen R. van den Berg  #pike __REAL_VERSION__
91e2382014-11-10Stephen R. van den Berg #require constant(Thread.Thread)
a2014e2008-07-14Stephen R. van den Berg 
ecbab12008-07-27Stephen R. van den Berg #include "pgsql.h"
a2014e2008-07-14Stephen R. van den Berg 
e22cab2008-07-14Stephen R. van den Berg #define ERROR(X ...) predef::error(X)
a2014e2008-07-14Stephen R. van den Berg 
d743bf2014-11-13Stephen R. van den Berg final int _fetchlimit=FETCHLIMIT; final Thread.Mutex _unnamedportalmux;
6eb5942014-11-12Stephen R. van den Berg private Thread.Mutex unnamedstatement;
d743bf2014-11-13Stephen R. van den Berg final int _portalsinflight;
cff3a32014-09-12Stephen R. van den Berg 
3b31fa2014-11-14Stephen R. van den Berg private .pgsql_util.conxion c;
6eb5942014-11-12Stephen R. van den Berg private string cancelsecret;
d743bf2014-11-13Stephen R. van den Berg private int backendpid, backendstatus; final mapping(string:mixed) _options;
6eb5942014-11-12Stephen R. van den Berg private array(string) lastmessage=({}); private int clearmessage; private mapping(string:array(mixed)) notifylist=([]);
d743bf2014-11-13Stephen R. van den Berg final mapping(string:string) _runtimeparameter; final mapping(string:mapping(string:mixed)) _prepareds=([]);
6eb5942014-11-12Stephen R. van den Berg private int pstmtcount; private int ptstmtcount; // Periodically one would like to reset this
4ef8a32014-10-29Stephen R. van den Berg  // but checking when this is safe to do // probably is more costly than the gain
d743bf2014-11-13Stephen R. van den Berg final int _pportalcount;
6eb5942014-11-12Stephen R. van den Berg private int totalhits; private int cachedepth=STATEMENTCACHEDEPTH; private int timeout=QUERYTIMEOUT; private int portalbuffersize=PORTALBUFFERSIZE;
d743bf2014-11-13Stephen R. van den Berg private int reconnected; // Number of times the connection was reset
6eb5942014-11-12Stephen R. van den Berg private int reconnectdelay; // Time to next reconnect
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_STATS
d743bf2014-11-13Stephen R. van den Berg private int skippeddescribe; // Number of times we skipped Describe phase private int portalsopened; // Number of portals opened private int prepstmtused; // Number of times we used prepared statements
cff3a32014-09-12Stephen R. van den Berg #endif
d743bf2014-11-13Stephen R. van den Berg final int _msgsreceived; // Number of protocol messages received final int _bytesreceived; // Number of bytes received private int warningsdropcount; // Number of uncollected warnings
6eb5942014-11-12Stephen R. van den Berg private int warningscollected; private int invalidatecache; private Thread.Queue qportals;
d743bf2014-11-13Stephen R. van den Berg final mixed _delayederror;
6eb5942014-11-12Stephen R. van den Berg private function (:void) readyforquery_cb;
cff3a32014-09-12Stephen R. van den Berg 
d743bf2014-11-13Stephen R. van den Berg final string _host; final int _port;
6eb5942014-11-12Stephen R. van den Berg private string database, user, pass; private Thread.Condition waitforauthready;
4e0be82014-11-18Stephen R. van den Berg final Thread.Mutex _shortmux;
d743bf2014-11-13Stephen R. van den Berg final Thread.Condition _readyforcommit; final int _waittocommit, _readyforquerycount;
a2014e2008-07-14Stephen R. van den Berg 
6eb5942014-11-12Stephen R. van den Berg private string _sprintf(int type, void|mapping flags) {
443c1f2014-08-21Stephen R. van den Berg  string res=UNDEFINED;
cff3a32014-09-12Stephen R. van den Berg  switch(type) { case 'O':
bf75ce2014-11-15Stephen R. van den Berg  res=sprintf(DRIVERNAME"(%s@%s:%d/%s,%d,%d)", user,_host,_port,database,c?->socket?->query_fd(),backendpid);
cff3a32014-09-12Stephen R. van den Berg  break;
a2014e2008-07-14Stephen R. van den Berg  } return res; }
1fe3992008-07-16Peter Bortas //! @decl void create()
a2014e2008-07-14Stephen R. van den Berg //! @decl void create(string host, void|string database, void|string user,@
c0f2b72009-04-10Stephen R. van den Berg //! void|string password, void|mapping(string:mixed) options)
a2014e2008-07-14Stephen R. van den Berg //!
f390972009-02-12Stephen R. van den Berg //! With no arguments, this function initialises (reinitialises if a
2c228b2009-02-15Stephen R. van den Berg //! connection has been set up previously) a connection to the
a2014e2008-07-14Stephen R. van den Berg //! PostgreSQL backend. Since PostgreSQL requires a database to be //! selected, it will try to connect to the default database. The
2c228b2009-02-15Stephen R. van den Berg //! connection may fail however, for a variety of reasons; in this case
f4c9d62009-02-15Stephen R. van den Berg //! the most likely reason is because you don't have sufficient privileges
a2014e2008-07-14Stephen R. van den Berg //! to connect to that database. So use of this particular syntax is //! discouraged. //!
2c228b2009-02-15Stephen R. van den Berg //! @param host //! Should either contain @expr{"hostname"@} or //! @expr{"hostname:portname"@}. This allows you to specify the TCP/IP //! port to connect to. If the parameter is @expr{0@} or @expr{""@}, //! it will try to connect to localhost, default port.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @param database //! Specifies the database to connect to. Not specifying this is //! only supported if the PostgreSQL backend has a default database //! configured. If you do not want to connect to any live database,
9a3d002011-01-09Henrik Grubbström (Grubba) //! you can use @expr{"template1"@}.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @param options //! Currently supports at least the following: //! @mapping
23914b2012-04-02Stephen R. van den Berg //! @member int "reconnect" //! Set it to zero to disable automatic reconnects upon losing
bbf6532012-04-13Stephen R. van den Berg //! the connection to the database. Not setting it, or setting //! it to one, will cause one timed reconnect to take place. //! Setting it to -1 will cause the system to try and reconnect
e6d6202012-04-16Stephen R. van den Berg //! indefinitely.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "use_ssl"
c0f2b72009-04-10Stephen R. van den Berg //! If the database supports and allows SSL connections, the session //! will be SSL encrypted, if not, the connection will fallback
e6d6202012-04-16Stephen R. van den Berg //! to plain unencrypted.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "force_ssl"
c0f2b72009-04-10Stephen R. van den Berg //! If the database supports and allows SSL connections, the session
e6d6202012-04-16Stephen R. van den Berg //! will be SSL encrypted, if not, the connection will abort.
dc97bc2012-04-06Stephen R. van den Berg //! @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
e6d6202012-04-16Stephen R. van den Berg //! by semicolons.
7e85fa2014-11-10Stephen R. van den Berg //! @member int "sync_parse" //! Set it to zero to turn synchronous parsing off for statements. //! Setting this to off can cause surprises because statements could //! be parsed before the previous statements have been executed. //! This can speed up parsing by increased parallelism.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "cache_autoprepared_statements"
c0f2b72009-04-10Stephen R. van den Berg //! 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
e6d6202012-04-16Stephen R. van den Berg //! references in the already cached prepared statements.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "client_encoding"
c0f2b72009-04-10Stephen R. van den Berg //! Character encoding for the client side, it defaults to using
1824a02009-04-10Stephen R. van den Berg //! the default encoding specified by the database, e.g.
9a3d002011-01-09Henrik Grubbström (Grubba) //! @expr{"UTF8"@} or @expr{"SQL_ASCII"@}.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "standard_conforming_strings"
c0f2b72009-04-10Stephen R. van den Berg //! When on, backslashes in strings must not be escaped any longer,
e6d6202012-04-16Stephen R. van den Berg //! @[quote()] automatically adjusts quoting strategy accordingly.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "escape_string_warning"
c0f2b72009-04-10Stephen R. van den Berg //! When on, a warning is issued if a backslash (\) appears in an
9a3d002011-01-09Henrik Grubbström (Grubba) //! ordinary string literal and @expr{"standard_conforming_strings"@} //! is off, defaults to on.
2c228b2009-02-15Stephen R. van den Berg //! @endmapping
cb26232008-08-04Stephen R. van den Berg //! For the numerous other options please check the PostgreSQL manual.
39fe7f2008-07-26Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @note //! You need to have a database selected before using the sql-object, //! otherwise you'll get exceptions when you try to query it. Also //! notice that this function @b{can@} raise exceptions if the db //! server doesn't respond, if the database doesn't exist or is not //! accessible by you. //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[Postgres.postgres], @[Sql.Sql], @[select_db()],
e6d6202012-04-16Stephen R. van den Berg //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+defaults@}
11b13b2014-08-16Martin Nilsson protected void create(void|string host, void|string database, void|string user, void|string pass,
cff3a32014-09-12Stephen R. van den Berg  void|mapping(string:mixed) options) {
8e06a32014-09-30Martin Nilsson  this::pass = pass;
cff3a32014-09-12Stephen R. van den Berg  if(pass) {
334f372008-08-12Stephen R. van den Berg  String.secure(pass);
11b13b2014-08-16Martin Nilsson  pass = "CENSORED"; }
8e06a32014-09-30Martin Nilsson  this::user = user; this::database = database;
d743bf2014-11-13Stephen R. van den Berg  _options = options || ([]);
11b13b2014-08-16Martin Nilsson  if(!host) host = PGSQL_DEFAULT_HOST;
cff3a32014-09-12Stephen R. van den Berg  if(has_value(host,":") && sscanf(host,"%s:%d",host,_port)!=2)
a2014e2008-07-14Stephen R. van den Berg  ERROR("Error in parsing the hostname argument\n");
cff3a32014-09-12Stephen R. van den Berg  this::_host = host;
11b13b2014-08-16Martin Nilsson 
cff3a32014-09-12Stephen R. van den Berg  if(!_port) _port = PGSQL_DEFAULT_PORT; .pgsql_util.register_backend();
4e0be82014-11-18Stephen R. van den Berg  _shortmux=Thread.Mutex();
a2014e2008-07-14Stephen R. van den Berg  reconnect(); }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! The textual description of the last
a2014e2008-07-14Stephen R. van den Berg //! 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). //!
f28c0d2008-08-01Stephen R. van den Berg //! During the execution of a statement, this function accumulates all //! non-error messages (notices, warnings, etc.). If a statement does not //! generate any errors, this function will return all collected messages //! from the last statement. //!
2c228b2009-02-15Stephen R. van den Berg //! @note
be07392009-02-01Stephen R. van den Berg //! The string returned is not newline-terminated. //!
2c228b2009-02-15Stephen R. van den Berg //! @param clear //! To clear the error, set it to @expr{1@}.
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[big_query()]
cff3a32014-09-12Stephen R. van den Berg string error(void|int clear) { throwdelayederror(this);
11b13b2014-08-16Martin Nilsson  string s=lastmessage*"\n";
a2014e2008-07-14Stephen R. van den Berg  if(clear)
be07392009-02-01Stephen R. van den Berg  lastmessage=({});
cb26232008-08-04Stephen R. van den Berg  warningscollected=0;
f11ef72009-02-01Stephen R. van den Berg  return sizeof(s) && s;
a2014e2008-07-14Stephen R. van den Berg } //! This function returns a string describing what host are we talking to, //! and how (TCP/IP or UNIX sockets).
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[server_info()]
cff3a32014-09-12Stephen R. van den Berg string host_info() {
11b13b2014-08-16Martin Nilsson  return sprintf("fd:%d TCP/IP %s:%d PID %d",
cff3a32014-09-12Stephen R. van den Berg  c?c->socket->query_fd():-1,_host,_port,backendpid);
a2014e2008-07-14Stephen R. van den Berg }
6b4ea82012-04-10Stephen R. van den Berg //! Returns true if the connection seems to be open. //! //! @note //! This function only checks that there's an open connection, //! and that the other end hasn't closed it yet. No data is //! sent over the connection. //! //! For a more reliable check of whether the connection //! is alive, please use @[ping()]. //! //! @seealso //! @[ping()]
cff3a32014-09-12Stephen R. van den Berg int is_open() { catch {
a362e32014-11-03Stephen R. van den Berg  return c->socket->is_open();
cff3a32014-09-12Stephen R. van den Berg  }; return 0;
6b4ea82012-04-10Stephen R. van den Berg } //! @decl int ping() //! //! Check whether the connection is alive. //! //! @returns //! Returns one of the following: //! @int //! @value 0 //! Everything ok. //! @value 1
cff3a32014-09-12Stephen R. van den Berg //! The connection has reconnected automatically.
6b4ea82012-04-10Stephen R. van den Berg //! @value -1 //! The server has gone away, and the connection is dead. //! @endint //! //! @seealso //! @[is_open()]
cff3a32014-09-12Stephen R. van den Berg int ping() {
0e7f332014-11-15Stephen R. van den Berg  return is_open() && !catch(c->start()->sendcmd(FLUSHSEND))
cff3a32014-09-12Stephen R. van den Berg  ? !!reconnected : -1;
6b4ea82012-04-10Stephen R. van den Berg }
3b31fa2014-11-14Stephen R. van den Berg private .pgsql_util.conxion getsocket(void|int nossl) {
4e0be82014-11-18Stephen R. van den Berg  return .pgsql_util.conxion(this,qportals,(int)nossl,_shortmux);
a2014e2008-07-14Stephen R. van den Berg }
cff3a32014-09-12Stephen R. van den Berg //! Cancels all currently running queries in this session.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @seealso //! @[reload()], @[resync()] //! //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg void cancelquery() { PD("CancelRequest\n");
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.conxion lcon=getsocket(1);
cff3a32014-09-12Stephen R. van den Berg  lcon->add_int32(16)->add_int32(PG_PROTOCOL(1234,5678))
0e7f332014-11-15Stephen R. van den Berg  ->add_int32(backendpid)->add(cancelsecret)->sendcmd(FLUSHSEND);
cff3a32014-09-12Stephen R. van den Berg  lcon->close();
fb8eb42014-10-31Stephen R. van den Berg #ifdef PG_DEBUGMORE PD("Closetrace %O\n",backtrace()); #endif
d8be282014-11-14Stephen R. van den Berg  if(c) {
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.conxion plugbuffer=c->start(1); foreach(qportals->peek_array();;int|.pgsql_util.sql_result portal)
d8be282014-11-14Stephen R. van den Berg  if(objectp(portal)) portal->_closeportal(plugbuffer);
0e7f332014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(SENDOUT);
d8be282014-11-14Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg }
9a3d002011-01-09Henrik Grubbström (Grubba) //! Changes the connection charset. When set to @expr{"UTF8"@}, the query,
26c0ad2009-02-28Stephen R. van den Berg //! parameters and results can be Pike-native wide strings.
d6f86e2008-08-20Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @param charset //! A PostgreSQL charset name.
d6f86e2008-08-20Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[get_charset()], @[create()],
e6d6202012-04-16Stephen R. van den Berg //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=character+sets@}
cff3a32014-09-12Stephen R. van den Berg void set_charset(string charset) {
bf75ce2014-11-15Stephen R. van den Berg  if(charset) big_query(sprintf("SET CLIENT_ENCODING TO '%s'",quote(charset)));
d6f86e2008-08-20Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! The PostgreSQL name for the current connection charset.
d6f86e2008-08-20Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[set_charset()], @[getruntimeparameters()],
e6d6202012-04-16Stephen R. van den Berg //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=character+sets@}
cff3a32014-09-12Stephen R. van den Berg string get_charset() {
6932232014-11-09Stephen R. van den Berg  waitauthready();
11b13b2014-08-16Martin Nilsson  return _runtimeparameter[CLIENT_ENCODING];
d6f86e2008-08-20Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! Currently active runtimeparameters for
9a3d002011-01-09Henrik Grubbström (Grubba) //! the open session; these are initialised by the @tt{options@} parameter
cb26232008-08-04Stephen R. van den Berg //! during session creation, and then processed and returned by the server. //! Common values are:
2c228b2009-02-15Stephen R. van den Berg //! @mapping
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "client_encoding"
1824a02009-04-10Stephen R. van den Berg //! Character encoding for the client side, e.g.
9a3d002011-01-09Henrik Grubbström (Grubba) //! @expr{"UTF8"@} or @expr{"SQL_ASCII"@}.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "server_encoding"
c0f2b72009-04-10Stephen R. van den Berg //! Character encoding for the server side as determined when the
e6d6202012-04-16Stephen R. van den Berg //! database was created, e.g. @expr{"UTF8"@} or @expr{"SQL_ASCII"@}.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "DateStyle"
e6d6202012-04-16Stephen R. van den Berg //! Date parsing/display, e.g. @expr{"ISO, DMY"@}.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "TimeZone"
e6d6202012-04-16Stephen R. van den Berg //! Default timezone used by the database, e.g. @expr{"localtime"@}.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "standard_conforming_strings"
e6d6202012-04-16Stephen R. van den Berg //! When on, backslashes in strings must not be escaped any longer.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "session_authorization"
e6d6202012-04-16Stephen R. van den Berg //! Displays the authorisationrole which the current session runs under.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "is_superuser"
c0f2b72009-04-10Stephen R. van den Berg //! Indicates if the current authorisationrole has database-superuser
e6d6202012-04-16Stephen R. van den Berg //! privileges.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "integer_datetimes"
e6d6202012-04-16Stephen R. van den Berg //! Reports wether the database supports 64-bit-integer dates and times.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "server_version"
e6d6202012-04-16Stephen R. van den Berg //! Shows the server version, e.g. @expr{"8.3.3"@}.
2c228b2009-02-15Stephen R. van den Berg //! @endmapping
cb26232008-08-04Stephen R. van den Berg //! //! The values can be changed during a session using SET commands to the //! database. //! For other runtimeparameters check the PostgreSQL documentation.
cbe2762008-08-01Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @seealso
e6d6202012-04-16Stephen R. van den Berg //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+defaults@}
2c228b2009-02-15Stephen R. van den Berg //! //! @note
cbe2762008-08-01Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg mapping(string:string) getruntimeparameters() {
6932232014-11-09Stephen R. van den Berg  waitauthready();
11b13b2014-08-16Martin Nilsson  return _runtimeparameter+([]);
cbe2762008-08-01Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! A set of statistics for the current session: //! @mapping
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "warnings_dropped"
cb26232008-08-04Stephen R. van den Berg //! Number of warnings/notices generated by the database but not
f4c9d62009-02-15Stephen R. van den Berg //! collected by the application by using @[error()] after the statements
cb26232008-08-04Stephen R. van den Berg //! that generated them.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "skipped_describe_count"
cb26232008-08-04Stephen R. van den Berg //! Number of times the driver skipped asking the database to //! describe the statement parameters because it was already cached.
933a762014-11-10Stephen R. van den Berg //! Only available if PG_STATS is compile-time enabled.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "used_prepared_statements"
cb26232008-08-04Stephen R. van den Berg //! Numer of times prepared statements were used from cache instead of //! reparsing in the current session.
933a762014-11-10Stephen R. van den Berg //! Only available if PG_STATS is compile-time enabled.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "current_prepared_statements"
cb26232008-08-04Stephen R. van den Berg //! Cache size of currently prepared statements.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "current_prepared_statement_hits"
cb26232008-08-04Stephen R. van den Berg //! Sum of the number hits on statements in the current statement cache.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "prepared_statement_count"
cb26232008-08-04Stephen R. van den Berg //! Total number of prepared statements generated.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "portals_opened_count"
cb26232008-08-04Stephen R. van den Berg //! Total number of portals opened, i.e. number of statements issued //! to the database.
933a762014-11-10Stephen R. van den Berg //! Only available if PG_STATS is compile-time enabled.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "bytes_received"
cb26232008-08-04Stephen R. van den Berg //! Total number of bytes received from the database so far.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "messages_received"
cb26232008-08-04Stephen R. van den Berg //! Total number of messages received from the database (one SQL-statement //! requires multiple messages to be exchanged).
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "reconnect_count"
cb26232008-08-04Stephen R. van den Berg //! Number of times the connection to the database has been lost.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "portals_in_flight"
cb26232008-08-04Stephen R. van den Berg //! Currently still open portals, i.e. running statements.
2c228b2009-02-15Stephen R. van den Berg //! @endmapping
cb26232008-08-04Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
cb26232008-08-04Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg mapping(string:mixed) getstatistics() {
11b13b2014-08-16Martin Nilsson  mapping(string:mixed) stats=([
cb26232008-08-04Stephen R. van den Berg  "warnings_dropped":warningsdropcount,
fb8eb42014-10-31Stephen R. van den Berg  "current_prepared_statements":sizeof(_prepareds),
cb26232008-08-04Stephen R. van den Berg  "current_prepared_statement_hits":totalhits, "prepared_statement_count":pstmtcount,
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_STATS
4ef8a32014-10-29Stephen R. van den Berg  "used_prepared_statements":prepstmtused,
cff3a32014-09-12Stephen R. van den Berg  "skipped_describe_count":skippeddescribe,
cb26232008-08-04Stephen R. van den Berg  "portals_opened_count":portalsopened,
cff3a32014-09-12Stephen R. van den Berg #endif
cb26232008-08-04Stephen R. van den Berg  "messages_received":_msgsreceived, "bytes_received":_bytesreceived, "reconnect_count":reconnected,
cff3a32014-09-12Stephen R. van den Berg  "portals_in_flight":_portalsinflight,
cb26232008-08-04Stephen R. van den Berg  ]); return stats; }
2c228b2009-02-15Stephen R. van den Berg //! @param newdepth //! Sets the new cachedepth for automatic caching of prepared statements.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @returns //! The previous cachedepth. //! //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg int setcachedepth(void|int newdepth) {
11b13b2014-08-16Martin Nilsson  int olddepth=cachedepth;
65340d2014-08-15Martin Nilsson  if(!undefinedp(newdepth) && newdepth>=0)
a2014e2008-07-14Stephen R. van den Berg  cachedepth=newdepth; return olddepth; }
2c228b2009-02-15Stephen R. van den Berg //! @param newtimeout //! Sets the new timeout for long running queries.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @returns //! The previous timeout. //! //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg int settimeout(void|int newtimeout) {
11b13b2014-08-16Martin Nilsson  int oldtimeout=timeout;
cff3a32014-09-12Stephen R. van den Berg  if(!undefinedp(newtimeout) && newtimeout>0) {
a2014e2008-07-14Stephen R. van den Berg  timeout=newtimeout;
cff3a32014-09-12Stephen R. van den Berg  if(c) c->timeout=timeout; }
a2014e2008-07-14Stephen R. van den Berg  return oldtimeout; }
f4c9d62009-02-15Stephen R. van den Berg //! @param newportalbuffersize //! Sets the new portalbuffersize for buffering partially concurrent queries. //! //! @returns //! The previous portalbuffersize.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg int setportalbuffersize(void|int newportalbuffersize) {
11b13b2014-08-16Martin Nilsson  int oldportalbuffersize=portalbuffersize;
65340d2014-08-15Martin Nilsson  if(!undefinedp(newportalbuffersize) && newportalbuffersize>0)
a2014e2008-07-14Stephen R. van den Berg  portalbuffersize=newportalbuffersize; return oldportalbuffersize; }
f4c9d62009-02-15Stephen R. van den Berg //! @param newfetchlimit //! Sets the new fetchlimit to interleave queries. //! //! @returns //! The previous fetchlimit.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg int setfetchlimit(void|int newfetchlimit) {
fb8eb42014-10-31Stephen R. van den Berg  int oldfetchlimit=_fetchlimit;
65340d2014-08-15Martin Nilsson  if(!undefinedp(newfetchlimit) && newfetchlimit>=0)
fb8eb42014-10-31Stephen R. van den Berg  _fetchlimit=newfetchlimit;
a2014e2008-07-14Stephen R. van den Berg  return oldfetchlimit; }
6eb5942014-11-12Stephen R. van den Berg private string glob2reg(string glob) {
11b13b2014-08-16Martin Nilsson  if(!glob||!sizeof(glob))
a2014e2008-07-14Stephen R. van den Berg  return "%"; return replace(glob,({"*","?","\\","%","_"}),({"%","_","\\\\","\\%","\\_"})); }
6eb5942014-11-12Stephen R. van den Berg private string a2nls(array(string) msg) {
11b13b2014-08-16Martin Nilsson  return msg*"\n"+"\n";
a2014e2008-07-14Stephen R. van den Berg }
6eb5942014-11-12Stephen R. van den Berg private string pinpointerror(void|string query,void|string offset) {
11b13b2014-08-16Martin Nilsson  if(!query)
a2014e2008-07-14Stephen R. van den Berg  return ""; int k=(int)offset; if(k<=0) return MARKSTART+query+MARKEND; return MARKSTART+(k>1?query[..k-2]:"")+MARKERROR+query[k-1..]+MARKEND; }
6eb5942014-11-12Stephen R. van den Berg private void connect_cb() {
cff3a32014-09-12Stephen R. van den Berg  PD("%O\n",_runtimeparameter); }
6eb5942014-11-12Stephen R. van den Berg private void reconnect_cb() {
fb8eb42014-10-31Stephen R. van den Berg  lastmessage+=({sprintf("Reconnected to database %s",host_info())}); runcallback(backendpid,"_reconnect","");
cff3a32014-09-12Stephen R. van den Berg }
3b31fa2014-11-14Stephen R. van den Berg private array(string) showbindings(.pgsql_util.sql_result portal) {
cff3a32014-09-12Stephen R. van den Berg  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))});
bac80c2010-02-15Stephen R. van den Berg  } }
cff3a32014-09-12Stephen R. van den Berg  return msgs;
bac80c2010-02-15Stephen R. van den Berg }
6eb5942014-11-12Stephen R. van den Berg private void preplastmessage(mapping(string:string) msgresponse) {
cff3a32014-09-12Stephen R. van den Berg  lastmessage=({ sprintf("%s %s:%s %s\n (%s:%s:%s)", msgresponse.S,msgresponse.C,msgresponse.P||"", msgresponse.M,msgresponse.F||"",msgresponse.R||"", msgresponse.L||"")}); }
6eb5942014-11-12Stephen R. van den Berg private void waitauthready() {
6932232014-11-09Stephen R. van den Berg  if(waitforauthready) {
bf75ce2014-11-15Stephen R. van den Berg  PD("%d Wait for auth ready %O\n",c?->socket?->query_fd(),backtrace()[-2]);
4e0be82014-11-18Stephen R. van den Berg  Thread.MutexKey lock=_shortmux->lock();
6932232014-11-09Stephen R. van den Berg  catch(waitforauthready->wait(lock)); lock=0;
bf75ce2014-11-15Stephen R. van den Berg  PD("%d Wait for auth ready released.\n",c?->socket?->query_fd());
6932232014-11-09Stephen R. van den Berg  } }
6eb5942014-11-12Stephen R. van den Berg private inline mixed callout(function(mixed ...:void) f,
0fed8c2014-11-10Stephen R. van den Berg  float|int delay,mixed ... args) { return .pgsql_util.local_backend->call_out(f,delay,@args); }
3b31fa2014-11-14Stephen R. van den Berg final void _processloop(.pgsql_util.conxion ci) {
a362e32014-11-03Stephen R. van den Berg  int terminating=0;
3b31fa2014-11-14Stephen R. van den Berg  int|.pgsql_util.sql_result portal;
cff3a32014-09-12Stephen R. van den Berg  mixed err;
11b13b2014-08-16Martin Nilsson  {
ab24362014-11-10Stephen R. van den Berg  Stdio.Buffer plugbuffer=Stdio.Buffer()->add_int32(PG_PROTOCOL(3,0));
cff3a32014-09-12Stephen R. van den Berg  if(user) plugbuffer->add("user\0")->add(user)->add_int8(0); if(database) plugbuffer->add("database\0")->add(database)->add_int8(0);
d743bf2014-11-13Stephen R. van den Berg  _options.reconnect=undefinedp(_options.reconnect) || _options.reconnect; foreach(_options
cff3a32014-09-12Stephen R. van den Berg  -(<"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);
0e7f332014-11-15Stephen R. van den Berg  ci->start()->add_hstring(plugbuffer,4,4)->sendcmd(SENDOUT);
42fc8c2014-11-03Stephen R. van den Berg  } // Do not flush at this point, PostgreSQL 9.4 disapproves
cff3a32014-09-12Stephen R. van den Berg  cancelsecret=0; #ifdef PG_DEBUG
4ef8a32014-10-29Stephen R. van den Berg  PD("Processloop\n");
cff3a32014-09-12Stephen R. van den Berg  string datarowdebug; int datarowdebugcount;
4ef8a32014-10-29Stephen R. van den Berg  void showportal(int msgtype) { if(objectp(portal))
bf75ce2014-11-15Stephen R. van den Berg  PD("%d<%O %d %c switch portal\n", ci->socket->query_fd(),portal._portalname,++ci->queueinidx,msgtype);
4ef8a32014-10-29Stephen R. van den Berg  else if(portal>0)
bf75ce2014-11-15Stephen R. van den Berg  PD("%d<Sync %d %d %c portal\n", ci->socket->query_fd(),++ci->queueinidx,portal,msgtype);
4ef8a32014-10-29Stephen R. van den Berg  };
a2014e2008-07-14Stephen R. van den Berg #endif
cff3a32014-09-12Stephen R. van den Berg  for(;;) { err=catch { #ifdef PG_DEBUG if(!portal && datarowdebug) { PD("%s rows %d\n",datarowdebug,datarowdebugcount); datarowdebug=0; datarowdebugcount=0;
a2014e2008-07-14Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg #endif int msgtype=ci->read_int8(); if(!portal) { portal=qportals->try_read();
4ef8a32014-10-29Stephen R. van den Berg #ifdef PG_DEBUG showportal(msgtype); #endif
cff3a32014-09-12Stephen R. van den Berg  } int msglen=ci->read_int32(); _msgsreceived++; _bytesreceived+=1+msglen;
b4c7e92014-11-18Stephen R. van den Berg  int errtype=NOERROR;
f2ecce2014-11-18Stephen R. van den Berg  PD("%d>",ci->socket->query_fd());
cff3a32014-09-12Stephen R. van den Berg  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)
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #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': {
f2ecce2014-11-18Stephen R. van den Berg  PD("Authentication ");
cff3a32014-09-12Stephen R. van den Berg  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);
fb8eb42014-10-31Stephen R. van den Berg  ci->gottimeout=cancelquery;
cff3a32014-09-12Stephen R. van den Berg  ci->timeout=timeout; reconnectdelay=0; cancelsecret=""; break; case 2: PD("KerberosV5\n");
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  break; case 3: PD("ClearTextPassword\n"); sendpass=pass; break; case 4: PD("CryptPassword\n");
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  break; case 5: PD("MD5Password\n"); #ifdef PG_DEBUG if(msglen<4)
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #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");
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  break; case 7: PD("GSS\n");
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  break; case 9: PD("SSPI\n");
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  break; case 8: PD("GSSContinue\n");
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
4ef8a32014-10-29Stephen R. van den Berg  cancelsecret=ci->read(msglen); // Actually SSauthdata
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_DEBUG if(msglen<1)
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg  msglen=0; #endif break; default: PD("Unknown Authentication Method %c\n",authtype);
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  break; } switch(errtype) {
b4c7e92014-11-18Stephen R. van den Berg  case NOERROR:
cff3a32014-09-12Stephen R. van den Berg  if(cancelsecret!="") ci->start()->add_int8('p')->add_hstring(sendpass,4,5)
0e7f332014-11-15Stephen R. van den Berg  ->add_int8(0)->sendcmd(SENDOUT);
42fc8c2014-11-03Stephen R. van den Berg  break; // No flushing here, PostgreSQL 9.4 disapproves
cff3a32014-09-12Stephen R. van den Berg  default:
b4c7e92014-11-18Stephen R. van den Berg  case PROTOCOLUNSUPPORTED:
cff3a32014-09-12Stephen R. van den Berg  ERROR("Unsupported authenticationmethod %c\n",authtype); break;
c8942c2009-04-10Stephen R. van den Berg  }
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  } case 'K': msglen-=4+4;backendpid=ci->read_int32(); cancelsecret=ci->read(msglen); #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("BackendKeyData %O\n",cancelsecret);
cff3a32014-09-12Stephen R. van den Berg  msglen=0; #endif
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  case 'S': {
f2ecce2014-11-18Stephen R. van den Berg  PD("ParameterStatus ");
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; array(string) ts=reads(); #ifdef PG_DEBUG if(sizeof(ts)==2) { #endif _runtimeparameter[ts[0]]=ts[1]; #ifdef PG_DEBUG
4ef8a32014-10-29Stephen R. van den Berg  PD("%O=%O\n",ts[0],ts[1]);
cff3a32014-09-12Stephen R. van den Berg  } else
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #endif
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  } case '3': #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("CloseComplete\n");
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #endif
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  case 'Z':
4ef8a32014-10-29Stephen R. van den Berg  backendstatus=ci->read_int8();
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_DEBUG msglen-=4+1;
f2ecce2014-11-18Stephen R. van den Berg  PD("ReadyForQuery %c\n",backendstatus);
4ef8a32014-10-29Stephen R. van den Berg #endif
7e85fa2014-11-10Stephen R. van den Berg  for(;objectp(portal);portal=qportals->read()) {
4ef8a32014-10-29Stephen R. van den Berg #ifdef PG_DEBUG showportal(msgtype); #endif
fb8eb42014-10-31Stephen R. van den Berg  portal->_purgeportal();
4ef8a32014-10-29Stephen R. van den Berg  }
3b31fa2014-11-14Stephen R. van den Berg  foreach(qportals->peek_array();;.pgsql_util.sql_result qp) {
a362e32014-11-03Stephen R. van den Berg  if(objectp(qp) && qp._synctransact && qp._synctransact<=portal) { PD("Checking portal %O %d<=%d\n", qp._portalname,qp._synctransact,portal);
fb8eb42014-10-31Stephen R. van den Berg  qp->_purgeportal();
a362e32014-11-03Stephen R. van den Berg  }
4ef8a32014-10-29Stephen R. van den Berg  } portal=0; _readyforquerycount--;
cff3a32014-09-12Stephen R. van den Berg  if(readyforquery_cb) readyforquery_cb(),readyforquery_cb=0;
fb8eb42014-10-31Stephen R. van den Berg  if(waitforauthready) destruct(waitforauthready);
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  case '1': #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("ParseComplete\n");
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #endif
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  case 't': { array a; int cols=ci->read_int16(); #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("%O ParameterDescription %d values\n",portal._query,cols);
cff3a32014-09-12Stephen R. van den Berg  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;
0fed8c2014-11-10Stephen R. van den Berg  callout(portal->_preparebind,0,a);
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  } case 'T': { array a; #ifdef PG_DEBUG int cols=ci->read_int16();
f2ecce2014-11-18Stephen R. van den Berg  PD("RowDescription %d columns %O\n",cols,portal._query);
cff3a32014-09-12Stephen R. van den Berg  msglen-=4+2;
fb8eb42014-10-31Stephen R. van den Berg  foreach(a=allocate(cols);int i;)
cff3a32014-09-12Stephen R. van den Berg #else
fb8eb42014-10-31Stephen R. van den Berg  foreach(a=allocate(ci->read_int16());int i;)
cff3a32014-09-12Stephen R. van den Berg #endif
fb8eb42014-10-31Stephen R. van den Berg  {
cff3a32014-09-12Stephen R. van den Berg  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(); /* formatcode contains just a zero when Bind has not been issued * yet, but the content is irrelevant because it's determined * at query time */ res.formatcode=ci->read_int16(); #else ci->consume(8); #endif a[i]=res; } #ifdef PG_DEBUGMORE PD("%O\n",a); #endif
a362e32014-11-03Stephen R. van den Berg  if(portal._forcetext) portal->_setrowdesc(a); // Do not consume queued portal else { portal->_processrowdesc(a); portal=0; }
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  } case 'n': { #ifdef PG_DEBUG msglen-=4;
f2ecce2014-11-18Stephen R. van den Berg  PD("NoData %O\n",portal._query);
4ef8a32014-10-29Stephen R. van den Berg #endif
cff3a32014-09-12Stephen R. van den Berg  portal._fetchlimit=0; // disables subsequent Executes
fb8eb42014-10-31Stephen R. van den Berg  portal->_processrowdesc(({}));
cff3a32014-09-12Stephen R. van den Berg  portal=0;
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  } case 'H':
fb8eb42014-10-31Stephen R. van den Berg  portal->_processrowdesc(getcols());
f2ecce2014-11-18Stephen R. van den Berg  PD("CopyOutResponse %O\n",portal._query);
11b13b2014-08-16Martin Nilsson  break;
cff3a32014-09-12Stephen R. van den Berg  case '2': { mapping tp; #ifdef PG_DEBUG msglen-=4;
f2ecce2014-11-18Stephen R. van den Berg  PD("%O BindComplete\n",portal._portalname);
4ef8a32014-10-29Stephen R. van den Berg #endif
cff3a32014-09-12Stephen R. van den Berg  if(tp=portal._tprepared) { int tend=gethrtime(); int tstart=tp.trun; if(tend==tstart)
fb8eb42014-10-31Stephen R. van den Berg  m_delete(_prepareds,portal._query);
cff3a32014-09-12Stephen R. van den Berg  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; } }
11b13b2014-08-16Martin Nilsson  break; }
15d4e12014-11-18Stephen R. van den Berg  case 'D':
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #ifdef PG_DEBUG
cd7f232014-03-01Martin Nilsson #ifdef PG_DEBUGMORE
f2ecce2014-11-18Stephen R. van den Berg  PD("%O DataRow %d bytes\n",portal._portalname,msglen);
a2014e2008-07-14Stephen R. van den Berg #endif
cff3a32014-09-12Stephen R. van den Berg  datarowdebugcount++; if(!datarowdebug) datarowdebug=sprintf(
f2ecce2014-11-18Stephen R. van den Berg  "%O DataRow %d bytes",portal._portalname,msglen);
a2014e2008-07-14Stephen R. van den Berg #endif
15d4e12014-11-18Stephen R. van den Berg #ifdef PG_DEBUG msglen=
7ac2f12008-07-27Stephen R. van den Berg #endif
15d4e12014-11-18Stephen R. van den Berg  portal->_decodedata(msglen,_runtimeparameter[CLIENT_ENCODING]);
cff3a32014-09-12Stephen R. van den Berg  break; case 's': #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("%O PortalSuspended\n",portal._portalname);
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #endif portal=0; break; case 'C': { msglen-=4; #ifdef PG_DEBUG if(msglen<1)
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #endif string s=ci->read(msglen-1);
15d4e12014-11-18Stephen R. van den Berg  portal->_storetiming();
f2ecce2014-11-18Stephen R. van den Berg  PD("%O CommandComplete %O\n",portal._portalname,s);
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_DEBUG if(ci->read_int8())
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg  msglen=0; #else ci->consume(1); #endif
bf20922014-11-17Stephen R. van den Berg  portal->_releasesession(s);
cff3a32014-09-12Stephen R. van den Berg  portal=0; break; } case 'I': #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("EmptyQueryResponse %O\n",portal._portalname);
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #endif portal->_releasesession(); portal=0; break; case 'd':
f2ecce2014-11-18Stephen R. van den Berg  PD("%O CopyData\n",portal._portalname);
15d4e12014-11-18Stephen R. van den Berg  portal->_storetiming();
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #ifdef PG_DEBUG if(msglen<0)
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #endif
15d4e12014-11-18Stephen R. van den Berg  portal->_processdataready(({ci->read(msglen)}),msglen);
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_DEBUG msglen=0; #endif break; case 'G':
fb8eb42014-10-31Stephen R. van den Berg  portal->_setrowdesc(getcols());
f2ecce2014-11-18Stephen R. van den Berg  PD("%O CopyInResponse\n",portal._portalname);
0e7f332014-11-15Stephen R. van den Berg  portal._state=COPYINPROGRESS;
11b13b2014-08-16Martin Nilsson  {
cff3a32014-09-12Stephen R. van den Berg  Thread.MutexKey resultlock=portal._resultmux->lock(); portal._newresult.signal(); resultlock=0;
11b13b2014-08-16Martin Nilsson  }
cff3a32014-09-12Stephen R. van den Berg  break; case 'c': #ifdef PG_DEBUG
f2ecce2014-11-18Stephen R. van den Berg  PD("%O CopyDone\n",portal._portalname);
cff3a32014-09-12Stephen R. van den Berg  msglen-=4; #endif portal=0; break; case 'E': {
4ef8a32014-10-29Stephen R. van den Berg  if(!_readyforquerycount) sendsync();
f2ecce2014-11-18Stephen R. van den Berg  PD("%O ErrorResponse %O\n",
42fc8c2014-11-03Stephen R. van den Berg  objectp(portal)&&(portal._portalname||portal._preparedname), objectp(portal)&&portal._query);
cff3a32014-09-12Stephen R. van den Berg  mapping(string:string) msgresponse; msgresponse=getresponse();
11b13b2014-08-16Martin Nilsson  warningsdropcount+=warningscollected;
cff3a32014-09-12Stephen R. van den Berg  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)));
d8be282014-11-14Stephen R. van den Berg  case "53000":case "53100":case "53200":case "53300":case "53400":
bf75ce2014-11-15Stephen R. van den Berg  case "57P01":case "57P02":case "57P03":case "57P04":case "3D000":
cff3a32014-09-12Stephen R. van den Berg  preplastmessage(msgresponse);
a362e32014-11-03Stephen R. van den Berg  PD(a2nls(lastmessage));throw(0);
cff3a32014-09-12Stephen R. van den Berg  case "08P01":case "42P05":
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg  case "XX000":case "42883":case "42P01": invalidatecache=1; default: preplastmessage(msgresponse); if(msgresponse.D) lastmessage+=({msgresponse.D}); if(msgresponse.H) lastmessage+=({msgresponse.H}); lastmessage+=({
42fc8c2014-11-03Stephen R. van den Berg  pinpointerror(objectp(portal)&&portal._query,msgresponse.P)+
cff3a32014-09-12Stephen R. van den Berg  pinpointerror(msgresponse.q,msgresponse.p)}); if(msgresponse.W) lastmessage+=({msgresponse.W});
cd158f2014-11-08Stephen R. van den Berg  if(objectp(portal)) lastmessage+=showbindings(portal);
cff3a32014-09-12Stephen R. van den Berg  switch(msgresponse.S) { case "PANIC":werror(a2nls(lastmessage)); } USERERROR(a2nls(lastmessage)); }
42fc8c2014-11-03Stephen R. van den Berg  if(objectp(portal))
4ef8a32014-10-29Stephen R. van den Berg  portal->_releasesession();
cff3a32014-09-12Stephen R. van den Berg  break; } case 'N': {
f2ecce2014-11-18Stephen R. van den Berg  PD("NoticeResponse\n");
cff3a32014-09-12Stephen R. van den Berg  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': {
f2ecce2014-11-18Stephen R. van den Berg  PD("NotificationResponse\n");
11b13b2014-08-16Martin Nilsson  msglen-=4+4;
cff3a32014-09-12Stephen R. van den Berg  int pid=ci->read_int32();
11b13b2014-08-16Martin Nilsson  string condition,extrainfo=UNDEFINED; {
cff3a32014-09-12Stephen R. van den Berg  array(string) ts=reads(); switch(sizeof(ts)) { #if PG_DEBUG case 0:
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg  break; default:
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #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
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
cff3a32014-09-12Stephen R. van den Berg  } else { lastmessage+=({ sprintf("Connection lost to database %s@%s:%d/%s %d\n", user,_host,_port,database,backendpid)});
a362e32014-11-03Stephen R. van den Berg  if(!waitforauthready) throw(0);
cff3a32014-09-12Stephen R. van den Berg  USERERROR(a2nls(lastmessage)); } break;
11b13b2014-08-16Martin Nilsson  }
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_DEBUG if(msglen)
b4c7e92014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
cff3a32014-09-12Stephen R. van den Berg #endif
11b13b2014-08-16Martin Nilsson  {
cff3a32014-09-12Stephen R. van den Berg  string msg; switch(errtype) {
b4c7e92014-11-18Stephen R. van den Berg  case PROTOCOLUNSUPPORTED:
cff3a32014-09-12Stephen R. van den Berg  msg=sprintf("Unsupported servermessage received %c\n",msgtype); break;
b4c7e92014-11-18Stephen R. van den Berg  case PROTOCOLERROR:
cff3a32014-09-12Stephen R. van den Berg  msg=sprintf("Protocol error with database %s",host_info()); break;
b4c7e92014-11-18Stephen R. van den Berg  case NOERROR:
cff3a32014-09-12Stephen R. van den Berg  continue; // Normal production loop } ERROR(a2nls(lastmessage+=({msg})));
11b13b2014-08-16Martin Nilsson  }
cff3a32014-09-12Stephen R. van den Berg  }; // We only get here if there is an error
3b31fa2014-11-14Stephen R. van den Berg  if(err==MAGICTERMINATE) { // Announce connection termination to server
0e7f332014-11-15Stephen R. van den Berg  ci->start()->add("X\0\0\0\4")->sendcmd(SENDOUT);
cff3a32014-09-12Stephen R. van den Berg  terminating=1; if(!sizeof(ci)) break;
a2014e2008-07-14Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  if(stringp(err)) {
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.sql_result or;
a362e32014-11-03Stephen R. van den Berg  if(!objectp(or=portal))
4ef8a32014-10-29Stephen R. van den Berg  or=this; if(!or._delayederror) or._delayederror=err;
a362e32014-11-03Stephen R. van den Berg  if(objectp(portal))
4ef8a32014-10-29Stephen R. van den Berg  portal->_releasesession(); portal=0;
68149e2014-11-14Stephen R. van den Berg  if(!waitforauthready) continue; // Only continue if authentication did not fail
a2014e2008-07-14Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  break;
a2014e2008-07-14Stephen R. van den Berg  }
d8be282014-11-14Stephen R. van den Berg  PD("Closing database processloop %O\n",err);
cff3a32014-09-12Stephen R. van den Berg  _delayederror=err;
d8be282014-11-14Stephen R. van den Berg  for(;objectp(portal);portal=qportals->read()) if(objectp(portal)) { #ifdef PG_DEBUG showportal(0); #endif portal->_purgeportal(); }
d743bf2014-11-13Stephen R. van den Berg  if(!ci->close() && !terminating && _options.reconnect)
cff3a32014-09-12Stephen R. van den Berg  _connectfail();
68149e2014-11-14Stephen R. van den Berg  else if(waitforauthready) destruct(waitforauthready); if(err && !stringp(err))
a362e32014-11-03Stephen R. van den Berg  throw(err);
a2014e2008-07-14Stephen R. van den Berg }
cb26232008-08-04Stephen R. van den Berg //! Closes the connection to the database, any running queries are //! terminated instantly. //!
2c228b2009-02-15Stephen R. van den Berg //! @note
cb26232008-08-04Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg void close() {
bf75ce2014-11-15Stephen R. van den Berg  catch(cancelquery()); catch(c->sendterminate());
cff3a32014-09-12Stephen R. van den Berg  c=0;
bf75ce2014-11-15Stephen R. van den Berg  if(waitforauthready) destruct(waitforauthready);
a2014e2008-07-14Stephen R. van den Berg }
6eb5942014-11-12Stephen R. van den Berg protected void destroy() {
bf75ce2014-11-15Stephen R. van den Berg  catch(close());
cff3a32014-09-12Stephen R. van den Berg  .pgsql_util.unregister_backend();
cb26232008-08-04Stephen R. van den Berg }
cff3a32014-09-12Stephen R. van den Berg void _connectfail(void|mixed err) {
a362e32014-11-03Stephen R. van den Berg  PD("Connect failed %O reconnectdelay %d\n",err,reconnectdelay);
bf75ce2014-11-15Stephen R. van den Berg  catch { if(waitforauthready) destruct(waitforauthready); };
cff3a32014-09-12Stephen R. van den Berg  if(!err || reconnectdelay) { int tdelay; switch(tdelay=reconnectdelay) { case 0: reconnectdelay=RECONNECTDELAY; break; default:
a362e32014-11-03Stephen R. van den Berg  if(err) _delayederror=err;
d743bf2014-11-13Stephen R. van den Berg  if(_options.reconnect!=-1)
cff3a32014-09-12Stephen R. van den Berg  return; reconnectdelay=RECONNECTBACKOFF; break; }
4e0be82014-11-18Stephen R. van den Berg  Thread.MutexKey lock=_shortmux->lock();
cff3a32014-09-12Stephen R. van den Berg  if(!waitforauthready) waitforauthready=Thread.Condition(); lock=0;
a362e32014-11-03Stephen R. van den Berg  PD("Schedule reconnect in %ds\n",tdelay); _delayederror=0;
0fed8c2014-11-10Stephen R. van den Berg  callout(reconnect,tdelay,1);
a362e32014-11-03Stephen R. van den Berg  } else if(err) _delayederror=err;
cff3a32014-09-12Stephen R. van den Berg }
6eb5942014-11-12Stephen R. van den Berg private int reconnect(void|int force) {
a362e32014-11-03Stephen R. van den Berg  PD("(Re)connect\n");
cff3a32014-09-12Stephen R. van den Berg  if(!force) {
4e0be82014-11-18Stephen R. van den Berg  Thread.MutexKey lock=_shortmux->lock();
cff3a32014-09-12Stephen R. van den Berg  if(waitforauthready) { lock=0; return 0; // Connect still in progress in other thread } waitforauthready=Thread.Condition(); lock=0; } if(c) {
a362e32014-11-03Stephen R. van den Berg  PD("Close old connection\n");
11b13b2014-08-16Martin Nilsson  reconnected++;
4ef8a32014-10-29Stephen R. van den Berg #ifdef PG_STATS
cb26232008-08-04Stephen R. van den Berg  prepstmtused=0;
4ef8a32014-10-29Stephen R. van den Berg #endif
a2014e2008-07-14Stephen R. van den Berg  if(!force)
cff3a32014-09-12Stephen R. van den Berg  c->sendterminate();
49dbce2011-10-03Henrik Grubbström (Grubba)  else
cff3a32014-09-12Stephen R. van den Berg  c->close(); c=0;
a362e32014-11-03Stephen R. van den Berg  PD("Flushing old cache\n");
fb8eb42014-10-31Stephen R. van den Berg  foreach(_prepareds;;mapping tp)
cb26232008-08-04Stephen R. van den Berg  m_delete(tp,"preparedname");
d743bf2014-11-13Stephen R. van den Berg  if(!_options.reconnect)
cff3a32014-09-12Stephen R. van den Berg  return 0;
6d9aa52008-08-21Stephen R. van den Berg  }
a362e32014-11-03Stephen R. van den Berg  PD("Actually start to connect\n");
cff3a32014-09-12Stephen R. van den Berg  qportals=Thread.Queue();
d743bf2014-11-13Stephen R. van den Berg  _readyforcommit=Thread.Condition();
4ef8a32014-10-29Stephen R. van den Berg  _readyforquerycount=1;
d743bf2014-11-13Stephen R. van den Berg  _waittocommit=0;
4ef8a32014-10-29Stephen R. van den Berg  qportals->write(1);
cff3a32014-09-12Stephen R. van den Berg  if(!(c=getsocket())) { string msg=sprintf("Couldn't connect to database on %s:%d",_host,_port); if(force) {
11b13b2014-08-16Martin Nilsson  if(!sizeof(lastmessage) || lastmessage[sizeof(lastmessage)-1]!=msg)
6b4ea82012-04-10Stephen R. van den Berg  lastmessage+=({msg});
6d9aa52008-08-21Stephen R. van den Berg  return 0;
cff3a32014-09-12Stephen R. van den Berg  } else
be07392009-02-01Stephen R. van den Berg  ERROR(msg+"\n");
a2014e2008-07-14Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  _runtimeparameter=([]);
fb8eb42014-10-31Stephen R. van den Berg  _unnamedportalmux=Thread.Mutex();
cff3a32014-09-12Stephen R. van den Berg  unnamedstatement=Thread.Mutex();
fb8eb42014-10-31Stephen R. van den Berg  readyforquery_cb=force?reconnect_cb:connect_cb;
cff3a32014-09-12Stephen R. van den Berg  _portalsinflight=0;
53b3792010-03-23Stephen R. van den Berg  return 1;
a2014e2008-07-14Stephen R. van den Berg } //! @decl void reload() //!
e7621f2009-02-02Stephen R. van den Berg //! For PostgreSQL this function performs the same function as @[resync()].
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[resync()], @[cancelquery()]
cff3a32014-09-12Stephen R. van den Berg void reload() {
11b13b2014-08-16Martin Nilsson  resync();
e7621f2009-02-02Stephen R. van den Berg }
c6e93f2014-11-13Stephen R. van den Berg private void reset_dbsession() { big_query("ROLLBACK"); big_query("RESET ALL"); big_query("CLOSE ALL"); big_query("DISCARD TEMP"); }
6eb5942014-11-12Stephen R. van den Berg private void resync_cb() {
cff3a32014-09-12Stephen R. van den Berg  switch(backendstatus) { case 'T':case 'E':
fb8eb42014-10-31Stephen R. van den Berg  foreach(_prepareds;;mapping tp) {
cff3a32014-09-12Stephen R. van den Berg  m_delete(tp,"datatypeoid"); m_delete(tp,"datarowdesc"); }
c6e93f2014-11-13Stephen R. van den Berg  Thread.Thread(reset_dbsession); // Urgently and deadlockfree
cff3a32014-09-12Stephen R. van den Berg  } }
6eb5942014-11-12Stephen R. van den Berg private void sendsync() {
4ef8a32014-10-29Stephen R. van den Berg  _readyforquerycount++;
0e7f332014-11-15Stephen R. van den Berg  c->start()->sendcmd(SYNCSEND);
4ef8a32014-10-29Stephen R. van den Berg }
e7621f2009-02-02Stephen R. van den Berg //! @decl void resync() //! //! Resyncs the database session; typically used to make sure the session is //! not still in a dangling transaction. //! //! If called while the connection is in idle state, the function is //! lightweight and briefly touches base with the database server to //! make sure client and server are in sync. //! //! If issued while inside a transaction, it will rollback the transaction, //! close all open cursors, drop all temporary tables and reset all //! session variables to their default values. //!
2c228b2009-02-15Stephen R. van den Berg //! @seealso //! @[cancelquery()], @[reload()] //! //! @note
e7621f2009-02-02Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
c960042014-11-09Stephen R. van den Berg void resync() {
11b13b2014-08-16Martin Nilsson  mixed err;
cff3a32014-09-12Stephen R. van den Berg  if(!is_open()&&!reconnect()) ERROR(a2nls(lastmessage)); err = catch { PD("Portalsinflight: %d\n",_portalsinflight);
bf75ce2014-11-15Stephen R. van den Berg  if(!waitforauthready) { readyforquery_cb=resync_cb; sendsync(); }
cff3a32014-09-12Stephen R. van den Berg  return; }; PD("%O\n",err); if(!reconnect())
6b81f42012-04-10Stephen R. van den Berg  ERROR(a2nls(lastmessage));
a2014e2008-07-14Stephen R. van den Berg } //! This function allows you to connect to a database. Due to //! restrictions of the Postgres frontend-backend protocol, you always //! have to be connected to a database, so in fact this function just //! allows you to connect to a different database on the same server. //! //! @note //! This function @b{can@} raise exceptions if something goes wrong
f4c9d62009-02-15Stephen R. van den Berg //! (backend process not running, insufficient privileges...)
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[create()]
cff3a32014-09-12Stephen R. van den Berg void select_db(string dbname) {
11b13b2014-08-16Martin Nilsson  database=dbname;
a2014e2008-07-14Stephen R. van den Berg  reconnect();
b133b02008-08-06Stephen R. van den Berg  reconnected=0;
a2014e2008-07-14Stephen R. van den Berg } //! With PostgreSQL you can LISTEN to NOTIFY events. //! This function allows you to detect and handle such events. //! //! @param condition //! Name of the notification event we're listening //! to. A special case is the empty string, which matches all events, //! and can be used as fallback function which is called only when the
b133b02008-08-06Stephen R. van den Berg //! specific condition is not handled. Another special case is
9a3d002011-01-09Henrik Grubbström (Grubba) //! @expr{"_reconnect"@} which gets called whenever the connection //! unexpectedly drops and reconnects to the database.
a2014e2008-07-14Stephen R. van den Berg //! //! @param notify_cb //! Function to be called on receiving a notification-event of
f4c9d62009-02-15Stephen R. van den Berg //! condition @ref{condition@}.
a2014e2008-07-14Stephen R. van den Berg //! The callback function is invoked with
c0f2b72009-04-10Stephen R. van den Berg //! @expr{void notify_cb(pid,condition,extrainfo, .. args);@}
9a3d002011-01-09Henrik Grubbström (Grubba) //! @tt{pid@} is the process id of the database session that originated //! the event. @tt{condition@} contains the current condition. //! @tt{extrainfo@} contains optional extra information specified by
a2014e2008-07-14Stephen R. van den Berg //! the database.
f4c9d62009-02-15Stephen R. van den Berg //! The rest of the arguments to @ref{notify_cb@} are passed //! verbatim from @ref{args@}.
a2014e2008-07-14Stephen R. van den Berg //! The callback function must return no value.
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @param selfnotify //! Normally notify events generated by your own session are ignored.
f4c9d62009-02-15Stephen R. van den Berg //! If you want to receive those as well, set @ref{selfnotify@} to one.
7ac2f12008-07-27Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @param args
f4c9d62009-02-15Stephen R. van den Berg //! Extra arguments to pass to @ref{notify_cb@}.
7ac2f12008-07-27Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface. void set_notify_callback(string condition, void|function(int,string,string,mixed ...:void) notify_cb,void|int selfnotify,
cff3a32014-09-12Stephen R. van den Berg  mixed ... args) {
11b13b2014-08-16Martin Nilsson  if(!notify_cb)
a2014e2008-07-14Stephen R. van den Berg  m_delete(notifylist,condition);
cff3a32014-09-12Stephen R. van den Berg  else {
11b13b2014-08-16Martin Nilsson  array old=notifylist[condition];
a2014e2008-07-14Stephen R. van den Berg  if(!old) old=({notify_cb}); if(selfnotify||args) old+=({selfnotify}); if(args) old+=args; notifylist[condition]=old; } }
6eb5942014-11-12Stephen R. van den Berg private void runcallback(int pid,string condition,string extrainfo) {
11b13b2014-08-16Martin Nilsson  array cb;
b133b02008-08-06Stephen R. van den Berg  if((cb=notifylist[condition]||notifylist[""])
11b13b2014-08-16Martin Nilsson  && (pid!=backendpid || sizeof(cb)>1 && cb[1]))
0fed8c2014-11-10Stephen R. van den Berg  callout(cb[0],0,pid,condition,extrainfo,@cb[2..]);
b133b02008-08-06Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @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.
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[big_query()], @[quotebinary()], @[create()]
cff3a32014-09-12Stephen R. van den Berg string quote(string s) {
6932232014-11-09Stephen R. van den Berg  waitauthready();
cff3a32014-09-12Stephen R. van den Berg  string r=_runtimeparameter.standard_conforming_strings;
a2014e2008-07-14Stephen R. van den Berg  if(r && r=="on") return replace(s, "'", "''"); return replace(s, ({ "'", "\\" }), ({ "''", "\\\\" }) ); }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! The given string, but escapes/quotes all contained magic characters
f4c9d62009-02-15Stephen R. van den Berg //! for binary (bytea) arguments in textual SQL-queries.
2c228b2009-02-15Stephen R. van den Berg //! //! @note //! Quoting must not be done for parameters passed in bindings.
cb26232008-08-04Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[big_query()], @[quote()]
cb26232008-08-04Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
cb26232008-08-04Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg string quotebinary(string s) {
11b13b2014-08-16Martin Nilsson  return replace(s, ({ "'", "\\", "\0" }), ({ "''", "\\\\", "\\000" }) );
cb26232008-08-04Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! This function creates a new database (assuming we
f4c9d62009-02-15Stephen R. van den Berg //! have sufficient privileges to do this).
2c228b2009-02-15Stephen R. van den Berg //! //! @param db //! Name of the new database.
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[drop_db()]
cff3a32014-09-12Stephen R. van den Berg void create_db(string db) {
71fc542014-08-13Stephen R. van den Berg  big_query(sprintf("CREATE DATABASE %s",db));
a2014e2008-07-14Stephen R. van den Berg } //! This function destroys a database and all the data it contains (assuming
f4c9d62009-02-15Stephen R. van den Berg //! we have sufficient privileges to do so). It is not possible to delete
cb26232008-08-04Stephen R. van den Berg //! the database you're currently connected to. You can connect to database
9a3d002011-01-09Henrik Grubbström (Grubba) //! @expr{"template1"@} to avoid connecting to any live database.
2c228b2009-02-15Stephen R. van den Berg //! //! @param db //! Name of the database to be deleted.
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[create_db()]
cff3a32014-09-12Stephen R. van den Berg void drop_db(string db) {
71fc542014-08-13Stephen R. van den Berg  big_query(sprintf("DROP DATABASE %s",db));
a2014e2008-07-14Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! A string describing the server we are
a2014e2008-07-14Stephen R. van den Berg //! 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.
f28c0d2008-08-01Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[host_info()]
cff3a32014-09-12Stephen R. van den Berg string server_info () {
6932232014-11-09Stephen R. van den Berg  waitauthready();
cff3a32014-09-12Stephen R. van den Berg  return DRIVERNAME"/"+(_runtimeparameter.server_version||"unknown");
a2014e2008-07-14Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! An array of the databases available on the server. //! //! @param glob //! If specified, list only those databases matching it.
cff3a32014-09-12Stephen R. van den Berg array(string) list_dbs (void|string glob) {
11b13b2014-08-16Martin Nilsson  array row,ret=({});
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.sql_result res=big_query("SELECT d.datname "
ab24362014-11-10Stephen R. van den Berg  "FROM pg_database d " "WHERE d.datname ILIKE :glob " "ORDER BY d.datname", ([":glob":glob2reg(glob)]));
c0f2b72009-04-10Stephen R. van den Berg  while(row=res->fetch_row()) ret+=({row[0]}); return ret;
a2014e2008-07-14Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! An array containing the names of all the tables and views in the
a2014e2008-07-14Stephen R. van den Berg //! path in the currently selected database.
2c228b2009-02-15Stephen R. van den Berg //! //! @param glob //! If specified, list only the tables with matching names.
cff3a32014-09-12Stephen R. van den Berg array(string) list_tables (void|string glob) {
11b13b2014-08-16Martin Nilsson  array row,ret=({}); // This query might not work on PostgreSQL 7.4
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.sql_result res=big_query( // due to missing schemasupport
c0f2b72009-04-10Stephen R. van den Berg  "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;
a2014e2008-07-14Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! A mapping, indexed on the column name, of mappings describing
a2014e2008-07-14Stephen R. van den Berg //! the attributes of a table of the current database. //! The currently defined fields are: //! //! @mapping
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "schema"
c0f2b72009-04-10Stephen R. van den Berg //! Schema the table belongs to
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "table"
c0f2b72009-04-10Stephen R. van den Berg //! Name of the table
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "kind"
c0f2b72009-04-10Stephen R. van den Berg //! Type of table
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "owner"
c0f2b72009-04-10Stephen R. van den Berg //! Tableowner
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "rowcount"
c0f2b72009-04-10Stephen R. van den Berg //! Estimated rowcount of the table
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "datasize"
c0f2b72009-04-10Stephen R. van den Berg //! Estimated total datasize of the table in bytes
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "indexsize"
c0f2b72009-04-10Stephen R. van den Berg //! Estimated total indexsize of the table in bytes
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "name"
c0f2b72009-04-10Stephen R. van den Berg //! Name of the column
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "type"
c0f2b72009-04-10Stephen R. van den Berg //! A textual description of the internal (to the server) column type-name
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "typeoid"
c0f2b72009-04-10Stephen R. van den Berg //! The OID of the internal (to the server) column type
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member string "length"
c0f2b72009-04-10Stephen R. van den Berg //! Size of the columndatatype
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member mixed "default"
c0f2b72009-04-10Stephen R. van den Berg //! Default value for the column
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "is_shared" //! @member int "has_index"
c0f2b72009-04-10Stephen R. van den Berg //! If the table has any indices
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "has_primarykey"
c0f2b72009-04-10Stephen R. van den Berg //! If the table has a primary key
a2014e2008-07-14Stephen R. van den Berg //! @endmapping //!
2c228b2009-02-15Stephen R. van den Berg //! @param glob //! If specified, list only the tables with matching names. //! Setting it to @expr{*@} will include system columns in the list.
cff3a32014-09-12Stephen R. van den Berg array(mapping(string:mixed)) list_fields(void|string table, void|string glob) {
11b13b2014-08-16Martin Nilsson  array row, ret=({});
a2014e2008-07-14Stephen R. van den Berg  string schema=UNDEFINED; sscanf(table||"*", "%s.%s", schema, table);
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.sql_result res = big_typed_query(
a2014e2008-07-14Stephen R. van den Berg  "SELECT a.attname, a.atttypid, t.typname, a.attlen, "
98a7762009-02-22Stephen R. van den Berg  " 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, "
a2014e2008-07-14Stephen R. van den Berg  " 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' "
7ac2f12008-07-27Stephen R. van den Berg  " WHEN 't' THEN 'toastable' " // pun intended :-)
a2014e2008-07-14Stephen R. van den Berg  " WHEN 'c' THEN 'composite' " " ELSE c.relkind::TEXT END AS relkind, " " r.rolname "
98a7762009-02-22Stephen R. van den Berg  "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 "
a2014e2008-07-14Stephen R. van den Berg  "WHERE c.relname ILIKE :table AND " " (n.nspname ILIKE :schema OR " " :schema IS NULL "
98a7762009-02-22Stephen R. van den Berg  " AND n.nspname<>'pg_catalog' AND n.nspname !~ '^pg_toast') "
2c228b2009-02-15Stephen R. van den Berg  " AND a.attname ILIKE :glob " " AND (a.attnum>0 OR '*'=:realglob) "
a2014e2008-07-14Stephen R. van den Berg  "ORDER BY n.nspname,c.relname,a.attnum,a.attname", ([":schema":glob2reg(schema),":table":glob2reg(table),
11b13b2014-08-16Martin Nilsson  ":glob":glob2reg(glob),":realglob":glob]));
a2014e2008-07-14Stephen R. van den Berg  array colnames=res->fetch_fields();
11b13b2014-08-16Martin Nilsson  { mapping(string:string) renames=([
a2014e2008-07-14Stephen R. van den Berg  "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", ]);
cff3a32014-09-12Stephen R. van den Berg  foreach(colnames;int i;mapping m) { string nf,field=m.name;
a2014e2008-07-14Stephen R. van den Berg  if(nf=renames[field])
c0f2b72009-04-10Stephen R. van den Berg  field=nf;
a2014e2008-07-14Stephen R. van den Berg  colnames[i]=field; } } #define delifzero(m,field) if(!(m)[field]) m_delete(m,field)
cff3a32014-09-12Stephen R. van den Berg  while(row=res->fetch_row()) {
11b13b2014-08-16Martin Nilsson  mapping m=mkmapping(colnames,row);
a2014e2008-07-14Stephen R. van den Berg  delifzero(m,"is_shared"); delifzero(m,"has_index"); delifzero(m,"has_primarykey"); delifzero(m,"default"); ret+=({m}); } return ret; }
6eb5942014-11-12Stephen R. van den Berg private string trbackendst(int c) {
cff3a32014-09-12Stephen R. van den Berg  switch(c) { case 'I': return "idle"; case 'T': return "intransaction"; case 'E': return "infailedtransaction";
a2014e2008-07-14Stephen R. van den Berg  }
9287132008-09-03Stephen R. van den Berg  return "";
a2014e2008-07-14Stephen R. van den Berg }
2c228b2009-02-15Stephen R. van den Berg //! @returns //! The current commitstatus of the connection. Returns either one of: //! @string
f4c9d62009-02-15Stephen R. van den Berg //! @value idle //! @value intransaction //! @value infailedtransaction
2c228b2009-02-15Stephen R. van den Berg //! @endstring
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
a2014e2008-07-14Stephen R. van den Berg //! This function is PostgreSQL-specific, and thus it is not available //! through the generic SQL-interface.
cff3a32014-09-12Stephen R. van den Berg final string status_commit() {
11b13b2014-08-16Martin Nilsson  return trbackendst(backendstatus);
a2014e2008-07-14Stephen R. van den Berg }
6eb5942014-11-12Stephen R. van den Berg private inline void closestatement(
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.conxion|.pgsql_util.bufcon plugbuffer,string oldprep) {
fb8eb42014-10-31Stephen R. van den Berg  .pgsql_util.closestatement(plugbuffer,oldprep); }
6eb5942014-11-12Stephen R. van den Berg private inline string int2hex(int i) {
fb8eb42014-10-31Stephen R. van den Berg  return String.int2hex(i);
cff3a32014-09-12Stephen R. van den Berg }
6eb5942014-11-12Stephen R. van den Berg private inline void throwdelayederror(object parent) {
cff3a32014-09-12Stephen R. van den Berg  .pgsql_util.throwdelayederror(parent);
d1a5a42008-08-27Stephen R. van den Berg }
3b31fa2014-11-14Stephen R. van den Berg //! @decl Sql.pgsql_util.sql_result big_query(string query) //! @decl Sql.pgsql_util.sql_result big_query(string query, mapping bindings)
0412962009-01-19Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! This is the only provided interface which allows you to query the
f4c9d62009-02-15Stephen R. van den Berg //! database. If you wish to use the simpler @[Sql.Sql()->query()] function, //! you need to use the @[Sql.Sql] generic SQL-object.
a2014e2008-07-14Stephen R. van den Berg //!
f4c9d62009-02-15Stephen R. van den Berg //! Bindings are supported natively straight across the network.
2c228b2009-02-15Stephen R. van den Berg //! Special bindings supported are: //! @mapping
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int ":_cache"
f4c9d62009-02-15Stephen R. van den Berg //! Forces caching on or off for the query at hand.
dc97bc2012-04-06Stephen R. van den Berg //! @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.
842f4e2012-04-10Stephen R. van den Berg //! Note that this mode causes all but the first query result of a list //! of semicolon separated statements to be discarded.
7e85fa2014-11-10Stephen R. van den Berg //! @member int ":_sync" //! Forces synchronous parsing on or off for statements. //! Setting this to off can cause surprises because statements could //! be parsed before the previous statements have been executed. //! This can speed up parsing by increased parallelism.
2c228b2009-02-15Stephen R. van den Berg //! @endmapping //!
0f8dde2014-11-15Stephen R. van den Berg //! @note //! The bindings-parameter passed to this function must remain unaltered //! until the parameters have been sent to the database. The driver //! currently does not expose this moment, but to avoid a race condition //! it is sufficient to keep them unaltered until the first resultrow //! has been fetched (or EOF is reached, in case of no resultrows). //!
2c228b2009-02-15Stephen R. van den Berg //! @returns
3b31fa2014-11-14Stephen R. van den Berg //! A @[Sql.pgsql_util.sql_result] object (which conforms to the
f4c9d62009-02-15Stephen R. van den Berg //! @[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
a2014e2008-07-14Stephen R. van den Berg //! data (it's harder to handle, but fetches results on demand). //! //! @note //! This function @b{can@} raise exceptions. //!
17b7532008-07-30Stephen R. van den Berg //! @note
2c228b2009-02-15Stephen R. van den Berg //! This function supports multiple simultaneous queries (portals) on a single //! database connection. This is a feature not commonly supported by other //! database backends. //! //! @note
dc97bc2012-04-06Stephen R. van den Berg //! This function, by default, does not support multiple queries in one //! querystring.
17b7532008-07-30Stephen R. van den Berg //! I.e. it allows for but does not require a trailing semicolon, but it
f4c9d62009-02-15Stephen R. van den Berg //! simply ignores any commands after the first unquoted semicolon. This can //! be viewed as a limited protection against SQL-injection attacks.
dc97bc2012-04-06Stephen R. van den Berg //! To make it support multiple queries in one querystring, use the
cff3a32014-09-12Stephen R. van den Berg //! @ref{:_text@} option.
17b7532008-07-30Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @seealso
f4c9d62009-02-15Stephen R. van den Berg //! @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result],
3b31fa2014-11-14Stephen R. van den Berg //! @[Sql.Sql()->query()], @[Sql.pgsql_util.sql_result] .pgsql_util.sql_result big_query(string q,
ab24362014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings, void|int _alltyped) {
cff3a32014-09-12Stephen R. van den Berg  throwdelayederror(this);
11b13b2014-08-16Martin Nilsson  string preparedname="";
d743bf2014-11-13Stephen R. van den Berg  int forcecache=-1, forcetext=_options.text_query; int syncparse=zero_type(_options.sync_parse)?-1:_options.sync_parse;
6932232014-11-09Stephen R. van den Berg  if(waitforauthready) waitauthready();
26c0ad2009-02-28Stephen R. van den Berg  string cenc=_runtimeparameter[CLIENT_ENCODING];
cff3a32014-09-12Stephen R. van den Berg  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);
26c0ad2009-02-28Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  array(string|int) paramValues;
c8942c2009-04-10Stephen R. van den Berg  array from;
cff3a32014-09-12Stephen R. van den Berg  if(bindings) {
a362e32014-11-03Stephen R. van den Berg  if(forcetext) q = .sql_util.emulate_bindings(q, bindings, this), paramValues=({}); else { 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)) { // 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;
e363e22014-11-10Stephen R. van den Berg  case ":_sync":
7e85fa2014-11-10Stephen R. van den Berg  syncparse=(int)value; break;
a362e32014-11-03Stephen R. van den Berg  } 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 else { paramValues[pi++]=value; rval=sprintf("$%d",pi); } to[rep++]=rval;
a2014e2008-07-14Stephen R. van den Berg  }
a362e32014-11-03Stephen R. van den Berg  if(rep--) q=replace(q,from=from[..rep],to=to[..rep]); paramValues= pi ? paramValues[..pi-1] : ({}); from=({from,to,paramValues});
a2014e2008-07-14Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  } else
a362e32014-11-03Stephen R. van den Berg  paramValues=({});
26c0ad2009-02-28Stephen R. van den Berg  if(String.width(q)>8) ERROR("Wide string literals in %O not supported\n",q);
8a4ac42009-01-24Stephen R. van den Berg  if(has_value(q,"\0")) ERROR("Querystring %O contains invalid literal nul-characters\n",q);
cb26232008-08-04Stephen R. van den Berg  mapping(string:mixed) tp;
a2014e2008-07-14Stephen R. van den Berg  int tstart;
a362e32014-11-03Stephen R. van den Berg  if(!forcetext && forcecache==1
fb8eb42014-10-31Stephen R. van den Berg  || forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || .pgsql_util.cachealways[q])) {
cff3a32014-09-12Stephen R. van den Berg  object plugbuffer=c->start();
fb8eb42014-10-31Stephen R. van den Berg  if(tp=_prepareds[q]) {
4ef8a32014-10-29Stephen R. van den Berg  if(tp.preparedname) { #ifdef PG_STATS prepstmtused++; #endif preparedname=tp.preparedname; } else if((tstart=tp.trun)
cff3a32014-09-12Stephen R. van den Berg  && tp.tparse*FACTORPLAN>=tstart
d743bf2014-11-13Stephen R. van den Berg  && (undefinedp(_options.cache_autoprepared_statements) || _options.cache_autoprepared_statements))
fb8eb42014-10-31Stephen R. van den Berg  preparedname=PREPSTMTPREFIX+int2hex(pstmtcount++);
cff3a32014-09-12Stephen R. van den Berg  } else {
11b13b2014-08-16Martin Nilsson  if(totalhits>=cachedepth)
fb8eb42014-10-31Stephen R. van den Berg  foreach(_prepareds;string ind;tp) {
cff3a32014-09-12Stephen R. van den Berg  int oldhits=tp.hits; totalhits-=oldhits-(tp.hits=oldhits>>1); if(oldhits<=1) { closestatement(plugbuffer,tp.preparedname);
fb8eb42014-10-31Stephen R. van den Berg  m_delete(_prepareds,ind);
a2014e2008-07-14Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  }
fb8eb42014-10-31Stephen R. van den Berg  if(forcecache!=1 && .pgsql_util.createprefix->match(q)) {
a362e32014-11-03Stephen R. van den Berg  invalidatecache=1; // Flush cache on CREATE
36b5732014-11-13Stephen R. van den Berg  pstmtcount=0; // Reset prepared statementnos
11b13b2014-08-16Martin Nilsson  tp=UNDEFINED;
cff3a32014-09-12Stephen R. van den Berg  } else
fb8eb42014-10-31Stephen R. van den Berg  _prepareds[q]=tp=([]);
a2014e2008-07-14Stephen R. van den Berg  }
cff3a32014-09-12Stephen R. van den Berg  if(invalidatecache) {
11b13b2014-08-16Martin Nilsson  invalidatecache=0;
fb8eb42014-10-31Stephen R. van den Berg  foreach(_prepareds;;mapping np) {
cff3a32014-09-12Stephen R. van den Berg  closestatement(plugbuffer,np.preparedname);
c0f2b72009-04-10Stephen R. van den Berg  m_delete(np,"preparedname");
d1a5a42008-08-27Stephen R. van den Berg  } }
cff3a32014-09-12Stephen R. van den Berg  if(sizeof(plugbuffer)) { PD("%O\n",(string)plugbuffer);
0e7f332014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(FLUSHSEND); // close expireds
cff3a32014-09-12Stephen R. van den Berg  } else
0e7f332014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(KEEP); // close start()
a2014e2008-07-14Stephen R. van den Berg  tstart=gethrtime();
3b31fa2014-11-14Stephen R. van den Berg  } else // sql_result autoassigns to portal
cb26232008-08-04Stephen R. van den Berg  tp=UNDEFINED;
3b31fa2014-11-14Stephen R. van den Berg  .pgsql_util.sql_result portal; portal=.pgsql_util.sql_result(this,c,q,
cff3a32014-09-12Stephen R. van den Berg  portalbuffersize,_alltyped,from,forcetext); portal._tprepared=tp; #ifdef PG_STATS portalsopened++; #endif clearmessage=1;
4ef8a32014-10-29Stephen R. van den Berg  if(forcetext) { // FIXME What happens if portals are still open?
fb8eb42014-10-31Stephen R. van den Berg  portal._unnamedportalkey=_unnamedportalmux->lock(1);
a362e32014-11-03Stephen R. van den Berg  portal._portalname="";
cff3a32014-09-12Stephen R. van den Berg  portal->_openportal();
4ef8a32014-10-29Stephen R. van den Berg  _readyforquerycount++; Thread.MutexKey lock=unnamedstatement->lock(1);
a362e32014-11-03Stephen R. van den Berg  c->start(1)->add_int8('Q')->add_hstring(q,4,4+1)->add_int8(0)
0e7f332014-11-15Stephen R. van den Berg  ->sendcmd(FLUSHLOGSEND,portal);
4ef8a32014-10-29Stephen R. van den Berg  lock=0;
cff3a32014-09-12Stephen R. van den Berg  PD("Simple query: %O\n",q); } else {
a362e32014-11-03Stephen R. van den Berg  object plugbuffer;
cff3a32014-09-12Stephen R. van den Berg  if(!sizeof(preparedname) || !tp || !tp.preparedname) { if(!sizeof(preparedname)) preparedname=
7e85fa2014-11-10Stephen R. van den Berg  (portal._unnamedstatementkey= (syncparse?unnamedstatement->lock:unnamedstatement->trylock)(1))
fb8eb42014-10-31Stephen R. van den Berg  ? "" : PTSTMTPREFIX+int2hex(ptstmtcount++);
f156a62014-11-13Stephen R. van den Berg  else if(syncparse) portal._unnamedstatementkey=unnamedstatement->lock(1);
cff3a32014-09-12Stephen R. van den Berg  // 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 PD("Parse statement %O=%O\n",preparedname,q);
a362e32014-11-03Stephen R. van den Berg  plugbuffer=c->start()->add_int8('P')
cff3a32014-09-12Stephen R. van den Berg  ->add_hstring(({preparedname,0,q,"\0\0\0"}),4,4)->add(PGFLUSH); }
061af32014-11-18Stephen R. van den Berg  portal._preparedname=preparedname;
cff3a32014-09-12Stephen R. van den Berg  if(!tp || !tp.datatypeoid) { PD("Describe statement %O\n",preparedname);
a362e32014-11-03Stephen R. van den Berg  (plugbuffer||c->start())->add_int8('D')
0e7f332014-11-15Stephen R. van den Berg  ->add_hstring(({'S',preparedname,0}),4,4)->sendcmd(FLUSHSEND,portal);
cff3a32014-09-12Stephen R. van den Berg  } else {
a362e32014-11-03Stephen R. van den Berg  if(plugbuffer)
0e7f332014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(KEEP);
cff3a32014-09-12Stephen R. van den Berg #ifdef PG_STATS skippeddescribe++; #endif
fb8eb42014-10-31Stephen R. van den Berg  portal->_setrowdesc(tp.datarowdesc);
cff3a32014-09-12Stephen R. van den Berg  } if((portal._tprepared=tp) && tp.datatypeoid) {
42fc8c2014-11-03Stephen R. van den Berg  mixed e=catch(portal->_preparebind(tp.datatypeoid));
a362e32014-11-03Stephen R. van den Berg  if(e && !portal._delayederror) throw(e);
cff3a32014-09-12Stephen R. van den Berg  } } throwdelayederror(portal); return portal; }
a2014e2008-07-14Stephen R. van den Berg //! This is an alias for @[big_query()], since @[big_query()] already supports //! streaming of multiple simultaneous queries through the same connection. //! //! @seealso
f4c9d62009-02-15Stephen R. van den Berg //! @[big_query()], @[big_typed_query()], @[Sql.Sql], @[Sql.sql_result]
3b31fa2014-11-14Stephen R. van den Berg .pgsql_util.sql_result streaming_query(string q,
ab24362014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings) {
11b13b2014-08-16Martin Nilsson  return big_query(q,bindings);
a2014e2008-07-14Stephen R. van den Berg }
b156722008-08-25Stephen R. van den Berg  //! This function returns an object that allows streaming and typed //! results. //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[big_query()], @[Sql.Sql], @[Sql.sql_result]
3b31fa2014-11-14Stephen R. van den Berg .pgsql_util.sql_result big_typed_query(string q,
ab24362014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings) {
11b13b2014-08-16Martin Nilsson  return big_query(q,bindings,1);
b156722008-08-25Stephen R. van den Berg }