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__
5a4c012016-11-08Stephen R. van den Berg #pragma dynamic_dot
5691e42014-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 
b52b452017-03-15Henrik Grubbström (Grubba) inherit __builtin.Sql.Connection;
1412f32014-11-13Stephen R. van den Berg final int _fetchlimit=FETCHLIMIT; final Thread.Mutex _unnamedportalmux;
4e491d2016-04-01Stephen R. van den Berg private Thread.Mutex unnamedstatement;
da3dc82016-02-24Stephen R. van den Berg private Thread.MutexKey termlock;
1412f32014-11-13Stephen R. van den Berg final int _portalsinflight;
2e28a92017-06-18Stephen R. van den Berg final int _statementsinflight;
6623742017-06-17Stephen R. van den Berg final int _wasparallelisable;
16ce8b2014-09-12Stephen R. van den Berg 
39e1c42014-11-14Stephen R. van den Berg private .pgsql_util.conxion c;
8384822014-11-12Stephen R. van den Berg private string cancelsecret;
1412f32014-11-13Stephen R. van den Berg private int backendpid, backendstatus; final mapping(string:mixed) _options;
8384822014-11-12Stephen R. van den Berg private array(string) lastmessage=({}); private int clearmessage; private mapping(string:array(mixed)) notifylist=([]);
1412f32014-11-13Stephen R. van den Berg final mapping(string:string) _runtimeparameter; final mapping(string:mapping(string:mixed)) _prepareds=([]);
8384822014-11-12Stephen R. van den Berg private int pstmtcount;
32eee72015-12-09Stephen R. van den Berg private int ptstmtcount; // Periodically one would like to reset these
3e8df22014-10-29Stephen R. van den Berg  // but checking when this is safe to do // probably is more costly than the gain
1412f32014-11-13Stephen R. van den Berg final int _pportalcount;
8384822014-11-12Stephen R. van den Berg private int totalhits; private int cachedepth=STATEMENTCACHEDEPTH; private int timeout=QUERYTIMEOUT; private int portalbuffersize=PORTALBUFFERSIZE;
1412f32014-11-13Stephen R. van den Berg private int reconnected; // Number of times the connection was reset
8384822014-11-12Stephen R. van den Berg private int reconnectdelay; // Time to next reconnect
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_STATS
1412f32014-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
16ce8b2014-09-12Stephen R. van den Berg #endif
1412f32014-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
8384822014-11-12Stephen R. van den Berg private int warningscollected; private int invalidatecache; private Thread.Queue qportals;
1412f32014-11-13Stephen R. van den Berg final mixed _delayederror;
8384822014-11-12Stephen R. van den Berg private function (:void) readyforquery_cb;
16ce8b2014-09-12Stephen R. van den Berg 
1412f32014-11-13Stephen R. van den Berg final string _host; final int _port;
8384822014-11-12Stephen R. van den Berg private string database, user, pass; private Thread.Condition waitforauthready;
03dfca2014-11-18Stephen R. van den Berg final Thread.Mutex _shortmux;
1412f32014-11-13Stephen R. van den Berg final Thread.Condition _readyforcommit; final int _waittocommit, _readyforquerycount;
a2014e2008-07-14Stephen R. van den Berg 
74b59e2015-12-20Martin Nilsson private string _sprintf(int type) {
443c1f2014-08-21Stephen R. van den Berg  string res=UNDEFINED;
16ce8b2014-09-12Stephen R. van den Berg  switch(type) { case 'O':
4f2ed82014-11-15Stephen R. van den Berg  res=sprintf(DRIVERNAME"(%s@%s:%d/%s,%d,%d)",
3ac53f2015-07-07Arne Goedeke  user,_host,_port,database,c?->socket&&c->socket->query_fd(),backendpid);
16ce8b2014-09-12Stephen R. van den Berg  break;
a2014e2008-07-14Stephen R. van den Berg  } return res; }
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.
f91bc22014-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
4d53542014-11-24Stephen R. van den Berg //! be parsed before the previous statements have been executed //! (e.g. references to temporary tables created in the preceding //! statement), //! but it can speed up parsing due to 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
4d53542014-11-24Stephen R. van den Berg //! accessible to you.
a2014e2008-07-14Stephen R. van den Berg //! //! @seealso
2c228b2009-02-15Stephen R. van den Berg //! @[Postgres.postgres], @[Sql.Sql], @[select_db()],
4d53542014-11-24Stephen R. van den Berg //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+search_path@}
11b13b2014-08-16Martin Nilsson protected void create(void|string host, void|string database, void|string user, void|string pass,
16ce8b2014-09-12Stephen R. van den Berg  void|mapping(string:mixed) options) {
8e06a32014-09-30Martin Nilsson  this::pass = pass;
16ce8b2014-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;
1412f32014-11-13Stephen R. van den Berg  _options = options || ([]);
11b13b2014-08-16Martin Nilsson  if(!host) host = PGSQL_DEFAULT_HOST;
16ce8b2014-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");
16ce8b2014-09-12Stephen R. van den Berg  this::_host = host;
11b13b2014-08-16Martin Nilsson 
16ce8b2014-09-12Stephen R. van den Berg  if(!_port) _port = PGSQL_DEFAULT_PORT;
5a5c8e2016-02-25Stephen R. van den Berg  .pgsql_util.register_backend();
03dfca2014-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
ac824b2014-11-24Stephen R. van den Berg //! since the last statement.
f28c0d2008-08-01Stephen R. van den Berg //!
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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final string error(void|int clear) {
16ce8b2014-09-12Stephen R. van den Berg  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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final string host_info() {
11b13b2014-08-16Martin Nilsson  return sprintf("fd:%d TCP/IP %s:%d PID %d",
16ce8b2014-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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int is_open() {
16ce8b2014-09-12Stephen R. van den Berg  catch {
3c93de2014-11-03Stephen R. van den Berg  return c->socket->is_open();
16ce8b2014-09-12Stephen R. van den Berg  }; return 0;
6b4ea82012-04-10Stephen R. van den Berg } //! Check whether the connection is alive. //! //! @returns //! Returns one of the following: //! @int //! @value 0 //! Everything ok. //! @value 1
16ce8b2014-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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int ping() {
2eef0b2014-11-15Stephen R. van den Berg  return is_open() && !catch(c->start()->sendcmd(FLUSHSEND))
16ce8b2014-09-12Stephen R. van den Berg  ? !!reconnected : -1;
6b4ea82012-04-10Stephen R. van den Berg }
39e1c42014-11-14Stephen R. van den Berg private .pgsql_util.conxion getsocket(void|int nossl) {
72d9bf2014-11-18Stephen R. van den Berg  return .pgsql_util.conxion(this,qportals,(int)nossl);
a2014e2008-07-14Stephen R. van den Berg }
16ce8b2014-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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void cancelquery() {
16ce8b2014-09-12Stephen R. van den Berg  PD("CancelRequest\n");
9d0a682016-10-12Stephen R. van den Berg  .pgsql_util.conxion lcon=getsocket(2);
16ce8b2014-09-12Stephen R. van den Berg  lcon->add_int32(16)->add_int32(PG_PROTOCOL(1234,5678))
2eef0b2014-11-15Stephen R. van den Berg  ->add_int32(backendpid)->add(cancelsecret)->sendcmd(FLUSHSEND);
8e64d32016-10-14Stephen R. van den Berg  destruct(lcon); // Destruct explicitly to avoid delayed close
615d422014-10-31Stephen R. van den Berg #ifdef PG_DEBUGMORE PD("Closetrace %O\n",backtrace()); #endif
d88e732014-11-14Stephen R. van den Berg  if(c) {
c02c1d2017-06-03Stephen R. van den Berg  .pgsql_util.conxsess plugbuffer;
c395a72015-12-09Stephen R. van den Berg  if(!catch(plugbuffer=c->start(1))) { foreach(qportals->peek_array();;int|.pgsql_util.sql_result portal) if(objectp(portal)) portal->_closeportal(plugbuffer); plugbuffer->sendcmd(SENDOUT); }
d88e732014-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@}
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void set_charset(string charset) {
4f2ed82014-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@}
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final string get_charset() {
5716222014-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
4d53542014-11-24Stephen R. van den Berg //! @url{http://www.postgresql.org/search/?u=%2Fdocs%2Fcurrent%2F&q=client+connection+search_path@}
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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final mapping(string:string) getruntimeparameters() {
5716222014-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
ae952f2014-11-10Stephen R. van den Berg //! describe the statement parameters because it was already cached. //! Only available if PG_STATS is compile-time enabled.
13d29c2009-04-16Henrik Grubbström (Grubba) //! @member int "used_prepared_statements"
aebf352014-11-26Stephen R. van den Berg //! Number of times prepared statements were used from cache instead of
ae952f2014-11-10Stephen R. van den Berg //! reparsing in the current session. //! 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
ae952f2014-11-10Stephen R. van den Berg //! to the database. //! 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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final mapping(string:mixed) getstatistics() {
11b13b2014-08-16Martin Nilsson  mapping(string:mixed) stats=([
cb26232008-08-04Stephen R. van den Berg  "warnings_dropped":warningsdropcount,
615d422014-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,
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_STATS
3e8df22014-10-29Stephen R. van den Berg  "used_prepared_statements":prepstmtused,
16ce8b2014-09-12Stephen R. van den Berg  "skipped_describe_count":skippeddescribe,
cb26232008-08-04Stephen R. van den Berg  "portals_opened_count":portalsopened,
16ce8b2014-09-12Stephen R. van den Berg #endif
cb26232008-08-04Stephen R. van den Berg  "messages_received":_msgsreceived, "bytes_received":_bytesreceived, "reconnect_count":reconnected,
16ce8b2014-09-12Stephen R. van den Berg  "portals_in_flight":_portalsinflight,
2e28a92017-06-18Stephen R. van den Berg  "statements_in_flight":_statementsinflight,
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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int settimeout(void|int newtimeout) {
11b13b2014-08-16Martin Nilsson  int oldtimeout=timeout;
5cb4f22014-11-28Stephen R. van den Berg  if(!undefinedp(newtimeout) && newtimeout>0)
a2014e2008-07-14Stephen R. van den Berg  timeout=newtimeout; 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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int setfetchlimit(void|int newfetchlimit) {
615d422014-10-31Stephen R. van den Berg  int oldfetchlimit=_fetchlimit;
65340d2014-08-15Martin Nilsson  if(!undefinedp(newfetchlimit) && newfetchlimit>=0)
615d422014-10-31Stephen R. van den Berg  _fetchlimit=newfetchlimit;
a2014e2008-07-14Stephen R. van den Berg  return oldfetchlimit; }
8384822014-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,({"*","?","\\","%","_"}),({"%","_","\\\\","\\%","\\_"})); }
8384822014-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 }
8384822014-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; }
8384822014-11-12Stephen R. van den Berg private void connect_cb() {
16ce8b2014-09-12Stephen R. van den Berg  PD("%O\n",_runtimeparameter); }
8384822014-11-12Stephen R. van den Berg private void reconnect_cb() {
615d422014-10-31Stephen R. van den Berg  lastmessage+=({sprintf("Reconnected to database %s",host_info())}); runcallback(backendpid,"_reconnect","");
16ce8b2014-09-12Stephen R. van den Berg }
39e1c42014-11-14Stephen R. van den Berg private array(string) showbindings(.pgsql_util.sql_result portal) {
16ce8b2014-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  } }
16ce8b2014-09-12Stephen R. van den Berg  return msgs;
bac80c2010-02-15Stephen R. van den Berg }
8384822014-11-12Stephen R. van den Berg private void preplastmessage(mapping(string:string) msgresponse) {
16ce8b2014-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||"")}); }
8384822014-11-12Stephen R. van den Berg private void waitauthready() {
5716222014-11-09Stephen R. van den Berg  if(waitforauthready) {
60f4ad2016-02-17Stephen R. van den Berg  PD("%d Wait for auth ready %O\n", c?->socket&&c->socket->query_fd(),backtrace()[-2]);
03dfca2014-11-18Stephen R. van den Berg  Thread.MutexKey lock=_shortmux->lock();
d4ef202014-12-01Stephen R. van den Berg  catch(PT(waitforauthready->wait(lock)));
5716222014-11-09Stephen R. van den Berg  lock=0;
3ac53f2015-07-07Arne Goedeke  PD("%d Wait for auth ready released.\n",c?->socket&&c->socket->query_fd());
5716222014-11-09Stephen R. van den Berg  } }
8384822014-11-12Stephen R. van den Berg private inline mixed callout(function(mixed ...:void) f,
2c98002014-11-10Stephen R. van den Berg  float|int delay,mixed ... args) { return .pgsql_util.local_backend->call_out(f,delay,@args); }
7f73f42014-12-16Stephen R. van den Berg private int|.pgsql_util.sql_result portal; // state information procmessage #ifdef PG_DEBUG private string datarowdebug; private int datarowdebugcount; #endif
39e1c42014-11-14Stephen R. van den Berg final void _processloop(.pgsql_util.conxion ci) {
7f73f42014-12-16Stephen R. van den Berg  (c=ci)->socket->set_id(procmessage); cancelsecret=0; portal=0;
11b13b2014-08-16Martin Nilsson  {
91d6542014-11-10Stephen R. van den Berg  Stdio.Buffer plugbuffer=Stdio.Buffer()->add_int32(PG_PROTOCOL(3,0));
16ce8b2014-09-12Stephen R. van den Berg  if(user)
c9c7fc2014-11-26Stephen R. van den Berg  plugbuffer->add("user\0",user,0);
16ce8b2014-09-12Stephen R. van den Berg  if(database)
c9c7fc2014-11-26Stephen R. van den Berg  plugbuffer->add("database\0",database,0);
1412f32014-11-13Stephen R. van den Berg  _options.reconnect=undefinedp(_options.reconnect) || _options.reconnect;
b840fa2014-11-19Stephen R. van den Berg  foreach(_options-.pgsql_util.censoroptions; string name; mixed value)
c9c7fc2014-11-26Stephen R. van den Berg  plugbuffer->add(name,0,(string)value,0);
16ce8b2014-09-12Stephen R. van den Berg  plugbuffer->add_int8(0); PD("%O\n",(string)plugbuffer);
c02c1d2017-06-03Stephen R. van den Berg  object cs; if (catch(cs = ci->start())) {
da3dc82016-02-24Stephen R. van den Berg  if(_options.reconnect) _connectfail(); else destruct(waitforauthready);
9d0a682016-10-12Stephen R. van den Berg  unnamedstatement=0;
da3dc82016-02-24Stephen R. van den Berg  termlock=0;
e7f7d12016-02-17Stephen R. van den Berg  return;
c02c1d2017-06-03Stephen R. van den Berg  } else { CHAIN(cs)->add_hstring(plugbuffer, 4, 4); cs->sendcmd(SENDOUT);
e7f7d12016-02-17Stephen R. van den Berg  } } // Do not flush at this point, PostgreSQL 9.4 disapproves procmessage();
7f73f42014-12-16Stephen R. van den Berg } private void procmessage() {
9d0a682016-10-12Stephen R. van den Berg  mixed err;
7f73f42014-12-16Stephen R. van den Berg  int terminating=0;
9d0a682016-10-12Stephen R. van den Berg  err = catch {
7f73f42014-12-16Stephen R. van den Berg  .pgsql_util.conxion ci=c; // cache value FIXME sensible? .pgsql_util.conxiin cr=ci->i; // cache value FIXME sensible?
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
3e8df22014-10-29Stephen R. van den Berg  PD("Processloop\n"); void showportal(int msgtype) { if(objectp(portal))
4f2ed82014-11-15Stephen R. van den Berg  PD("%d<%O %d %c switch portal\n", ci->socket->query_fd(),portal._portalname,++ci->queueinidx,msgtype);
3e8df22014-10-29Stephen R. van den Berg  else if(portal>0)
4f2ed82014-11-15Stephen R. van den Berg  PD("%d<Sync %d %d %c portal\n", ci->socket->query_fd(),++ci->queueinidx,portal,msgtype);
3e8df22014-10-29Stephen R. van den Berg  };
a2014e2008-07-14Stephen R. van den Berg #endif
16ce8b2014-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  }
16ce8b2014-09-12Stephen R. van den Berg #endif
7f73f42014-12-16Stephen R. van den Berg  if(!sizeof(cr)) { // Preliminary check, fast path Thread.MutexKey lock=cr->fillreadmux->lock(); if(!sizeof(cr)) { // Check for real
b95bf72016-02-17Stephen R. van den Berg  if(!cr->fillread) { lock=0; throw(MAGICTERMINATE); // Force proper termination }
7f73f42014-12-16Stephen R. van den Berg  cr->procmsg=1; lock=0; return; // Terminate thread, wait for callback } lock=0; }
c7de862014-11-20Stephen R. van den Berg  int msgtype=cr->read_int8();
16ce8b2014-09-12Stephen R. van den Berg  if(!portal) { portal=qportals->try_read();
3e8df22014-10-29Stephen R. van den Berg #ifdef PG_DEBUG showportal(msgtype); #endif
16ce8b2014-09-12Stephen R. van den Berg  }
c7de862014-11-20Stephen R. van den Berg  int msglen=cr->read_int32();
16ce8b2014-09-12Stephen R. van den Berg  _msgsreceived++; _bytesreceived+=1+msglen;
5bbace2014-11-18Stephen R. van den Berg  int errtype=NOERROR;
5318352014-11-22Stephen R. van den Berg  PD("%d<",ci->socket->query_fd());
16ce8b2014-09-12Stephen R. van den Berg  switch(msgtype) {
8059252014-11-25Stephen R. van den Berg  array getcols() {
c7de862014-11-20Stephen R. van den Berg  int bintext=cr->read_int8(); int cols=cr->read_int16();
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG array a; msglen-=4+1+2+2*cols; foreach(a=allocate(cols,([]));;mapping m)
c7de862014-11-20Stephen R. van den Berg  m.type=cr->read_int16();
16ce8b2014-09-12Stephen R. van den Berg #else
c7de862014-11-20Stephen R. van den Berg  cr->consume(cols<<1);
16ce8b2014-09-12Stephen R. van den Berg #endif // Discard column info, and make it line oriented
8059252014-11-25Stephen R. van den Berg  return ({ ({(["name":"line"])}), ({bintext?BYTEAOID:TEXTOID}) });
16ce8b2014-09-12Stephen R. van den Berg  }; array(string) reads() { #ifdef PG_DEBUG if(msglen<1)
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg #endif array ret=({}),aw=({0}); do {
c7de862014-11-20Stephen R. van den Berg  string w=cr->read_cstring();
16ce8b2014-09-12Stephen R. van den Berg  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': {
10fa552014-11-18Stephen R. van den Berg  PD("Authentication ");
16ce8b2014-09-12Stephen R. van den Berg  string sendpass; msglen-=4+4;
907a7a2014-11-20Stephen R. van den Berg  int authtype=cr->read_int32(); switch(authtype) {
16ce8b2014-09-12Stephen R. van den Berg  case 0: PD("Ok\n"); .pgsql_util.local_backend->remove_call_out(reconnect); reconnectdelay=0; cancelsecret=""; break; case 2: PD("KerberosV5\n");
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  break; case 3: PD("ClearTextPassword\n"); sendpass=pass; break; case 4: PD("CryptPassword\n");
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  break; case 5: PD("MD5Password\n"); #ifdef PG_DEBUG if(msglen<4)
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg #endif #define md5hex(x) String.string2hex(Crypto.MD5.hash(x)) sendpass=md5hex(pass+user);
c7de862014-11-20Stephen R. van den Berg  sendpass="md5"+md5hex(sendpass+cr->read(msglen));
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG msglen=0; #endif break; case 6: PD("SCMCredential\n");
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  break; case 7: PD("GSS\n");
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  break; case 9: PD("SSPI\n");
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  break; case 8: PD("GSSContinue\n");
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
c7de862014-11-20Stephen R. van den Berg  cancelsecret=cr->read(msglen); // Actually SSauthdata
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG if(msglen<1)
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg  msglen=0; #endif break; default: PD("Unknown Authentication Method %c\n",authtype);
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  break; } switch(errtype) {
5bbace2014-11-18Stephen R. van den Berg  case NOERROR:
c02c1d2017-06-03Stephen R. van den Berg  if(cancelsecret!="") { object cs = ci->start(); CHAIN(cs)->add_int8('p')->add_hstring(({sendpass, 0}), 4, 4); cs->sendcmd(SENDOUT); }
075c542014-11-03Stephen R. van den Berg  break; // No flushing here, PostgreSQL 9.4 disapproves
16ce8b2014-09-12Stephen R. van den Berg  default:
5bbace2014-11-18Stephen R. van den Berg  case PROTOCOLUNSUPPORTED:
16ce8b2014-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;
16ce8b2014-09-12Stephen R. van den Berg  } case 'K':
c7de862014-11-20Stephen R. van den Berg  msglen-=4+4;backendpid=cr->read_int32(); cancelsecret=cr->read(msglen);
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("BackendKeyData %O\n",cancelsecret);
16ce8b2014-09-12Stephen R. van den Berg  msglen=0; #endif
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  case 'S': {
10fa552014-11-18Stephen R. van den Berg  PD("ParameterStatus ");
16ce8b2014-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
3e8df22014-10-29Stephen R. van den Berg  PD("%O=%O\n",ts[0],ts[1]);
16ce8b2014-09-12Stephen R. van den Berg  } else
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg #endif
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  } case '3': #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("CloseComplete\n");
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #endif
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  case 'Z':
c7de862014-11-20Stephen R. van den Berg  backendstatus=cr->read_int8();
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG msglen-=4+1;
10fa552014-11-18Stephen R. van den Berg  PD("ReadyForQuery %c\n",backendstatus);
3e8df22014-10-29Stephen R. van den Berg #endif
f91bc22014-11-10Stephen R. van den Berg  for(;objectp(portal);portal=qportals->read()) {
3e8df22014-10-29Stephen R. van den Berg #ifdef PG_DEBUG showportal(msgtype); #endif
615d422014-10-31Stephen R. van den Berg  portal->_purgeportal();
3e8df22014-10-29Stephen R. van den Berg  }
39e1c42014-11-14Stephen R. van den Berg  foreach(qportals->peek_array();;.pgsql_util.sql_result qp) {
3c93de2014-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);
615d422014-10-31Stephen R. van den Berg  qp->_purgeportal();
3c93de2014-11-03Stephen R. van den Berg  }
3e8df22014-10-29Stephen R. van den Berg  } portal=0; _readyforquerycount--;
16ce8b2014-09-12Stephen R. van den Berg  if(readyforquery_cb) readyforquery_cb(),readyforquery_cb=0;
c7de862014-11-20Stephen R. van den Berg  destruct(waitforauthready);
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  case '1': #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("ParseComplete\n");
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #endif
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  case 't': { array a; #ifdef PG_DEBUG
8059252014-11-25Stephen R. van den Berg  int cols=cr->read_int16();
10fa552014-11-18Stephen R. van den Berg  PD("%O ParameterDescription %d values\n",portal._query,cols);
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4+2+4*cols;
8059252014-11-25Stephen R. van den Berg  a=cr->read_ints(cols,4); #else a=cr->read_ints(cr->read_int16(),4);
16ce8b2014-09-12Stephen R. van den Berg #endif #ifdef PG_DEBUGMORE PD("%O\n",a); #endif if(portal._tprepared) portal._tprepared.datatypeoid=a;
5318352014-11-22Stephen R. van den Berg  Thread.Thread(portal->_preparebind,a);
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  } case 'T': {
8059252014-11-25Stephen R. van den Berg  array a,at;
c7de862014-11-20Stephen R. van den Berg  int cols=cr->read_int16();
8059252014-11-25Stephen R. van den Berg #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("RowDescription %d columns %O\n",cols,portal._query);
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4+2; #endif
8059252014-11-25Stephen R. van den Berg  at=allocate(cols); foreach(a=allocate(cols);int i;)
615d422014-10-31Stephen R. van den Berg  {
c7de862014-11-20Stephen R. van den Berg  string s=cr->read_cstring();
16ce8b2014-09-12Stephen R. van den Berg  mapping(string:mixed) res=(["name":s]); #ifdef PG_DEBUG msglen-=sizeof(s)+1+4+2+4+2+4+2;
c7de862014-11-20Stephen R. van den Berg  res.tableoid=cr->read_int32()||UNDEFINED; res.tablecolattr=cr->read_int16()||UNDEFINED;
16ce8b2014-09-12Stephen R. van den Berg #else
c7de862014-11-20Stephen R. van den Berg  cr->consume(6);
16ce8b2014-09-12Stephen R. van den Berg #endif
8059252014-11-25Stephen R. van den Berg  at[i]=cr->read_int32();
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
2e90332014-11-25Stephen R. van den Berg  res.type=at[i];
16ce8b2014-09-12Stephen R. van den Berg  {
c7de862014-11-20Stephen R. van den Berg  int len=cr->read_sint(2);
16ce8b2014-09-12Stephen R. van den Berg  res.length=len>=0?len:"variable"; }
c7de862014-11-20Stephen R. van den Berg  res.atttypmod=cr->read_int32();
16ce8b2014-09-12Stephen R. van den Berg  /* formatcode contains just a zero when Bind has not been issued * yet, but the content is irrelevant because it's determined * at query time */
c7de862014-11-20Stephen R. van den Berg  res.formatcode=cr->read_int16();
16ce8b2014-09-12Stephen R. van den Berg #else
c7de862014-11-20Stephen R. van den Berg  cr->consume(8);
16ce8b2014-09-12Stephen R. van den Berg #endif a[i]=res; } #ifdef PG_DEBUGMORE PD("%O\n",a); #endif
3c93de2014-11-03Stephen R. van den Berg  if(portal._forcetext)
8059252014-11-25Stephen R. van den Berg  portal->_setrowdesc(a,at); // Do not consume queued portal
3c93de2014-11-03Stephen R. van den Berg  else {
8059252014-11-25Stephen R. van den Berg  portal->_processrowdesc(a,at);
3c93de2014-11-03Stephen R. van den Berg  portal=0; }
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  } case 'n': { #ifdef PG_DEBUG msglen-=4;
10fa552014-11-18Stephen R. van den Berg  PD("NoData %O\n",portal._query);
3e8df22014-10-29Stephen R. van den Berg #endif
16ce8b2014-09-12Stephen R. van den Berg  portal._fetchlimit=0; // disables subsequent Executes
8059252014-11-25Stephen R. van den Berg  portal ->_processrowdesc(.pgsql_util.emptyarray,.pgsql_util.emptyarray);
16ce8b2014-09-12Stephen R. van den Berg  portal=0;
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  } case 'H':
8059252014-11-25Stephen R. van den Berg  portal->_processrowdesc(@getcols());
10fa552014-11-18Stephen R. van den Berg  PD("CopyOutResponse %O\n",portal._query);
11b13b2014-08-16Martin Nilsson  break;
16ce8b2014-09-12Stephen R. van den Berg  case '2': { mapping tp; #ifdef PG_DEBUG msglen-=4;
10fa552014-11-18Stephen R. van den Berg  PD("%O BindComplete\n",portal._portalname);
3e8df22014-10-29Stephen R. van den Berg #endif
16ce8b2014-09-12Stephen R. van den Berg  if(tp=portal._tprepared) { int tend=gethrtime(); int tstart=tp.trun; if(tend==tstart)
615d422014-10-31Stephen R. van den Berg  m_delete(_prepareds,portal._query);
16ce8b2014-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; }
aed7142014-11-18Stephen R. van den Berg  case 'D':
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #ifdef PG_DEBUG
cd7f232014-03-01Martin Nilsson #ifdef PG_DEBUGMORE
10fa552014-11-18Stephen R. van den Berg  PD("%O DataRow %d bytes\n",portal._portalname,msglen);
a2014e2008-07-14Stephen R. van den Berg #endif
16ce8b2014-09-12Stephen R. van den Berg  datarowdebugcount++; if(!datarowdebug) datarowdebug=sprintf(
10fa552014-11-18Stephen R. van den Berg  "%O DataRow %d bytes",portal._portalname,msglen);
a2014e2008-07-14Stephen R. van den Berg #endif
aed7142014-11-18Stephen R. van den Berg #ifdef PG_DEBUG msglen=
7ac2f12008-07-27Stephen R. van den Berg #endif
aed7142014-11-18Stephen R. van den Berg  portal->_decodedata(msglen,_runtimeparameter[CLIENT_ENCODING]);
16ce8b2014-09-12Stephen R. van den Berg  break; case 's': #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("%O PortalSuspended\n",portal._portalname);
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #endif portal=0; break; case 'C': { msglen-=4; #ifdef PG_DEBUG if(msglen<1)
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg #endif
c7de862014-11-20Stephen R. van den Berg  string s=cr->read(msglen-1);
aed7142014-11-18Stephen R. van den Berg  portal->_storetiming();
10fa552014-11-18Stephen R. van den Berg  PD("%O CommandComplete %O\n",portal._portalname,s);
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG
c7de862014-11-20Stephen R. van den Berg  if(cr->read_int8())
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg  msglen=0; #else
c7de862014-11-20Stephen R. van den Berg  cr->consume(1);
16ce8b2014-09-12Stephen R. van den Berg #endif
05b1f52014-11-17Stephen R. van den Berg  portal->_releasesession(s);
16ce8b2014-09-12Stephen R. van den Berg  portal=0; break; } case 'I': #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("EmptyQueryResponse %O\n",portal._portalname);
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #endif portal->_releasesession(); portal=0; break; case 'd':
10fa552014-11-18Stephen R. van den Berg  PD("%O CopyData\n",portal._portalname);
aed7142014-11-18Stephen R. van den Berg  portal->_storetiming();
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #ifdef PG_DEBUG if(msglen<0)
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg #endif
c7de862014-11-20Stephen R. van den Berg  portal->_processdataready(({cr->read(msglen)}),msglen);
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG msglen=0; #endif break; case 'G':
2e28a92017-06-18Stephen R. van den Berg  portal->_releasestatement();
8059252014-11-25Stephen R. van den Berg  portal->_setrowdesc(@getcols());
10fa552014-11-18Stephen R. van den Berg  PD("%O CopyInResponse\n",portal._portalname);
2eef0b2014-11-15Stephen R. van den Berg  portal._state=COPYINPROGRESS;
16ce8b2014-09-12Stephen R. van den Berg  break; case 'c': #ifdef PG_DEBUG
10fa552014-11-18Stephen R. van den Berg  PD("%O CopyDone\n",portal._portalname);
16ce8b2014-09-12Stephen R. van den Berg  msglen-=4; #endif portal=0; break; case 'E': {
3e8df22014-10-29Stephen R. van den Berg  if(!_readyforquerycount) sendsync();
10fa552014-11-18Stephen R. van den Berg  PD("%O ErrorResponse %O\n",
075c542014-11-03Stephen R. van den Berg  objectp(portal)&&(portal._portalname||portal._preparedname), objectp(portal)&&portal._query);
16ce8b2014-09-12Stephen R. van den Berg  mapping(string:string) msgresponse; msgresponse=getresponse();
11b13b2014-08-16Martin Nilsson  warningsdropcount+=warningscollected;
16ce8b2014-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)));
d88e732014-11-14Stephen R. van den Berg  case "53000":case "53100":case "53200":case "53300":case "53400":
4f2ed82014-11-15Stephen R. van den Berg  case "57P01":case "57P02":case "57P03":case "57P04":case "3D000":
16ce8b2014-09-12Stephen R. van den Berg  preplastmessage(msgresponse);
3c93de2014-11-03Stephen R. van den Berg  PD(a2nls(lastmessage));throw(0);
16ce8b2014-09-12Stephen R. van den Berg  case "08P01":case "42P05":
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-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+=({
075c542014-11-03Stephen R. van den Berg  pinpointerror(objectp(portal)&&portal._query,msgresponse.P)+
16ce8b2014-09-12Stephen R. van den Berg  pinpointerror(msgresponse.q,msgresponse.p)}); if(msgresponse.W) lastmessage+=({msgresponse.W});
4a619c2014-11-08Stephen R. van den Berg  if(objectp(portal)) lastmessage+=showbindings(portal);
16ce8b2014-09-12Stephen R. van den Berg  switch(msgresponse.S) { case "PANIC":werror(a2nls(lastmessage)); }
f369912017-06-21Stephen R. van den Berg  case "25P02": // Preserve last error message
16ce8b2014-09-12Stephen R. van den Berg  USERERROR(a2nls(lastmessage)); }
075c542014-11-03Stephen R. van den Berg  if(objectp(portal))
3e8df22014-10-29Stephen R. van den Berg  portal->_releasesession();
16ce8b2014-09-12Stephen R. van den Berg  break; } case 'N': {
10fa552014-11-18Stephen R. van den Berg  PD("NoticeResponse\n");
16ce8b2014-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)});
38f2812015-09-18Stephen R. van den Berg  if(has_prefix(msgresponse.C,"53")||has_prefix(msgresponse.C,"57P")) { preplastmessage(msgresponse); PD(a2nls(lastmessage));throw(0); // Some warnings are fatal }
16ce8b2014-09-12Stephen R. van den Berg  break; } case 'A': {
10fa552014-11-18Stephen R. van den Berg  PD("NotificationResponse\n");
11b13b2014-08-16Martin Nilsson  msglen-=4+4;
c7de862014-11-20Stephen R. van den Berg  int pid=cr->read_int32();
11b13b2014-08-16Martin Nilsson  string condition,extrainfo=UNDEFINED; {
16ce8b2014-09-12Stephen R. van den Berg  array(string) ts=reads(); switch(sizeof(ts)) { #if PG_DEBUG case 0:
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg  break; default:
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-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);
c7de862014-11-20Stephen R. van den Berg  s=cr->read(msglen-=4);PD("%O\n",s);
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG msglen=0; #endif
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLUNSUPPORTED;
16ce8b2014-09-12Stephen R. van den Berg  } else { lastmessage+=({ sprintf("Connection lost to database %s@%s:%d/%s %d\n", user,_host,_port,database,backendpid)});
3c93de2014-11-03Stephen R. van den Berg  if(!waitforauthready) throw(0);
16ce8b2014-09-12Stephen R. van den Berg  USERERROR(a2nls(lastmessage)); } break;
11b13b2014-08-16Martin Nilsson  }
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_DEBUG if(msglen)
5bbace2014-11-18Stephen R. van den Berg  errtype=PROTOCOLERROR;
16ce8b2014-09-12Stephen R. van den Berg #endif
11b13b2014-08-16Martin Nilsson  {
16ce8b2014-09-12Stephen R. van den Berg  string msg; switch(errtype) {
5bbace2014-11-18Stephen R. van den Berg  case PROTOCOLUNSUPPORTED:
16ce8b2014-09-12Stephen R. van den Berg  msg=sprintf("Unsupported servermessage received %c\n",msgtype); break;
5bbace2014-11-18Stephen R. van den Berg  case PROTOCOLERROR:
16ce8b2014-09-12Stephen R. van den Berg  msg=sprintf("Protocol error with database %s",host_info()); break;
5bbace2014-11-18Stephen R. van den Berg  case NOERROR:
16ce8b2014-09-12Stephen R. van den Berg  continue; // Normal production loop } ERROR(a2nls(lastmessage+=({msg})));
11b13b2014-08-16Martin Nilsson  }
16ce8b2014-09-12Stephen R. van den Berg  }; // We only get here if there is an error
39e1c42014-11-14Stephen R. van den Berg  if(err==MAGICTERMINATE) { // Announce connection termination to server
c02c1d2017-06-03Stephen R. van den Berg  object cs = ci->start(); CHAIN(cs)->add("X\0\0\0\4"); cs->sendcmd(SENDOUT);
16ce8b2014-09-12Stephen R. van den Berg  terminating=1;
5811852016-02-18Stephen R. van den Berg  err=0; } else if(stringp(err)) {
39e1c42014-11-14Stephen R. van den Berg  .pgsql_util.sql_result or;
3c93de2014-11-03Stephen R. van den Berg  if(!objectp(or=portal))
3e8df22014-10-29Stephen R. van den Berg  or=this; if(!or._delayederror) or._delayederror=err;
3c93de2014-11-03Stephen R. van den Berg  if(objectp(portal))
3e8df22014-10-29Stephen R. van den Berg  portal->_releasesession(); portal=0;
eafa302014-11-14Stephen R. van den Berg  if(!waitforauthready) continue; // Only continue if authentication did not fail
a2014e2008-07-14Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  break;
a2014e2008-07-14Stephen R. van den Berg  }
d88e732014-11-14Stephen R. van den Berg  PD("Closing database processloop %O\n",err);
da3dc82016-02-24Stephen R. van den Berg  _delayederror=err;
8469442016-12-06Stephen R. van den Berg  if (objectp(portal)) { #ifdef PG_DEBUG showportal(0); #endif portal->_purgeportal(); }
dcc15c2016-02-27Stephen R. van den Berg  if(!terminating && _options.reconnect)
da3dc82016-02-24Stephen R. van den Berg  _connectfail(); else destruct(waitforauthready); termlock=0;
5a558d2016-02-17Stephen R. van den Berg  if(err && !stringp(err))
3c93de2014-11-03Stephen R. van den Berg  throw(err);
9d0a682016-10-12Stephen R. van den Berg  }; if (err) { unnamedstatement=0; termlock = 0; 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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void close() {
aeadf72017-06-21Stephen R. van den Berg  throwdelayederror(this);
a7124b2016-10-19Stephen R. van den Berg  Thread.MutexKey lock; if (qportals && qportals->size())
b95bf72016-02-17Stephen R. van den Berg  catch(cancelquery());
9d0a682016-10-12Stephen R. van den Berg  if (unnamedstatement)
a7124b2016-10-19Stephen R. van den Berg  termlock = unnamedstatement->lock(1);
da3dc82016-02-24Stephen R. van den Berg  c->close();
9d0a682016-10-12Stephen R. van den Berg  if (unnamedstatement)
a7124b2016-10-19Stephen R. van den Berg  lock = unnamedstatement->lock(1); destruct(c); lock = 0; destruct(waitforauthready);
a2014e2008-07-14Stephen R. van den Berg }
8384822014-11-12Stephen R. van den Berg protected void destroy() {
aeadf72017-06-21Stephen R. van den Berg  string errstring; mixed err = catch(close());
16ce8b2014-09-12Stephen R. van den Berg  .pgsql_util.unregister_backend();
aeadf72017-06-21Stephen R. van den Berg  /* * Flush out any asynchronously reported errors to stderr; because we are * inside a destructor, throwing an error will not work anymore. */ if (err || (err = catch(errstring = error(1)))) werror(describe_backtrace(err)); else if (sizeof(errstring)) werror(errstring);
cb26232008-08-04Stephen R. van den Berg }
e7c5c82014-11-24Stephen R. van den Berg final void _connectfail(void|mixed err) {
3c93de2014-11-03Stephen R. van den Berg  PD("Connect failed %O reconnectdelay %d\n",err,reconnectdelay);
16ce8b2014-09-12Stephen R. van den Berg  if(!err || reconnectdelay) { int tdelay; switch(tdelay=reconnectdelay) { case 0: reconnectdelay=RECONNECTDELAY; break; default:
3c93de2014-11-03Stephen R. van den Berg  if(err) _delayederror=err;
8469442016-12-06Stephen R. van den Berg  if (_options.reconnect!=-1) { destruct(waitforauthready); destruct(c);
16ce8b2014-09-12Stephen R. van den Berg  return;
8469442016-12-06Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  reconnectdelay=RECONNECTBACKOFF; break; }
03dfca2014-11-18Stephen R. van den Berg  Thread.MutexKey lock=_shortmux->lock();
16ce8b2014-09-12Stephen R. van den Berg  if(!waitforauthready) waitforauthready=Thread.Condition(); lock=0;
3c93de2014-11-03Stephen R. van den Berg  PD("Schedule reconnect in %ds\n",tdelay); _delayederror=0;
2c98002014-11-10Stephen R. van den Berg  callout(reconnect,tdelay,1);
3c93de2014-11-03Stephen R. van den Berg  } else if(err) _delayederror=err;
16ce8b2014-09-12Stephen R. van den Berg }
cab2812015-12-23Stephen R. van den Berg private int reconnect() { int recon=0;
3c93de2014-11-03Stephen R. van den Berg  PD("(Re)connect\n");
cab2812015-12-23Stephen R. van den Berg  {
03dfca2014-11-18Stephen R. van den Berg  Thread.MutexKey lock=_shortmux->lock();
8469442016-12-06Stephen R. van den Berg  if (!waitforauthready) waitforauthready=Thread.Condition();
16ce8b2014-09-12Stephen R. van den Berg  lock=0; } if(c) {
3c93de2014-11-03Stephen R. van den Berg  PD("Close old connection\n");
cab2812015-12-23Stephen R. van den Berg  reconnected++;recon=1;
3e8df22014-10-29Stephen R. van den Berg #ifdef PG_STATS
cb26232008-08-04Stephen R. van den Berg  prepstmtused=0;
3e8df22014-10-29Stephen R. van den Berg #endif
c02c1d2017-06-03Stephen R. van den Berg  if (unnamedstatement) termlock = unnamedstatement->lock(1);
8469442016-12-06Stephen R. van den Berg  catch(c->close()); unnamedstatement = 0; termlock = 0;
32857b2016-02-28Stephen R. van den Berg  destruct(c);
3c93de2014-11-03Stephen R. van den Berg  PD("Flushing old cache\n");
615d422014-10-31Stephen R. van den Berg  foreach(_prepareds;;mapping tp)
cb26232008-08-04Stephen R. van den Berg  m_delete(tp,"preparedname");
cab2812015-12-23Stephen R. van den Berg  if(!_options.reconnect) ERROR("Lost connection to database %s:%d\n",_host,_port);
6d9aa52008-08-21Stephen R. van den Berg  }
3c93de2014-11-03Stephen R. van den Berg  PD("Actually start to connect\n");
16ce8b2014-09-12Stephen R. van den Berg  qportals=Thread.Queue();
1412f32014-11-13Stephen R. van den Berg  _readyforcommit=Thread.Condition();
3e8df22014-10-29Stephen R. van den Berg  _readyforquerycount=1;
1412f32014-11-13Stephen R. van den Berg  _waittocommit=0;
3e8df22014-10-29Stephen R. van den Berg  qportals->write(1);
1ab7532016-02-17Stephen R. van den Berg  if(!(c=getsocket())) ERROR("Couldn't connect to database on %s:%d\n",_host,_port);
16ce8b2014-09-12Stephen R. van den Berg  _runtimeparameter=([]);
615d422014-10-31Stephen R. van den Berg  _unnamedportalmux=Thread.Mutex();
16ce8b2014-09-12Stephen R. van den Berg  unnamedstatement=Thread.Mutex();
cab2812015-12-23Stephen R. van den Berg  readyforquery_cb=recon?reconnect_cb:connect_cb;
16ce8b2014-09-12Stephen R. van den Berg  _portalsinflight=0;
2e28a92017-06-18Stephen R. van den Berg  _statementsinflight = 0;
6623742017-06-17Stephen R. van den Berg  _wasparallelisable = 0;
53b3792010-03-23Stephen R. van den Berg  return 1;
a2014e2008-07-14Stephen R. van den Berg }
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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void reload() {
11b13b2014-08-16Martin Nilsson  resync();
e7621f2009-02-02Stephen R. van den Berg }
0fb5742014-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"); }
8384822014-11-12Stephen R. van den Berg private void resync_cb() {
16ce8b2014-09-12Stephen R. van den Berg  switch(backendstatus) { case 'T':case 'E':
615d422014-10-31Stephen R. van den Berg  foreach(_prepareds;;mapping tp) {
16ce8b2014-09-12Stephen R. van den Berg  m_delete(tp,"datatypeoid"); m_delete(tp,"datarowdesc");
8059252014-11-25Stephen R. van den Berg  m_delete(tp,"datarowtypes");
16ce8b2014-09-12Stephen R. van den Berg  }
0fb5742014-11-13Stephen R. van den Berg  Thread.Thread(reset_dbsession); // Urgently and deadlockfree
16ce8b2014-09-12Stephen R. van den Berg  } }
8384822014-11-12Stephen R. van den Berg private void sendsync() {
3e8df22014-10-29Stephen R. van den Berg  _readyforquerycount++;
2eef0b2014-11-15Stephen R. van den Berg  c->start()->sendcmd(SYNCSEND);
3e8df22014-10-29Stephen R. van den Berg }
e7621f2009-02-02Stephen R. van den Berg //! 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. //!
aebf352014-11-26Stephen R. van den Berg //! @note //! This function @b{can@} raise exceptions. //!
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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void resync() {
11b13b2014-08-16Martin Nilsson  mixed err;
8781972014-11-21Stephen R. van den Berg  if(is_open()) { err = catch {
2e28a92017-06-18Stephen R. van den Berg  PD("Statementsinflight: %d Portalsinflight: %d\n", _statementsinflight, _portalsinflight);
8781972014-11-21Stephen R. van den Berg  if(!waitforauthready) { readyforquery_cb=resync_cb; sendsync(); } return; }; PD("%O\n",err); }
74b6052014-11-20Stephen R. van den Berg  if(!reconnect()&&sizeof(lastmessage))
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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void set_notify_callback(string condition,
a2014e2008-07-14Stephen R. van den Berg  void|function(int,string,string,mixed ...:void) notify_cb,void|int selfnotify,
16ce8b2014-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);
16ce8b2014-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; } }
8384822014-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]))
2c98002014-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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final string quote(string s) {
5716222014-11-09Stephen R. van den Berg  waitauthready();
16ce8b2014-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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final string server_info () {
5716222014-11-09Stephen R. van den Berg  waitauthready();
16ce8b2014-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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final array(string) list_dbs (void|string glob) {
11b13b2014-08-16Martin Nilsson  array row,ret=({});
39e1c42014-11-14Stephen R. van den Berg  .pgsql_util.sql_result res=big_query("SELECT d.datname "
91d6542014-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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final array(string) list_tables (void|string glob) {
11b13b2014-08-16Martin Nilsson  array row,ret=({}); // This query might not work on PostgreSQL 7.4
39e1c42014-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.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final 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);
39e1c42014-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", ]);
16ce8b2014-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)
16ce8b2014-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; }
8384822014-11-12Stephen R. van den Berg private string trbackendst(int c) {
16ce8b2014-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.
16ce8b2014-09-12Stephen R. van den Berg final string status_commit() {
11b13b2014-08-16Martin Nilsson  return trbackendst(backendstatus);
a2014e2008-07-14Stephen R. van den Berg }
8384822014-11-12Stephen R. van den Berg private inline void closestatement(
c02c1d2017-06-03Stephen R. van den Berg  .pgsql_util.bufcon|.pgsql_util.conxsess plugbuffer,string oldprep) {
615d422014-10-31Stephen R. van den Berg  .pgsql_util.closestatement(plugbuffer,oldprep); }
8384822014-11-12Stephen R. van den Berg private inline string int2hex(int i) {
615d422014-10-31Stephen R. van den Berg  return String.int2hex(i);
16ce8b2014-09-12Stephen R. van den Berg }
8384822014-11-12Stephen R. van den Berg private inline void throwdelayederror(object parent) {
16ce8b2014-09-12Stephen R. van den Berg  .pgsql_util.throwdelayederror(parent);
d1a5a42008-08-27Stephen 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.
f91bc22014-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
ac824b2014-11-24Stephen R. van den Berg //! be parsed before the previous statements have been executed //! (e.g. references to temporary tables created in the preceding //! statement), //! but it can speed up parsing due to increased parallelism.
2c228b2009-02-15Stephen R. van den Berg //! @endmapping //!
b33f1b2014-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
39e1c42014-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
16ce8b2014-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],
39e1c42014-11-14Stephen R. van den Berg //! @[Sql.Sql()->query()], @[Sql.pgsql_util.sql_result]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final .pgsql_util.sql_result big_query(string q,
91d6542014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings, void|int _alltyped) {
16ce8b2014-09-12Stephen R. van den Berg  throwdelayederror(this);
11b13b2014-08-16Martin Nilsson  string preparedname="";
1412f32014-11-13Stephen R. van den Berg  int forcecache=-1, forcetext=_options.text_query; int syncparse=zero_type(_options.sync_parse)?-1:_options.sync_parse;
5716222014-11-09Stephen R. van den Berg  if(waitforauthready) waitauthready();
26c0ad2009-02-28Stephen R. van den Berg  string cenc=_runtimeparameter[CLIENT_ENCODING];
16ce8b2014-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;
16ce8b2014-09-12Stephen R. van den Berg  if(bindings) {
3c93de2014-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;
d1e1c02014-11-10Stephen R. van den Berg  case ":_sync":
f91bc22014-11-10Stephen R. van den Berg  syncparse=(int)value; break;
3c93de2014-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  }
3c93de2014-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  }
16ce8b2014-09-12Stephen R. van den Berg  } else
3c93de2014-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;
3c93de2014-11-03Stephen R. van den Berg  if(!forcetext && forcecache==1
615d422014-10-31Stephen R. van den Berg  || forcecache!=0 && (sizeof(q)>=MINPREPARELENGTH || .pgsql_util.cachealways[q])) {
c395a72015-12-09Stephen R. van den Berg  object plugbuffer; while(catch(plugbuffer=c->start())) reconnect();
615d422014-10-31Stephen R. van den Berg  if(tp=_prepareds[q]) {
3e8df22014-10-29Stephen R. van den Berg  if(tp.preparedname) { #ifdef PG_STATS prepstmtused++; #endif preparedname=tp.preparedname; } else if((tstart=tp.trun)
16ce8b2014-09-12Stephen R. van den Berg  && tp.tparse*FACTORPLAN>=tstart
1412f32014-11-13Stephen R. van den Berg  && (undefinedp(_options.cache_autoprepared_statements) || _options.cache_autoprepared_statements))
615d422014-10-31Stephen R. van den Berg  preparedname=PREPSTMTPREFIX+int2hex(pstmtcount++);
16ce8b2014-09-12Stephen R. van den Berg  } else {
11b13b2014-08-16Martin Nilsson  if(totalhits>=cachedepth)
615d422014-10-31Stephen R. van den Berg  foreach(_prepareds;string ind;tp) {
16ce8b2014-09-12Stephen R. van den Berg  int oldhits=tp.hits; totalhits-=oldhits-(tp.hits=oldhits>>1); if(oldhits<=1) { closestatement(plugbuffer,tp.preparedname);
615d422014-10-31Stephen R. van den Berg  m_delete(_prepareds,ind);
a2014e2008-07-14Stephen R. van den Berg  }
c0f2b72009-04-10Stephen R. van den Berg  }
615d422014-10-31Stephen R. van den Berg  if(forcecache!=1 && .pgsql_util.createprefix->match(q)) {
6623742017-06-17Stephen R. van den Berg  PD("Invalidate cache\n");
3c93de2014-11-03Stephen R. van den Berg  invalidatecache=1; // Flush cache on CREATE
11b13b2014-08-16Martin Nilsson  tp=UNDEFINED;
16ce8b2014-09-12Stephen R. van den Berg  } else
615d422014-10-31Stephen R. van den Berg  _prepareds[q]=tp=([]);
a2014e2008-07-14Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  if(invalidatecache) {
11b13b2014-08-16Martin Nilsson  invalidatecache=0;
615d422014-10-31Stephen R. van den Berg  foreach(_prepareds;;mapping np) {
16ce8b2014-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  } }
c02c1d2017-06-03Stephen R. van den Berg  if(sizeof(CHAIN(plugbuffer))) { PD("%O\n",(string)CHAIN(plugbuffer));
2eef0b2014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(FLUSHSEND); // close expireds
16ce8b2014-09-12Stephen R. van den Berg  } else
2eef0b2014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(KEEP); // close start()
a2014e2008-07-14Stephen R. van den Berg  tstart=gethrtime();
39e1c42014-11-14Stephen R. van den Berg  } else // sql_result autoassigns to portal
cb26232008-08-04Stephen R. van den Berg  tp=UNDEFINED;
39e1c42014-11-14Stephen R. van den Berg  .pgsql_util.sql_result portal; portal=.pgsql_util.sql_result(this,c,q,
657cd92017-02-04Stephen R. van den Berg  portalbuffersize, _alltyped, from, forcetext, timeout, syncparse);
16ce8b2014-09-12Stephen R. van den Berg  portal._tprepared=tp; #ifdef PG_STATS portalsopened++; #endif clearmessage=1;
3e8df22014-10-29Stephen R. van den Berg  if(forcetext) { // FIXME What happens if portals are still open?
615d422014-10-31Stephen R. van den Berg  portal._unnamedportalkey=_unnamedportalmux->lock(1);
3c93de2014-11-03Stephen R. van den Berg  portal._portalname="";
6623742017-06-17Stephen R. van den Berg  portal->_parseportal(); portal->_bindportal();
3e8df22014-10-29Stephen R. van den Berg  _readyforquerycount++; Thread.MutexKey lock=unnamedstatement->lock(1);
c02c1d2017-06-03Stephen R. van den Berg  .pgsql_util.conxsess cs = c->start(1); CHAIN(cs)->add_int8('Q')->add_hstring(({q, 0}), 4, 4); cs->sendcmd(FLUSHLOGSEND,portal);
3e8df22014-10-29Stephen R. van den Berg  lock=0;
16ce8b2014-09-12Stephen R. van den Berg  PD("Simple query: %O\n",q); } else {
3c93de2014-11-03Stephen R. van den Berg  object plugbuffer;
6623742017-06-17Stephen R. van den Berg  portal->_parseportal();
16ce8b2014-09-12Stephen R. van den Berg  if(!sizeof(preparedname) || !tp || !tp.preparedname) { if(!sizeof(preparedname)) preparedname=
6623742017-06-17Stephen R. van den Berg  (portal._unnamedstatementkey = unnamedstatement->trylock(1))
615d422014-10-31Stephen R. van den Berg  ? "" : PTSTMTPREFIX+int2hex(ptstmtcount++);
7214f62014-11-24Stephen R. van den Berg  PD("Parse statement %O=%O\n",preparedname,q);
c02c1d2017-06-03Stephen R. van den Berg  plugbuffer = c->start(); CHAIN(plugbuffer)->add_int8('P')
7214f62014-11-24Stephen R. van den Berg  ->add_hstring(({preparedname,0,q,"\0\0\0"}),4,4) #if 0
16ce8b2014-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
934f5f2014-11-20Stephen R. van den Berg  // In v8.4 and later, things speed up slightly when it is omitted.
7214f62014-11-24Stephen R. van den Berg  ->add(PGFLUSH) #endif ;
16ce8b2014-09-12Stephen R. van den Berg  }
9d350f2014-11-18Stephen R. van den Berg  portal._preparedname=preparedname;
16ce8b2014-09-12Stephen R. van den Berg  if(!tp || !tp.datatypeoid) { PD("Describe statement %O\n",preparedname);
c02c1d2017-06-03Stephen R. van den Berg  if (!plugbuffer) plugbuffer = c->start(); CHAIN(plugbuffer)->add_int8('D') ->add_hstring(({'S', preparedname, 0}), 4, 4); plugbuffer->sendcmd(FLUSHSEND,portal);
16ce8b2014-09-12Stephen R. van den Berg  } else {
3c93de2014-11-03Stephen R. van den Berg  if(plugbuffer)
2eef0b2014-11-15Stephen R. van den Berg  plugbuffer->sendcmd(KEEP);
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_STATS skippeddescribe++; #endif
8059252014-11-25Stephen R. van den Berg  portal->_setrowdesc(tp.datarowdesc,tp.datarowtypes);
16ce8b2014-09-12Stephen R. van den Berg  } if((portal._tprepared=tp) && tp.datatypeoid) {
075c542014-11-03Stephen R. van den Berg  mixed e=catch(portal->_preparebind(tp.datatypeoid));
178e982016-11-07Stephen R. van den Berg  if (e && !portal._delayederror) { portal._unnamedstatementkey = 0; // Release early, release often
3c93de2014-11-03Stephen R. van den Berg  throw(e);
178e982016-11-07Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  }
178e982016-11-07Stephen R. van den Berg  if (!unnamedstatement)
f19c202016-11-07Stephen R. van den Berg  portal._unnamedstatementkey = 0; // Cover for a destruct race
16ce8b2014-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]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final .pgsql_util.sql_result streaming_query(string q,
91d6542014-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]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final .pgsql_util.sql_result big_typed_query(string q,
91d6542014-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 }