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
b7ae0e2017-06-29Stephen R. van den Berg //! PostgreSQL server running on @b{your@} host to use this module: you can //! connect to the database over a TCP/IP socket on a different host.
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
f0ce5e2017-11-16Stephen R. van den Berg //! currently supported are: cleartext, md5 and scram (recommended).
2c228b2009-02-15Stephen R. van den Berg //! @item
57fdb22017-11-21Stephen R. van den Berg //! Optional asynchronous query interface through callbacks. //! @item
2c228b2009-02-15Stephen R. van den Berg //! 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
1f33812017-11-21Stephen R. van den Berg //! Multiple simultaneous queries on the same database connection are a
4e1d1f2008-08-01Stephen R. van den Berg //! 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
63fcae2017-11-22Stephen R. van den Berg //! @[Sql.Connection], @[Sql.postgres],
57fdb22017-11-21Stephen R. van den Berg //! @url{https://www.postgresql.org/docs/current/static/@}
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;
f66d602017-11-18Stephen R. van den Berg private .pgsql_util.proxy proxy;
16ce8b2014-09-12Stephen R. van den Berg 
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
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
f66d602017-11-18Stephen R. van den Berg private int cachedepth = STATEMENTCACHEDEPTH; private int portalbuffersize = PORTALBUFFERSIZE; private int timeout = QUERYTIMEOUT;
aa81772018-01-26Stephen R. van den Berg private array connparmcache; private int reconnected;
a2014e2008-07-14Stephen R. van den Berg 
67879d2017-11-10Stephen R. van den Berg protected string _sprintf(int type) {
f66d602017-11-18Stephen R. van den Berg  string res;
16ce8b2014-09-12Stephen R. van den Berg  switch(type) { case 'O':
f66d602017-11-18Stephen R. van den Berg  res = sprintf(DRIVERNAME"(%s@%s:%d/%s,%d,%d)", proxy.user, proxy.host, proxy.port, proxy.database, proxy.c?->socket && proxy.c->socket->query_fd(), proxy.backendpid);
16ce8b2014-09-12Stephen R. van den Berg  break;
a2014e2008-07-14Stephen R. van den Berg  } return res; }
57fdb22017-11-21Stephen R. van den Berg //! With no arguments, this function initialises 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
aa81772018-01-26Stephen R. van den Berg //! @member int "reconnect" //! Set it to zero to disable automatic reconnects upon losing //! 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 //! 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
57fdb22017-11-21Stephen R. van den Berg //! by semicolons (not recommended).
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
57fdb22017-11-21Stephen R. van den Berg //! You need to have a database selected before using the SQL-object,
a2014e2008-07-14Stephen R. van den Berg //! 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 //!
57fdb22017-11-21Stephen R. van den Berg //! @note //! It is possible that the exception from a failed connect //! will not be triggered on this call (because the connect //! proceeds asynchronously in the background), but on the first //! attempt to actually use the database instead. //!
a2014e2008-07-14Stephen R. van den Berg //! @seealso
63fcae2017-11-22Stephen R. van den Berg //! @[Postgres.postgres], @[Sql.Connection], @[select_db()],
57fdb22017-11-21Stephen R. van den Berg //! @url{https://www.postgresql.org/docs/current/static/runtime-config-client.html@}
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) {
f66d602017-11-18Stephen R. van den Berg  string spass = pass && pass != "" ? Standards.IDNA.to_ascii(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"; }
aa81772018-01-26Stephen R. van den Berg  connparmcache = ({ host, database,
f66d602017-11-18Stephen R. van den Berg  user && user != "" ? Standards.IDNA.to_ascii(user, 1) : user,
aa81772018-01-26Stephen R. van den Berg  spass, options || ([])}); proxy = .pgsql_util.proxy(@connparmcache);
a2014e2008-07-14Stephen R. van den Berg }
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) {
8b6d742018-01-31Stephen R. van den Berg  throwdelayederror(proxy);
f66d602017-11-18Stephen R. van den Berg  return proxy.geterror(clear);
a2014e2008-07-14Stephen R. van den Berg } //! This function returns a string describing what host are we talking to,
57fdb22017-11-21Stephen R. van den Berg //! and how (TCP/IP or UNIX socket).
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() {
f66d602017-11-18Stephen R. van den Berg  return proxy.host_info();
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
57fdb22017-11-21Stephen R. van den Berg //! is alive, please use @[ping()] instead.
6b4ea82012-04-10Stephen R. van den Berg //! //! @seealso //! @[ping()]
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int is_open() {
f66d602017-11-18Stephen R. van den Berg  return proxy.is_open();
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 //! 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() {
e1963e2017-11-15Stephen R. van den Berg  waitauthready();
aa81772018-01-26Stephen R. van den Berg  return is_open() && !catch(proxy.c->start()->sendcmd(FLUSHSEND)) ? !!reconnected : -1;
6b4ea82012-04-10Stephen 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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void cancelquery() {
f66d602017-11-18Stephen R. van den Berg  proxy.cancelquery();
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()],
57fdb22017-11-21Stephen R. van den Berg //! @url{https://www.postgresql.org/docs/current/static/multibyte.html@}
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void set_charset(string charset) {
4f2ed82014-11-15Stephen R. van den Berg  if(charset)
f66d602017-11-18Stephen R. van den Berg  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()],
57fdb22017-11-21Stephen R. van den Berg //! @url{https://www.postgresql.org/docs/current/static/multibyte.html@}
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final string get_charset() {
f66d602017-11-18Stephen R. van den Berg  return getruntimeparameters()[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
57fdb22017-11-21Stephen R. van den Berg //! @url{https://www.postgresql.org/docs/current/static/runtime-config-client.html@}
2c228b2009-02-15Stephen R. van den Berg //! //! @note
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final mapping(string:string) getruntimeparameters() {
5716222014-11-09Stephen R. van den Berg  waitauthready();
f66d602017-11-18Stephen R. van den Berg  return proxy.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 "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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final mapping(string:mixed) getstatistics() {
f66d602017-11-18Stephen R. van den Berg  mapping(string:mixed) stats = ([ "warnings_dropped":proxy.warningsdropcount, "current_prepared_statements":sizeof(proxy.prepareds), "current_prepared_statement_hits":proxy.totalhits,
cb26232008-08-04Stephen R. van den Berg  "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
f66d602017-11-18Stephen R. van den Berg  "messages_received":proxy.msgsreceived, "bytes_received":proxy.bytesreceived,
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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int setcachedepth(void|int newdepth) {
f66d602017-11-18Stephen R. van den Berg  int olddepth = cachedepth; if (!undefinedp(newdepth) && newdepth >= 0) cachedepth = newdepth;
a2014e2008-07-14Stephen R. van den Berg  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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int settimeout(void|int newtimeout) {
f66d602017-11-18Stephen R. van den Berg  int oldtimeout = timeout; if (!undefinedp(newtimeout) && newtimeout > 0) timeout = newtimeout;
a2014e2008-07-14Stephen R. van den Berg  return oldtimeout; }
f4c9d62009-02-15Stephen R. van den Berg //! @param newportalbuffersize //! Sets the new portalbuffersize for buffering partially concurrent queries. //! //! @returns //! The previous portalbuffersize.
a2014e2008-07-14Stephen R. van den Berg //!
2c228b2009-02-15Stephen R. van den Berg //! @note
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int setportalbuffersize(void|int newportalbuffersize) {
f66d602017-11-18Stephen R. van den Berg  int oldportalbuffersize = portalbuffersize; if (!undefinedp(newportalbuffersize) && newportalbuffersize>0) portalbuffersize = newportalbuffersize;
a2014e2008-07-14Stephen R. van den Berg  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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final int setfetchlimit(void|int newfetchlimit) {
f66d602017-11-18Stephen R. van den Berg  int oldfetchlimit = proxy._fetchlimit; if (!undefinedp(newfetchlimit) && newfetchlimit >= 0) proxy._fetchlimit = newfetchlimit;
a2014e2008-07-14Stephen R. van den Berg  return oldfetchlimit; }
8384822014-11-12Stephen R. van den Berg private string glob2reg(string glob) {
f66d602017-11-18Stephen R. van den Berg  if (!glob || !sizeof(glob))
a2014e2008-07-14Stephen R. van den Berg  return "%";
f66d602017-11-18Stephen R. van den Berg  return replace(glob, ({"*", "?", "\\", "%", "_"}), ({"%", "_", "\\\\", "\\%", "\\_"}));
16ce8b2014-09-12Stephen R. van den Berg }
8384822014-11-12Stephen R. van den Berg private void waitauthready() {
f66d602017-11-18Stephen R. van den Berg  if (proxy.waitforauthready) {
60f4ad2016-02-17Stephen R. van den Berg  PD("%d Wait for auth ready %O\n",
f66d602017-11-18Stephen R. van den Berg  proxy.c?->socket && proxy.c->socket->query_fd(), backtrace()[-2]); Thread.MutexKey lock = proxy.shortmux->lock(); catch(PT(proxy.waitforauthready->wait(lock))); PD("%d Wait for auth ready released.\n", proxy.c?->socket && proxy.c->socket->query_fd());
5716222014-11-09Stephen 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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void close() {
f66d602017-11-18Stephen R. van den Berg  proxy.close();
a2014e2008-07-14Stephen R. van den Berg }
c071bc2017-11-05Henrik Grubbström (Grubba) protected void _destruct() {
f66d602017-11-18Stephen R. van den Berg  destruct(proxy);
cb26232008-08-04Stephen 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() {
dc915d2018-06-05Stephen R. van den Berg  proxy.statementsinflight->wait_till_drained(); proxy.delayederror = 0; error(1);
0fb5742014-11-13Stephen R. van den Berg  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() {
f66d602017-11-18Stephen R. van den Berg  switch (proxy.backendstatus) {
16ce8b2014-09-12Stephen R. van den Berg  case 'T':case 'E':
f66d602017-11-18Stephen R. van den Berg  foreach (proxy.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  } }
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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void resync() {
11b13b2014-08-16Martin Nilsson  mixed err;
f66d602017-11-18Stephen R. van den Berg  if (is_open()) {
8781972014-11-21Stephen R. van den Berg  err = catch {
2e28a92017-06-18Stephen R. van den Berg  PD("Statementsinflight: %d Portalsinflight: %d\n",
f66d602017-11-18Stephen R. van den Berg  proxy.statementsinflight, proxy.portalsinflight); if(!proxy.waitforauthready) { proxy.readyforquery_cb = resync_cb; proxy.sendsync();
8781972014-11-21Stephen R. van den Berg  } return; };
f66d602017-11-18Stephen R. van den Berg  PD("%O\n", err);
8781972014-11-21Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  if (sizeof(proxy.lastmessage)) ERROR(proxy.a2nls(proxy.lastmessage));
a2014e2008-07-14Stephen R. van den Berg }
e1963e2017-11-15Stephen R. van den Berg //! Due to restrictions of the Postgres frontend-backend protocol, you always //! already have to be connected to a database. //! To connect to a different database you have to select the right //! database while connecting instead. This function is a no-op when //! specifying the same database, and throws an error otherwise.
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) {
f66d602017-11-18Stephen R. van den Berg  if (proxy.database != dbname)
e1963e2017-11-15Stephen R. van den Berg  ERROR("Cannot switch databases from %O to %O" " in an already established connection\n",
f66d602017-11-18Stephen R. van den Berg  proxy.database, dbname);
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
e1963e2017-11-15Stephen R. van den Berg //! @expr{"_lost"@} which gets called whenever the connection //! to the database unexpectedly drops.
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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
e7c5c82014-11-24Stephen R. van den Berg /*semi*/final void set_notify_callback(string condition,
f66d602017-11-18Stephen 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) {
f66d602017-11-18Stephen R. van den Berg  if (!notify_cb) m_delete(proxy.notifylist, condition);
16ce8b2014-09-12Stephen R. van den Berg  else {
f66d602017-11-18Stephen R. van den Berg  array old = proxy.notifylist[condition]; if (!old) old = ({notify_cb}); if (selfnotify || args) old += ({selfnotify}); if (args) old += args; proxy.notifylist[condition] = old;
a2014e2008-07-14Stephen 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();
f66d602017-11-18Stephen R. van den Berg  string r = proxy.runtimeparameter.standard_conforming_strings; if (r && r == "on")
a2014e2008-07-14Stephen R. van den Berg  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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
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) {
f66d602017-11-18Stephen 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) {
f66d602017-11-18Stephen 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();
f66d602017-11-18Stephen R. van den Berg  return DRIVERNAME"/" + (proxy.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) {
f66d602017-11-18Stephen R. van den Berg  array row, ret = .pgsql_util.emptyarray;
63fcae2017-11-22Stephen R. van den Berg  .pgsql_util.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)]));
f66d602017-11-18Stephen R. van den Berg  while(row = res->fetch_row()) ret += ({row[0]});
c0f2b72009-04-10Stephen R. van den Berg  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) {
f66d602017-11-18Stephen R. van den Berg  array row, ret = .pgsql_util.emptyarray;
63fcae2017-11-22Stephen R. van den Berg  .pgsql_util.Result res = big_query( // due to missing schemasupport
f66d602017-11-18Stephen R. van den Berg  // This query might not work on PostgreSQL 7.4
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)]));
f66d602017-11-18Stephen R. van den Berg  while(row = res->fetch_row()) ret += ({row[0]});
c0f2b72009-04-10Stephen R. van den Berg  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) {
f66d602017-11-18Stephen R. van den Berg  array row, ret = .pgsql_util.emptyarray; string schema;
a2014e2008-07-14Stephen R. van den Berg  sscanf(table||"*", "%s.%s", schema, table);
63fcae2017-11-22Stephen R. van den Berg  .pgsql_util.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  {
f66d602017-11-18Stephen R. van den Berg  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", ]);
f66d602017-11-18Stephen 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])
f66d602017-11-18Stephen R. van den Berg  field=nf;
a2014e2008-07-14Stephen R. van den Berg  colnames[i]=field; } }
f66d602017-11-18Stephen R. van den Berg #define delifzero(m, field) if(!(m)[field]) m_delete(m, field)
a2014e2008-07-14Stephen R. van den Berg 
16ce8b2014-09-12Stephen R. van den Berg  while(row=res->fetch_row()) {
f66d602017-11-18Stephen R. van den Berg  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");
f66d602017-11-18Stephen R. van den Berg  ret += ({m});
a2014e2008-07-14Stephen R. van den Berg  } 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
f0ce5e2017-11-16Stephen R. van den Berg //! This function is PostgreSQL-specific.
16ce8b2014-09-12Stephen R. van den Berg final string status_commit() {
f66d602017-11-18Stephen R. van den Berg  return trbackendst(proxy.backendstatus);
a2014e2008-07-14Stephen R. van den Berg }
8384822014-11-12Stephen R. van den Berg private inline void closestatement(
f66d602017-11-18Stephen R. van den Berg  .pgsql_util.bufcon|.pgsql_util.conxsess plugbuffer, string oldprep) { .pgsql_util.closestatement(plugbuffer, oldprep);
615d422014-10-31Stephen R. van den Berg }
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 }
1a2da92017-11-30Stephen R. van den Berg private void startquery(int forcetext, .pgsql_util.Result portal, string q, mapping(string:mixed) tp, string preparedname) { .pgsql_util.conxion c = proxy.c;
aa81772018-01-26Stephen R. van den Berg  if (!c && (proxy.options["reconnect"] || zero_type(proxy.options["reconnect"]))) { sleep(BACKOFFDELAY); // Force a backoff delay if (!proxy.c) { reconnected++; proxy = .pgsql_util.proxy(@connparmcache); } c = proxy.c; }
1a2da92017-11-30Stephen R. van den Berg  if (forcetext) { // FIXME What happens if portals are still open? portal._unnamedportalkey = proxy.unnamedportalmux->lock(1); portal._portalname = ""; portal->_parseportal(); portal->_bindportal(); proxy.readyforquerycount++; { Thread.MutexKey lock = proxy.unnamedstatement->lock(1); .pgsql_util.conxsess cs = c->start(1); CHAIN(cs)->add_int8('Q')->add_hstring(({q, 0}), 4, 4); cs->sendcmd(FLUSHLOGSEND, portal); } PD("Simple query: %O\n", q); } else { object plugbuffer; portal->_parseportal(); if (!sizeof(preparedname) || !tp || !tp.preparedname) { if (!sizeof(preparedname)) preparedname = (portal._unnamedstatementkey = proxy.unnamedstatement->trylock(1)) ? "" : PTSTMTPREFIX + int2hex(ptstmtcount++); PD("Parse statement %O=%O\n", preparedname, q); plugbuffer = c->start(); CHAIN(plugbuffer)->add_int8('P') ->add_hstring(({preparedname, 0, q, "\0\0\0"}), 4, 4) #if 0 // 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 // In v8.4 and later, things speed up slightly when it is omitted. ->add(PGFLUSH) #endif ; } else { // Use the name from the cache preparedname = tp.preparedname; // to shortcut a potential race PD("Using prepared statement %s for %O\n", preparedname, q); } portal._preparedname = preparedname; if (!tp || !tp.datatypeoid) { PD("Describe statement %O\n", preparedname); if (!plugbuffer) plugbuffer = c->start(); CHAIN(plugbuffer)->add_int8('D') ->add_hstring(({'S', preparedname, 0}), 4, 4); plugbuffer->sendcmd(FLUSHSEND, portal); } else { if (plugbuffer) plugbuffer->sendcmd(KEEP); #ifdef PG_STATS skippeddescribe++; #endif portal->_setrowdesc(tp.datarowdesc, tp.datarowtypes); } if ((portal._tprepared=tp) && tp.datatypeoid) { mixed e = catch(portal->_preparebind(tp.datatypeoid)); if (e && !portal.delayederror) { portal._unnamedstatementkey = 0; // Release early, release often throw(e); } } if (!proxy.unnamedstatement) portal._unnamedstatementkey = 0; // Cover for a destruct race } }
57fdb22017-11-21Stephen R. van den Berg //! This is the only provided direct interface which allows you to query the //! database. A simpler synchronous interface can be used through @[query()].
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
d2e6342017-12-22Stephen R. van den Berg //! The parameters referenced via the @expr{bindings@}-parameter-mapping //! passed to this function must remain unaltered
b33f1b2014-11-15Stephen R. van den Berg //! 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
63fcae2017-11-22Stephen R. van den Berg //! A @[Sql.pgsql_util.Result] object (which conforms to the //! @[Sql.Result] standard interface for accessing data). It is
57fdb22017-11-21Stephen R. van den Berg //! recommended to use @[query()] for simpler queries (because
f4c9d62009-02-15Stephen R. van den Berg //! it is easier to handle, but stores all the result in memory), and
57fdb22017-11-21Stephen R. van den Berg //! @[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
57fdb22017-11-21Stephen R. van den Berg //! @ref{:_text@} option (not recommended).
17b7532008-07-30Stephen R. van den Berg //!
a2014e2008-07-14Stephen R. van den Berg //! @seealso
63fcae2017-11-22Stephen R. van den Berg //! @[big_typed_query()], @[Sql.Connection], @[Sql.Result], //! @[query()], @[Sql.pgsql_util.Result]
7ae2502017-11-24Stephen R. van den Berg /*semi*/final variant .pgsql_util.Result big_query(string q,
91d6542014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings, void|int _alltyped) {
8b6d742018-01-31Stephen R. van den Berg  throwdelayederror(proxy);
f66d602017-11-18Stephen R. van den Berg  string preparedname = ""; mapping(string:mixed) options = proxy.options; .pgsql_util.conxion c = proxy.c; int forcecache = -1, forcetext = options.text_query; int syncparse = zero_type(options.sync_parse) ? -1 : options.sync_parse; if (proxy.waitforauthready)
5716222014-11-09Stephen R. van den Berg  waitauthready();
f66d602017-11-18Stephen R. van den Berg  string cenc = proxy.runtimeparameter[CLIENT_ENCODING];
16ce8b2014-09-12Stephen R. van den Berg  switch(cenc) { case UTF8CHARSET:
f66d602017-11-18Stephen R. van den Berg  q = string_to_utf8(q);
16ce8b2014-09-12Stephen R. van den Berg  break; default:
f66d602017-11-18Stephen R. van den Berg  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;
f66d602017-11-18Stephen R. van den Berg  if (bindings) { if (forcetext) q = .sql_util.emulate_bindings(q, bindings, this), paramValues = .pgsql_util.emptyarray;
3c93de2014-11-03Stephen R. van den Berg  else {
bea3fb2018-05-26Stephen R. van den Berg  int pi = 0;
f66d602017-11-18Stephen R. van den Berg  paramValues = allocate(sizeof(bindings)); from = allocate(sizeof(bindings));
bea3fb2018-05-26Stephen R. van den Berg  array(string) litfrom, litto, to = allocate(sizeof(bindings)); litfrom = litto = .pgsql_util.emptyarray;
f66d602017-11-18Stephen R. van den Berg  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
3c93de2014-11-03Stephen R. van den Berg  switch(name) { case ":_cache":
f66d602017-11-18Stephen R. van den Berg  forcecache = (int)value;
3c93de2014-11-03Stephen R. van den Berg  break; case ":_text":
f66d602017-11-18Stephen R. van den Berg  forcetext = (int)value;
3c93de2014-11-03Stephen R. van den Berg  break;
d1e1c02014-11-10Stephen R. van den Berg  case ":_sync":
f66d602017-11-18Stephen R. van den Berg  syncparse = (int)value;
f91bc22014-11-10Stephen R. van den Berg  break;
3c93de2014-11-03Stephen R. van den Berg  } continue; }
f66d602017-11-18Stephen R. van den Berg  if (!has_value(q, name))
3c93de2014-11-03Stephen R. van den Berg  continue; }
bea3fb2018-05-26Stephen R. van den Berg  if (multisetp(value)) { // multisets are taken literally litto += ({indices(value)*","}); // and bypass the encoding logic litfrom += ({name}); } else { paramValues[pi] = value; to[pi] = sprintf("$%d", pi + 1); from[pi++] = name;
3c93de2014-11-03Stephen R. van den Berg  }
a2014e2008-07-14Stephen R. van den Berg  }
bea3fb2018-05-26Stephen R. van den Berg  if (pi--) { paramValues = paramValues[.. pi]; q = replace(q, litfrom += from = from[.. pi], litto += to = to[.. pi]); } else { paramValues = .pgsql_util.emptyarray; if (sizeof(litfrom)) q = replace(q, litfrom, litto); }
f66d602017-11-18Stephen R. van den Berg  from = ({from, to, paramValues});
a2014e2008-07-14Stephen R. van den Berg  }
16ce8b2014-09-12Stephen R. van den Berg  } else
f66d602017-11-18Stephen R. van den Berg  paramValues = .pgsql_util.emptyarray; if (String.width(q) > 8) ERROR("Wide string literals in %O not supported\n", q); if (has_value(q, "\0")) ERROR("Querystring %O contains invalid literal nul-characters\n", q);
cb26232008-08-04Stephen R. van den Berg  mapping(string:mixed) tp;
55a08b2017-06-25Stephen R. van den Berg  /* * FIXME What happens with regards to this detection when presented with * multistatement text-queries?
66d6ff2017-06-27Stephen R. van den Berg  * The primary function of this detection is to ensure a SYNC * right after a COMMIT, and no SYNC after a BEGIN.
55a08b2017-06-25Stephen R. van den Berg  */ int transtype = .pgsql_util.transendprefix->match(q) ? TRANSEND : .pgsql_util.transbeginprefix->match(q) ? TRANSBEGIN : NOTRANS;
2aeb8a2017-06-27Stephen R. van den Berg  if (transtype != NOTRANS) tp = .pgsql_util.describenodata; // Description already known else if (!forcetext && forcecache == 1 || forcecache && sizeof(q) >= MINPREPARELENGTH) {
e1963e2017-11-15Stephen R. van den Berg  object plugbuffer = c->start();
f66d602017-11-18Stephen R. van den Berg  if (tp = proxy.prepareds[q]) { if (tp.preparedname) {
3e8df22014-10-29Stephen R. van den Berg #ifdef PG_STATS
f66d602017-11-18Stephen R. van den Berg  prepstmtused++;
3e8df22014-10-29Stephen R. van den Berg #endif
f66d602017-11-18Stephen R. van den Berg  preparedname = tp.preparedname;
1a2da92017-11-30Stephen R. van den Berg  } else if(tp.trun && tp.tparse*FACTORPLAN >= tp.trun
f66d602017-11-18Stephen R. van den Berg  && (undefinedp(options.cache_autoprepared_statements) || options.cache_autoprepared_statements)) preparedname = PREPSTMTPREFIX + int2hex(pstmtcount++);
16ce8b2014-09-12Stephen R. van den Berg  } else {
f66d602017-11-18Stephen R. van den Berg  if (proxy.totalhits >= cachedepth) foreach (proxy.prepareds; string ind; tp) { int oldhits = tp.hits; proxy.totalhits -= oldhits-(tp.hits = oldhits >> 1); if (oldhits <= 1) { closestatement(plugbuffer, tp.preparedname); m_delete(proxy.prepareds, ind); } } if (forcecache != 1 && .pgsql_util.createprefix->match(q)) {
6623742017-06-17Stephen R. van den Berg  PD("Invalidate cache\n");
f66d602017-11-18Stephen R. van den Berg  proxy.invalidatecache = 1; // Flush cache on CREATE
2aeb8a2017-06-27Stephen R. van den Berg  tp = 0;
16ce8b2014-09-12Stephen R. van den Berg  } else
f66d602017-11-18Stephen R. van den Berg  proxy.prepareds[q] = tp = ([]);
a2014e2008-07-14Stephen R. van den Berg  }
f66d602017-11-18Stephen R. van den Berg  if (proxy.invalidatecache) { proxy.invalidatecache = 0; foreach (proxy.prepareds; ; mapping np) { closestatement(plugbuffer, np.preparedname); m_delete(np, "preparedname");
d1a5a42008-08-27Stephen R. van den Berg  } }
f66d602017-11-18Stephen R. van den Berg  if (sizeof(CHAIN(plugbuffer))) {
1a2da92017-11-30Stephen R. van den Berg  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()
63fcae2017-11-22Stephen R. van den Berg  } else // Result autoassigns to portal
2aeb8a2017-06-27Stephen R. van den Berg  tp = 0;
63fcae2017-11-22Stephen R. van den Berg  .pgsql_util.Result portal; portal = .pgsql_util.Result(proxy, c, q, portalbuffersize, _alltyped,
f66d602017-11-18Stephen R. van den Berg  from, forcetext, timeout, syncparse, transtype); portal._tprepared = tp;
16ce8b2014-09-12Stephen R. van den Berg #ifdef PG_STATS portalsopened++; #endif
f66d602017-11-18Stephen R. van den Berg  proxy.clearmessage = 1;
1a2da92017-11-30Stephen R. van den Berg  // Do not run a query in the local_backend to prevent deadlocks if (Thread.this_thread() == .pgsql_util.local_backend.executing_thread())
6e41052017-11-30Stephen R. van den Berg  Thread.Thread(startquery, forcetext, portal, q, tp, preparedname);
1a2da92017-11-30Stephen R. van den Berg  else startquery(forcetext, portal, q, tp, preparedname);
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
d62e942018-05-16Stephen R. van den Berg //! @[big_query()], @[big_typed_query()], @[streaming_typed_query()], //! @[Sql.Connection], @[Sql.Result]
7ae2502017-11-24Stephen R. van den Berg /*semi*/final variant inline .pgsql_util.Result streaming_query(string q,
91d6542014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings) {
f66d602017-11-18Stephen R. van den Berg  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
63fcae2017-11-22Stephen R. van den Berg //! @[big_query()], @[Sql.Connection], @[Sql.Result]
7ae2502017-11-24Stephen R. van den Berg /*semi*/final variant inline .pgsql_util.Result big_typed_query(string q,
91d6542014-11-10Stephen R. van den Berg  void|mapping(string|int:mixed) bindings) {
f66d602017-11-18Stephen R. van den Berg  return big_query(q, bindings, 1);
b156722008-08-25Stephen R. van den Berg }
d62e942018-05-16Stephen R. van den Berg  //! This function returns an object that allows streaming and typed //! results. //! //! @seealso //! @[big_query()], @[Sql.Connection], @[Sql.Result] /*semi*/final variant inline .pgsql_util.Result streaming_typed_query(string q, void|mapping(string|int:mixed) bindings) { return big_query(q, bindings, 1); }