diff --git a/extensions/pgsql/AMBuilder b/extensions/pgsql/AMBuilder index aecbaeaa04..f1c816f5c4 100644 --- a/extensions/pgsql/AMBuilder +++ b/extensions/pgsql/AMBuilder @@ -13,9 +13,8 @@ for cxx in builder.targets: elif compiler.family == 'msvc': compiler.cxxflags += ['/GR-'] - if compiler.target.platform == 'linux' or compiler.target.platform == 'mac': + if compiler.target.platform == 'linux': compiler.postlink += [ - '-lz', '-lpthread', '-lm' ] @@ -26,7 +25,8 @@ for cxx in builder.targets: path = os.path.join(builder.sourcePath, 'extensions', 'pgsql', 'lib_linux64') compiler.postlink += [ os.path.join(path, 'libpq.a'), - '-lrt' + os.path.join(path, 'libpgcommon_shlib.a'), + os.path.join(path, 'libpgport_shlib.a'), ] elif compiler.target.platform == 'windows': if arch == 'x86': @@ -35,18 +35,17 @@ for cxx in builder.targets: path = os.path.join(builder.sourcePath, 'extensions', 'pgsql', 'lib_win64') compiler.postlink += [ os.path.join(path, 'libpq.lib'), + os.path.join(path, 'libpgcommon_shlib.lib'), + os.path.join(path, 'libpgport_shlib.lib'), 'wsock32.lib', 'ws2_32.lib', - 'secur32.lib' - ] - elif compiler.target.platform == 'mac': - if arch == 'x86': - path = os.path.join(builder.sourcePath, 'extensions', 'pgsql', 'lib_darwin') - elif arch == 'x86_64': - path = os.path.join(builder.sourcePath, 'extensions', 'pgsql', 'lib_darwin64') - compiler.postlink += [ - os.path.join(path, 'libpq.a') + 'secur32.lib', + 'wldap32.lib', + 'advapi32.lib', + 'shell32.lib' ] + elif compiler.target.platform == 'mac': # mac isn't supported + continue compiler.cxxincludes += [path] @@ -59,4 +58,3 @@ for cxx in builder.targets: 'extension.cpp' ] SM.extensions += [builder.Add(binary)] - diff --git a/extensions/pgsql/README.md b/extensions/pgsql/README.md index 22b17bc063..6bc93c6b1f 100644 --- a/extensions/pgsql/README.md +++ b/extensions/pgsql/README.md @@ -1,22 +1,82 @@ -# PostgreSQL Build Instructions -As of writing, we're using PostgreSQL v9.6.9 +# PostgreSQL Static Libraries -https://www.postgresql.org/ftp/source/ +This extension bundles PostgreSQL 18.4 static libraries built by: -After building, libpq can be found in src/interfaces/libpq/ +https://github.com/ProjectSky/build-postgresql-library -## Windows -https://www.postgresql.org/docs/9.6/install-windows-libpq.html +Use that project to rebuild or update the bundled libraries. It contains the +current PostgreSQL version, Linux configure flags, Windows Meson flags, and +release packaging details. -Change `src/interfaces/win32.mak` line 35 from `OPT=/O2 /MD` to `OPT=/O2 /MT`. Library will be in `interfaces\libpq\Release\libpq.lib`. -You have to delete the interfaces\libpq\Release folder between x86 and x86_64 builds. +## Manual Build -## Mac -For x86 or x86_64 add -m32 or -m64 to `CFLAGS` +The current build uses PostgreSQL `18.4`. -`CFLAGS='-mmacosx-version-min=10.7' ./configure && make` +1. Install the build dependencies. -## Linux -For x86 or x86_64 add -m32 or -m64 to `CFLAGS` + Linux requires `build-essential gcc-multilib g++-multilib`. -`CFLAGS='-fPIC' ./configure --without-readline && make` + Windows requires an MSVC developer shell, `winflexbison3`, `meson`, and + `ninja`. + +2. Download and unpack [postgresql-18.4.tar.gz](https://ftp.postgresql.org/pub/latest/postgresql-18.4.tar.gz) + +3. For Linux, create a separate build directory for each architecture and + configure with `--without-readline --without-icu --without-zlib`. + + Use `CFLAGS="-fPIC -m32" LDFLAGS="-m32"` for `lib_linux`, or + `CFLAGS="-fPIC -m64" LDFLAGS="-m64"` for `lib_linux64`. + +4. Build the Linux static libraries: + +```sh +make -C src/backend generated-headers +make -C src/port libpgport_shlib.a +make -C src/common libpgcommon_shlib.a +make -C src/interfaces/libpq libpq.a SHLIB_PREREQS= +``` + +5. For Windows, configure from an MSVC developer shell: + +```bat +python -m mesonbuild.mesonmain setup build-win64 . --backend=ninja -Dreadline=disabled -Dicu=disabled -Dlibcurl=disabled -Dzlib=disabled -Dssl=none -Db_vscrt=mt +``` + +6. Build the Windows static libraries: + +```bat +python -m ninja -C build-win64 src/interfaces/libpq/libpq.a src/common/libpgcommon_shlib.a src/port/libpgport_shlib.a +``` + + Use `build-win32` from an x86 MSVC shell for `lib_win`. + +7. Copy the three static libraries plus `libpq-fe.h` and `postgres_ext.h` into + the matching `lib_*` directory. Windows packages rename the `.a` outputs to + `.lib`. + +## Bundled Files + +Linux: + +- `lib_linux/libpq.a` +- `lib_linux/libpgcommon_shlib.a` +- `lib_linux/libpgport_shlib.a` +- `lib_linux64/libpq.a` +- `lib_linux64/libpgcommon_shlib.a` +- `lib_linux64/libpgport_shlib.a` + +Windows: + +- `lib_win/libpq.lib` +- `lib_win/libpgcommon_shlib.lib` +- `lib_win/libpgport_shlib.lib` +- `lib_win64/libpq.lib` +- `lib_win64/libpgcommon_shlib.lib` +- `lib_win64/libpgport_shlib.lib` + +Headers: + +- `libpq-fe.h` +- `postgres_ext.h` + +macOS pgsql builds are not currently supported. diff --git a/extensions/pgsql/lib_darwin/libpq.a b/extensions/pgsql/lib_darwin/libpq.a deleted file mode 100644 index 31e21e88c2..0000000000 Binary files a/extensions/pgsql/lib_darwin/libpq.a and /dev/null differ diff --git a/extensions/pgsql/lib_darwin/pg_config_ext.h b/extensions/pgsql/lib_darwin/pg_config_ext.h deleted file mode 100644 index f8238576b7..0000000000 --- a/extensions/pgsql/lib_darwin/pg_config_ext.h +++ /dev/null @@ -1,8 +0,0 @@ -/* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ -/* - * src/include/pg_config_ext.h.in. This is generated manually, not by - * autoheader, since we want to limit which symbols get defined here. - */ - -/* Define to the name of a signed 64-bit integer type. */ -#define PG_INT64_TYPE long long int diff --git a/extensions/pgsql/lib_darwin64/libpq.a b/extensions/pgsql/lib_darwin64/libpq.a deleted file mode 100644 index f85ce17def..0000000000 Binary files a/extensions/pgsql/lib_darwin64/libpq.a and /dev/null differ diff --git a/extensions/pgsql/lib_darwin64/pg_config_ext.h b/extensions/pgsql/lib_darwin64/pg_config_ext.h deleted file mode 100644 index b4c07dd857..0000000000 --- a/extensions/pgsql/lib_darwin64/pg_config_ext.h +++ /dev/null @@ -1,8 +0,0 @@ -/* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ -/* - * src/include/pg_config_ext.h.in. This is generated manually, not by - * autoheader, since we want to limit which symbols get defined here. - */ - -/* Define to the name of a signed 64-bit integer type. */ -#define PG_INT64_TYPE long int diff --git a/extensions/pgsql/lib_linux/libpgcommon_shlib.a b/extensions/pgsql/lib_linux/libpgcommon_shlib.a new file mode 100755 index 0000000000..8bf97ed696 Binary files /dev/null and b/extensions/pgsql/lib_linux/libpgcommon_shlib.a differ diff --git a/extensions/pgsql/lib_linux/libpgport_shlib.a b/extensions/pgsql/lib_linux/libpgport_shlib.a new file mode 100755 index 0000000000..01c95a557c Binary files /dev/null and b/extensions/pgsql/lib_linux/libpgport_shlib.a differ diff --git a/extensions/pgsql/lib_linux/libpq.a b/extensions/pgsql/lib_linux/libpq.a index e1d9b04a78..73a3199e0b 100644 Binary files a/extensions/pgsql/lib_linux/libpq.a and b/extensions/pgsql/lib_linux/libpq.a differ diff --git a/extensions/pgsql/lib_linux/pg_config_ext.h b/extensions/pgsql/lib_linux/pg_config_ext.h deleted file mode 100644 index f8238576b7..0000000000 --- a/extensions/pgsql/lib_linux/pg_config_ext.h +++ /dev/null @@ -1,8 +0,0 @@ -/* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ -/* - * src/include/pg_config_ext.h.in. This is generated manually, not by - * autoheader, since we want to limit which symbols get defined here. - */ - -/* Define to the name of a signed 64-bit integer type. */ -#define PG_INT64_TYPE long long int diff --git a/extensions/pgsql/lib_linux64/libpgcommon_shlib.a b/extensions/pgsql/lib_linux64/libpgcommon_shlib.a new file mode 100755 index 0000000000..7ec043c77d Binary files /dev/null and b/extensions/pgsql/lib_linux64/libpgcommon_shlib.a differ diff --git a/extensions/pgsql/lib_linux64/libpgport_shlib.a b/extensions/pgsql/lib_linux64/libpgport_shlib.a new file mode 100755 index 0000000000..d9586a1b5d Binary files /dev/null and b/extensions/pgsql/lib_linux64/libpgport_shlib.a differ diff --git a/extensions/pgsql/lib_linux64/libpq.a b/extensions/pgsql/lib_linux64/libpq.a index c60bc577f6..8eddf5820b 100644 Binary files a/extensions/pgsql/lib_linux64/libpq.a and b/extensions/pgsql/lib_linux64/libpq.a differ diff --git a/extensions/pgsql/lib_linux64/pg_config_ext.h b/extensions/pgsql/lib_linux64/pg_config_ext.h deleted file mode 100644 index b4c07dd857..0000000000 --- a/extensions/pgsql/lib_linux64/pg_config_ext.h +++ /dev/null @@ -1,8 +0,0 @@ -/* src/include/pg_config_ext.h. Generated from pg_config_ext.h.in by configure. */ -/* - * src/include/pg_config_ext.h.in. This is generated manually, not by - * autoheader, since we want to limit which symbols get defined here. - */ - -/* Define to the name of a signed 64-bit integer type. */ -#define PG_INT64_TYPE long int diff --git a/extensions/pgsql/lib_win/libpgcommon_shlib.lib b/extensions/pgsql/lib_win/libpgcommon_shlib.lib new file mode 100755 index 0000000000..701058bc2b Binary files /dev/null and b/extensions/pgsql/lib_win/libpgcommon_shlib.lib differ diff --git a/extensions/pgsql/lib_win/libpgport_shlib.lib b/extensions/pgsql/lib_win/libpgport_shlib.lib new file mode 100755 index 0000000000..76b77c2915 Binary files /dev/null and b/extensions/pgsql/lib_win/libpgport_shlib.lib differ diff --git a/extensions/pgsql/lib_win/libpq.lib b/extensions/pgsql/lib_win/libpq.lib index 171ee65e60..aa8477950a 100644 Binary files a/extensions/pgsql/lib_win/libpq.lib and b/extensions/pgsql/lib_win/libpq.lib differ diff --git a/extensions/pgsql/lib_win/pg_config_ext.h b/extensions/pgsql/lib_win/pg_config_ext.h deleted file mode 100644 index 65bbb5d80d..0000000000 --- a/extensions/pgsql/lib_win/pg_config_ext.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * src/include/pg_config_ext.h.win32. This is generated manually, not by - * autoheader, since we want to limit which symbols get defined here. - */ - -/* Define to the name of a signed 64-bit integer type. */ -#define PG_INT64_TYPE long long int diff --git a/extensions/pgsql/lib_win64/libpgcommon_shlib.lib b/extensions/pgsql/lib_win64/libpgcommon_shlib.lib new file mode 100755 index 0000000000..3a1110f514 Binary files /dev/null and b/extensions/pgsql/lib_win64/libpgcommon_shlib.lib differ diff --git a/extensions/pgsql/lib_win64/libpgport_shlib.lib b/extensions/pgsql/lib_win64/libpgport_shlib.lib new file mode 100755 index 0000000000..6df83071e1 Binary files /dev/null and b/extensions/pgsql/lib_win64/libpgport_shlib.lib differ diff --git a/extensions/pgsql/lib_win64/libpq.lib b/extensions/pgsql/lib_win64/libpq.lib index d95a216249..806989ebc8 100644 Binary files a/extensions/pgsql/lib_win64/libpq.lib and b/extensions/pgsql/lib_win64/libpq.lib differ diff --git a/extensions/pgsql/lib_win64/pg_config_ext.h b/extensions/pgsql/lib_win64/pg_config_ext.h deleted file mode 100644 index 65bbb5d80d..0000000000 --- a/extensions/pgsql/lib_win64/pg_config_ext.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * src/include/pg_config_ext.h.win32. This is generated manually, not by - * autoheader, since we want to limit which symbols get defined here. - */ - -/* Define to the name of a signed 64-bit integer type. */ -#define PG_INT64_TYPE long long int diff --git a/extensions/pgsql/libpq-fe.h b/extensions/pgsql/libpq-fe.h index 9ca0756c4b..877a6483b3 100644 --- a/extensions/pgsql/libpq-fe.h +++ b/extensions/pgsql/libpq-fe.h @@ -4,7 +4,7 @@ * This file contains definitions for structures and * externs for functions used by frontend postgres applications. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/interfaces/libpq/libpq-fe.h @@ -16,10 +16,11 @@ #define LIBPQ_FE_H #ifdef __cplusplus -extern "C" +extern "C" { #endif +#include #include /* @@ -28,6 +29,40 @@ extern "C" */ #include "postgres_ext.h" +/* + * These symbols may be used in compile-time #ifdef tests for the availability + * of v14-and-newer libpq features. + */ +/* Features added in PostgreSQL v14: */ +/* Indicates presence of PQenterPipelineMode and friends */ +#define LIBPQ_HAS_PIPELINING 1 +/* Indicates presence of PQsetTraceFlags; also new PQtrace output format */ +#define LIBPQ_HAS_TRACE_FLAGS 1 + +/* Features added in PostgreSQL v15: */ +/* Indicates that PQsslAttribute(NULL, "library") is useful */ +#define LIBPQ_HAS_SSL_LIBRARY_DETECTION 1 + +/* Features added in PostgreSQL v17: */ +/* Indicates presence of PGcancelConn typedef and associated routines */ +#define LIBPQ_HAS_ASYNC_CANCEL 1 +/* Indicates presence of PQchangePassword */ +#define LIBPQ_HAS_CHANGE_PASSWORD 1 +/* Indicates presence of PQsetChunkedRowsMode, PGRES_TUPLES_CHUNK */ +#define LIBPQ_HAS_CHUNK_MODE 1 +/* Indicates presence of PQclosePrepared, PQclosePortal, etc */ +#define LIBPQ_HAS_CLOSE_PREPARED 1 +/* Indicates presence of PQsendPipelineSync */ +#define LIBPQ_HAS_SEND_PIPELINE_SYNC 1 +/* Indicates presence of PQsocketPoll, PQgetCurrentTimeUSec */ +#define LIBPQ_HAS_SOCKET_POLL 1 + +/* Features added in PostgreSQL v18: */ +/* Indicates presence of PQfullProtocolVersion */ +#define LIBPQ_HAS_FULL_PROTOCOL_VERSION 1 +/* Indicates presence of the PQAUTHDATA_PROMPT_OAUTH_DEVICE authdata hook */ +#define LIBPQ_HAS_PROMPT_OAUTH_DEVICE 1 + /* * Option flags for PQcopyResult */ @@ -56,13 +91,23 @@ typedef enum */ CONNECTION_STARTED, /* Waiting for connection to be made. */ CONNECTION_MADE, /* Connection OK; waiting to send. */ - CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the - * postmaster. */ + CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the + * postmaster. */ CONNECTION_AUTH_OK, /* Received authentication; waiting for * backend startup. */ - CONNECTION_SETENV, /* Negotiating environment. */ - CONNECTION_SSL_STARTUP, /* Negotiating SSL. */ - CONNECTION_NEEDED /* Internal state: connect() needed */ + CONNECTION_SETENV, /* This state is no longer used. */ + CONNECTION_SSL_STARTUP, /* Performing SSL handshake. */ + CONNECTION_NEEDED, /* Internal state: connect() needed. */ + CONNECTION_CHECK_WRITABLE, /* Checking if session is read-write. */ + CONNECTION_CONSUME, /* Consuming any extra messages. */ + CONNECTION_GSS_STARTUP, /* Negotiating GSSAPI. */ + CONNECTION_CHECK_TARGET, /* Internal state: checking target server + * properties. */ + CONNECTION_CHECK_STANDBY, /* Checking if server is in standby mode. */ + CONNECTION_ALLOCATED, /* Waiting for connection attempt to be + * started. */ + CONNECTION_AUTHENTICATING, /* Authentication is in progress with some + * external system. */ } ConnStatusType; typedef enum @@ -71,8 +116,7 @@ typedef enum PGRES_POLLING_READING, /* These two indicate that one may */ PGRES_POLLING_WRITING, /* use select before polling again. */ PGRES_POLLING_OK, - PGRES_POLLING_ACTIVE /* unused; keep for awhile for backwards - * compatibility */ + PGRES_POLLING_ACTIVE /* unused; keep for backwards compatibility */ } PostgresPollingStatusType; typedef enum @@ -91,7 +135,11 @@ typedef enum PGRES_NONFATAL_ERROR, /* notice or warning message */ PGRES_FATAL_ERROR, /* query failed */ PGRES_COPY_BOTH, /* Copy In/Out data transfer in progress */ - PGRES_SINGLE_TUPLE /* single tuple from larger resultset */ + PGRES_SINGLE_TUPLE, /* single tuple from larger resultset */ + PGRES_PIPELINE_SYNC, /* pipeline synchronization point */ + PGRES_PIPELINE_ABORTED, /* Command didn't run because of an abort + * earlier in a pipeline */ + PGRES_TUPLES_CHUNK /* chunk of tuples from larger resultset */ } ExecStatusType; typedef enum @@ -107,7 +155,8 @@ typedef enum { PQERRORS_TERSE, /* single-line error messages */ PQERRORS_DEFAULT, /* recommended style */ - PQERRORS_VERBOSE /* all the facts, ma'am */ + PQERRORS_VERBOSE, /* all the facts, ma'am */ + PQERRORS_SQLSTATE /* only error severity and SQLSTATE code */ } PGVerbosity; typedef enum @@ -130,11 +179,33 @@ typedef enum PQPING_NO_ATTEMPT /* connection not attempted (bad params) */ } PGPing; +/* + * PGpipelineStatus - Current status of pipeline mode + */ +typedef enum +{ + PQ_PIPELINE_OFF, + PQ_PIPELINE_ON, + PQ_PIPELINE_ABORTED +} PGpipelineStatus; + +typedef enum +{ + PQAUTHDATA_PROMPT_OAUTH_DEVICE, /* user must visit a device-authorization + * URL */ + PQAUTHDATA_OAUTH_BEARER_TOKEN, /* server requests an OAuth Bearer token */ +} PGauthData; + /* PGconn encapsulates a connection to the backend. * The contents of this struct are not supposed to be known to applications. */ typedef struct pg_conn PGconn; +/* PGcancelConn encapsulates a cancel connection to the backend. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_cancel_conn PGcancelConn; + /* PGresult encapsulates the result of a query (or more precisely, of a single * SQL command --- a query string given to PQsendQuery can contain multiple * commands and thus return multiple PGresult objects). @@ -163,6 +234,9 @@ typedef struct pgNotify struct pgNotify *next; /* list link */ } PGnotify; +/* pg_usec_time_t is like time_t, but with microsecond resolution */ +typedef int64_t pg_usec_time_t; + /* Function types for notice-handling callbacks */ typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); typedef void (*PQnoticeProcessor) (void *arg, const char *message); @@ -244,23 +318,23 @@ typedef struct pgresAttDesc * ---------------- */ -/* === in fe-connect.c === */ +/* === in fe-connect.c === */ /* make a new client connection to the backend */ /* Asynchronous (non-blocking) */ extern PGconn *PQconnectStart(const char *conninfo); -extern PGconn *PQconnectStartParams(const char *const * keywords, - const char *const * values, int expand_dbname); +extern PGconn *PQconnectStartParams(const char *const *keywords, + const char *const *values, int expand_dbname); extern PostgresPollingStatusType PQconnectPoll(PGconn *conn); /* Synchronous (blocking) */ extern PGconn *PQconnectdb(const char *conninfo); -extern PGconn *PQconnectdbParams(const char *const * keywords, - const char *const * values, int expand_dbname); +extern PGconn *PQconnectdbParams(const char *const *keywords, + const char *const *values, int expand_dbname); extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport, - const char *pgoptions, const char *pgtty, - const char *dbName, - const char *login, const char *pwd); + const char *pgoptions, const char *pgtty, + const char *dbName, + const char *login, const char *pwd); #define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) \ PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL) @@ -281,7 +355,7 @@ extern PQconninfoOption *PQconninfo(PGconn *conn); extern void PQconninfoFree(PQconninfoOption *connOptions); /* - * close the current connection and restablish a new one with the same + * close the current connection and reestablish a new one with the same * parameters */ /* Asynchronous (non-blocking) */ @@ -291,16 +365,34 @@ extern PostgresPollingStatusType PQresetPoll(PGconn *conn); /* Synchronous (blocking) */ extern void PQreset(PGconn *conn); +/* Create a PGcancelConn that's used to cancel a query on the given PGconn */ +extern PGcancelConn *PQcancelCreate(PGconn *conn); + +/* issue a cancel request in a non-blocking manner */ +extern int PQcancelStart(PGcancelConn *cancelConn); + +/* issue a blocking cancel request */ +extern int PQcancelBlocking(PGcancelConn *cancelConn); + +/* poll a non-blocking cancel request */ +extern PostgresPollingStatusType PQcancelPoll(PGcancelConn *cancelConn); +extern ConnStatusType PQcancelStatus(const PGcancelConn *cancelConn); +extern int PQcancelSocket(const PGcancelConn *cancelConn); +extern char *PQcancelErrorMessage(const PGcancelConn *cancelConn); +extern void PQcancelReset(PGcancelConn *cancelConn); +extern void PQcancelFinish(PGcancelConn *cancelConn); + + /* request a cancel structure */ extern PGcancel *PQgetCancel(PGconn *conn); /* free a cancel structure */ extern void PQfreeCancel(PGcancel *cancel); -/* issue a cancel request */ +/* deprecated version of PQcancelBlocking, but one which is signal-safe */ extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize); -/* backwards compatible version of PQcancel; not thread-safe */ +/* deprecated version of PQcancel; not thread-safe */ extern int PQrequestCancel(PGconn *conn); /* Accessor functions for PGconn objects */ @@ -308,20 +400,24 @@ extern char *PQdb(const PGconn *conn); extern char *PQuser(const PGconn *conn); extern char *PQpass(const PGconn *conn); extern char *PQhost(const PGconn *conn); +extern char *PQhostaddr(const PGconn *conn); extern char *PQport(const PGconn *conn); extern char *PQtty(const PGconn *conn); extern char *PQoptions(const PGconn *conn); extern ConnStatusType PQstatus(const PGconn *conn); extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn); extern const char *PQparameterStatus(const PGconn *conn, - const char *paramName); + const char *paramName); extern int PQprotocolVersion(const PGconn *conn); +extern int PQfullProtocolVersion(const PGconn *conn); extern int PQserverVersion(const PGconn *conn); extern char *PQerrorMessage(const PGconn *conn); extern int PQsocket(const PGconn *conn); extern int PQbackendPID(const PGconn *conn); +extern PGpipelineStatus PQpipelineStatus(const PGconn *conn); extern int PQconnectionNeedsPassword(const PGconn *conn); extern int PQconnectionUsedPassword(const PGconn *conn); +extern int PQconnectionUsedGSSAPI(const PGconn *conn); extern int PQclientEncoding(const PGconn *conn); extern int PQsetClientEncoding(PGconn *conn, const char *encoding); @@ -329,7 +425,7 @@ extern int PQsetClientEncoding(PGconn *conn, const char *encoding); extern int PQsslInUse(PGconn *conn); extern void *PQsslStruct(PGconn *conn, const char *struct_name); extern const char *PQsslAttribute(PGconn *conn, const char *attribute_name); -extern const char *const * PQsslAttributeNames(PGconn *conn); +extern const char *const *PQsslAttributeNames(PGconn *conn); /* Get the OpenSSL structure associated with a connection. Returns NULL for * unencrypted connections or if any other TLS library is in use. */ @@ -341,24 +437,26 @@ extern void PQinitSSL(int do_init); /* More detailed way to tell libpq whether it needs to initialize OpenSSL */ extern void PQinitOpenSSL(int do_ssl, int do_crypto); +/* Return true if GSSAPI encryption is in use */ +extern int PQgssEncInUse(PGconn *conn); + +/* Returns GSSAPI context if GSSAPI is in use */ +extern void *PQgetgssctx(PGconn *conn); + /* Set verbosity for PQerrorMessage and PQresultErrorMessage */ extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); /* Set CONTEXT visibility for PQerrorMessage and PQresultErrorMessage */ extern PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, - PGContextVisibility show_context); - -/* Enable/disable tracing */ -extern void PQtrace(PGconn *conn, FILE *debug_port); -extern void PQuntrace(PGconn *conn); + PGContextVisibility show_context); /* Override default notice handling routines */ extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, - PQnoticeReceiver proc, - void *arg); + PQnoticeReceiver proc, + void *arg); extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, - PQnoticeProcessor proc, - void *arg); + PQnoticeProcessor proc, + void *arg); /* * Used to set callback that prevents concurrent access to @@ -371,56 +469,77 @@ typedef void (*pgthreadlock_t) (int acquire); extern pgthreadlock_t PQregisterThreadLock(pgthreadlock_t newhandler); +/* === in fe-trace.c === */ +extern void PQtrace(PGconn *conn, FILE *debug_port); +extern void PQuntrace(PGconn *conn); + +/* flags controlling trace output: */ +/* omit timestamps from each line */ +#define PQTRACE_SUPPRESS_TIMESTAMPS (1<<0) +/* redact portions of some messages, for testing frameworks */ +#define PQTRACE_REGRESS_MODE (1<<1) +extern void PQsetTraceFlags(PGconn *conn, int flags); + /* === in fe-exec.c === */ /* Simple synchronous query */ extern PGresult *PQexec(PGconn *conn, const char *query); extern PGresult *PQexecParams(PGconn *conn, - const char *command, - int nParams, - const Oid *paramTypes, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat); + const char *command, + int nParams, + const Oid *paramTypes, + const char *const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); extern PGresult *PQprepare(PGconn *conn, const char *stmtName, - const char *query, int nParams, - const Oid *paramTypes); + const char *query, int nParams, + const Oid *paramTypes); extern PGresult *PQexecPrepared(PGconn *conn, - const char *stmtName, - int nParams, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat); + const char *stmtName, + int nParams, + const char *const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); /* Interface for multiple-result or asynchronous queries */ +#define PQ_QUERY_PARAM_MAX_LIMIT 65535 + extern int PQsendQuery(PGconn *conn, const char *query); -extern int PQsendQueryParams(PGconn *conn, - const char *command, - int nParams, - const Oid *paramTypes, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat); -extern int PQsendPrepare(PGconn *conn, const char *stmtName, - const char *query, int nParams, - const Oid *paramTypes); -extern int PQsendQueryPrepared(PGconn *conn, - const char *stmtName, - int nParams, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat); +extern int PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsendPrepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern int PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); extern int PQsetSingleRowMode(PGconn *conn); +extern int PQsetChunkedRowsMode(PGconn *conn, int chunkSize); extern PGresult *PQgetResult(PGconn *conn); /* Routines for managing an asynchronous query */ extern int PQisBusy(PGconn *conn); extern int PQconsumeInput(PGconn *conn); +/* Routines for pipeline mode management */ +extern int PQenterPipelineMode(PGconn *conn); +extern int PQexitPipelineMode(PGconn *conn); +extern int PQpipelineSync(PGconn *conn); +extern int PQsendFlushRequest(PGconn *conn); +extern int PQsendPipelineSync(PGconn *conn); + /* LISTEN/NOTIFY support */ extern PGnotify *PQnotifies(PGconn *conn); @@ -430,7 +549,7 @@ extern int PQputCopyEnd(PGconn *conn, const char *errormsg); extern int PQgetCopyData(PGconn *conn, char **buffer, int async); /* Deprecated routines for copy in/out */ -extern int PQgetline(PGconn *conn, char *string, int length); +extern int PQgetline(PGconn *conn, char *buffer, int length); extern int PQputline(PGconn *conn, const char *string); extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize); extern int PQputnbytes(PGconn *conn, const char *buffer, int nbytes); @@ -441,8 +560,8 @@ extern int PQsetnonblocking(PGconn *conn, int arg); extern int PQisnonblocking(const PGconn *conn); extern int PQisthreadsafe(void); extern PGPing PQping(const char *conninfo); -extern PGPing PQpingParams(const char *const * keywords, - const char *const * values, int expand_dbname); +extern PGPing PQpingParams(const char *const *keywords, + const char *const *values, int expand_dbname); /* Force the write buffer to be written (or at least try) */ extern int PQflush(PGconn *conn); @@ -452,20 +571,20 @@ extern int PQflush(PGconn *conn); * use */ extern PGresult *PQfn(PGconn *conn, - int fnid, - int *result_buf, - int *result_len, - int result_is_int, - const PQArgBlock *args, - int nargs); + int fnid, + int *result_buf, + int *result_len, + int result_is_int, + const PQArgBlock *args, + int nargs); /* Accessor functions for PGresult objects */ extern ExecStatusType PQresultStatus(const PGresult *res); extern char *PQresStatus(ExecStatusType status); extern char *PQresultErrorMessage(const PGresult *res); extern char *PQresultVerboseErrorMessage(const PGresult *res, - PGVerbosity verbosity, - PGContextVisibility show_context); + PGVerbosity verbosity, + PGContextVisibility show_context); extern char *PQresultErrorField(const PGresult *res, int fieldcode); extern int PQntuples(const PGresult *res); extern int PQnfields(const PGresult *res); @@ -494,6 +613,12 @@ extern PGresult *PQdescribePortal(PGconn *conn, const char *portal); extern int PQsendDescribePrepared(PGconn *conn, const char *stmt); extern int PQsendDescribePortal(PGconn *conn, const char *portal); +/* Close prepared statements and portals */ +extern PGresult *PQclosePrepared(PGconn *conn, const char *stmt); +extern PGresult *PQclosePortal(PGconn *conn, const char *portal); +extern int PQsendClosePrepared(PGconn *conn, const char *stmt); +extern int PQsendClosePortal(PGconn *conn, const char *portal); + /* Delete a PGresult */ extern void PQclear(PGresult *res); @@ -512,48 +637,50 @@ extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); extern PGresult *PQcopyResult(const PGresult *src, int flags); extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs); extern void *PQresultAlloc(PGresult *res, size_t nBytes); +extern size_t PQresultMemorySize(const PGresult *res); extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len); /* Quoting strings before inclusion in queries. */ extern size_t PQescapeStringConn(PGconn *conn, - char *to, const char *from, size_t length, - int *error); + char *to, const char *from, size_t length, + int *error); extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len); extern char *PQescapeIdentifier(PGconn *conn, const char *str, size_t len); extern unsigned char *PQescapeByteaConn(PGconn *conn, - const unsigned char *from, size_t from_length, - size_t *to_length); + const unsigned char *from, size_t from_length, + size_t *to_length); extern unsigned char *PQunescapeBytea(const unsigned char *strtext, - size_t *retbuflen); + size_t *retbuflen); /* These forms are deprecated! */ extern size_t PQescapeString(char *to, const char *from, size_t length); extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, - size_t *to_length); + size_t *to_length); /* === in fe-print.c === */ -extern void PQprint(FILE *fout, /* output stream */ - const PGresult *res, - const PQprintOpt *ps); /* option structure */ +extern void PQprint(FILE *fout, /* output stream */ + const PGresult *res, + const PQprintOpt *po); /* option structure */ /* * really old printing routines */ extern void PQdisplayTuples(const PGresult *res, - FILE *fp, /* where to send the output */ - int fillAlign, /* pad the fields with spaces */ - const char *fieldSep, /* field separator */ - int printHeader, /* display headers? */ - int quiet); + FILE *fp, /* where to send the output */ + int fillAlign, /* pad the fields with spaces */ + const char *fieldSep, /* field separator */ + int printHeader, /* display headers? */ + int quiet); extern void PQprintTuples(const PGresult *res, - FILE *fout, /* output stream */ - int printAttName, /* print attribute names */ - int terseOutput, /* delimiter bars */ - int width); /* width of column, if 0, use variable width */ + FILE *fout, /* output stream */ + int PrintAttNames, /* print attribute names */ + int TerseOutput, /* delimiter bars */ + int colWidth); /* width of column, if 0, use + * variable width */ /* === in fe-lobj.c === */ @@ -564,13 +691,13 @@ extern int lo_close(PGconn *conn, int fd); extern int lo_read(PGconn *conn, int fd, char *buf, size_t len); extern int lo_write(PGconn *conn, int fd, const char *buf, size_t len); extern int lo_lseek(PGconn *conn, int fd, int offset, int whence); -extern pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence); +extern int64_t lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence); extern Oid lo_creat(PGconn *conn, int mode); extern Oid lo_create(PGconn *conn, Oid lobjId); extern int lo_tell(PGconn *conn, int fd); -extern pg_int64 lo_tell64(PGconn *conn, int fd); +extern int64_t lo_tell64(PGconn *conn, int fd); extern int lo_truncate(PGconn *conn, int fd, size_t len); -extern int lo_truncate64(PGconn *conn, int fd, pg_int64 len); +extern int lo_truncate64(PGconn *conn, int fd, int64_t len); extern int lo_unlink(PGconn *conn, Oid lobjId); extern Oid lo_import(PGconn *conn, const char *filename); extern Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId); @@ -581,9 +708,19 @@ extern int lo_export(PGconn *conn, Oid lobjId, const char *filename); /* Get the version of the libpq library in use */ extern int PQlibVersion(void); +/* Poll a socket for reading and/or writing with an optional timeout */ +extern int PQsocketPoll(int sock, int forRead, int forWrite, + pg_usec_time_t end_time); + +/* Get current time in the form PQsocketPoll wants */ +extern pg_usec_time_t PQgetCurrentTimeUSec(void); + /* Determine length of multibyte encoded char at *s */ extern int PQmblen(const char *s, int encoding); +/* Same, but not more than the distance to the end of string s */ +extern int PQmblenBounded(const char *s, int encoding); + /* Determine display length of multibyte encoded char at *s */ extern int PQdsplen(const char *s, int encoding); @@ -592,7 +729,92 @@ extern int PQenv2encoding(void); /* === in fe-auth.c === */ +typedef struct _PGpromptOAuthDevice +{ + const char *verification_uri; /* verification URI to visit */ + const char *user_code; /* user code to enter */ + const char *verification_uri_complete; /* optional combination of URI and + * code, or NULL */ + int expires_in; /* seconds until user code expires */ +} PGpromptOAuthDevice; + +/* + * For PGoauthBearerRequest.async(). This macro just allows clients to avoid + * depending on libpq-int.h or Winsock for the "socket" type; it's undefined + * immediately below. + */ +#ifdef _WIN32 +#define PQ_SOCKTYPE uintptr_t /* avoids depending on winsock2.h for SOCKET */ +#else +#define PQ_SOCKTYPE int +#endif + +typedef struct PGoauthBearerRequest +{ + /* Hook inputs (constant across all calls) */ + const char *openid_configuration; /* OIDC discovery URI */ + const char *scope; /* required scope(s), or NULL */ + + /* Hook outputs */ + + /*--------- + * Callback implementing a custom asynchronous OAuth flow. + * + * The callback may return + * - PGRES_POLLING_READING/WRITING, to indicate that a socket descriptor + * has been stored in *altsock and libpq should wait until it is + * readable or writable before calling back; + * - PGRES_POLLING_OK, to indicate that the flow is complete and + * request->token has been set; or + * - PGRES_POLLING_FAILED, to indicate that token retrieval has failed. + * + * This callback is optional. If the token can be obtained without + * blocking during the original call to the PQAUTHDATA_OAUTH_BEARER_TOKEN + * hook, it may be returned directly, but one of request->async or + * request->token must be set by the hook. + * + * The (PQ_SOCKTYPE *) in the signature is a placeholder for the platform's + * native socket type: (SOCKET *) on Windows, and (int *) everywhere else. + */ + PostgresPollingStatusType (*async) (PGconn *conn, + struct PGoauthBearerRequest *request, + PQ_SOCKTYPE * altsock); + + /* + * Callback to clean up custom allocations. A hook implementation may use + * this to free request->token and any resources in request->user. + * + * This is technically optional, but highly recommended, because there is + * no other indication as to when it is safe to free the token. + */ + void (*cleanup) (PGconn *conn, struct PGoauthBearerRequest *request); + + /* + * The hook should set this to the Bearer token contents for the + * connection, once the flow is completed. The token contents must remain + * available to libpq until the hook's cleanup callback is called. + */ + char *token; + + /* + * Hook-defined data. libpq will not modify this pointer across calls to + * the async callback, so it can be used to keep track of + * application-specific state. Resources allocated here should be freed by + * the cleanup callback. + */ + void *user; +} PGoauthBearerRequest; + +#undef PQ_SOCKTYPE + extern char *PQencryptPassword(const char *passwd, const char *user); +extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm); +extern PGresult *PQchangePassword(PGconn *conn, const char *user, const char *passwd); + +typedef int (*PQauthDataHook_type) (PGauthData type, PGconn *conn, void *data); +extern void PQsetAuthDataHook(PQauthDataHook_type hook); +extern PQauthDataHook_type PQgetAuthDataHook(void); +extern int PQdefaultAuthDataHook(PGauthData type, PGconn *conn, void *data); /* === in encnames.c === */ @@ -600,8 +822,16 @@ extern int pg_char_to_encoding(const char *name); extern const char *pg_encoding_to_char(int encoding); extern int pg_valid_server_encoding_id(int encoding); +/* === in fe-secure-openssl.c === */ + +/* Support for overriding sslpassword handling with a callback */ +typedef int (*PQsslKeyPassHook_OpenSSL_type) (char *buf, int size, PGconn *conn); +extern PQsslKeyPassHook_OpenSSL_type PQgetSSLKeyPassHook_OpenSSL(void); +extern void PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook); +extern int PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn); + #ifdef __cplusplus } #endif -#endif /* LIBPQ_FE_H */ +#endif /* LIBPQ_FE_H */ diff --git a/extensions/pgsql/pgsql/PgBasicResults.cpp b/extensions/pgsql/pgsql/PgBasicResults.cpp index 91276cbc92..c9fb5a5b94 100644 --- a/extensions/pgsql/pgsql/PgBasicResults.cpp +++ b/extensions/pgsql/pgsql/PgBasicResults.cpp @@ -39,6 +39,11 @@ PgBasicResults::PgBasicResults(PGresult *res) PgBasicResults::~PgBasicResults() { + if (m_pRes != nullptr) + { + PQclear(m_pRes); + m_pRes = nullptr; + } } void PgBasicResults::Update() @@ -340,11 +345,6 @@ bool PgQuery::FetchMoreResults() void PgQuery::Destroy() { - if (m_rs.m_pRes != nullptr) - { - PQclear(m_rs.m_pRes); - } - /* Self destruct */ delete this; } diff --git a/extensions/pgsql/pgsql/PgDatabase.cpp b/extensions/pgsql/pgsql/PgDatabase.cpp index 3a9b7fa6bd..68fbaef7de 100644 --- a/extensions/pgsql/pgsql/PgDatabase.cpp +++ b/extensions/pgsql/pgsql/PgDatabase.cpp @@ -33,6 +33,7 @@ #include "smsdk_ext.h" #include "PgBasicResults.h" #include "PgStatement.h" +#include // Some selected defines from postgresql 9.2.4's src/include/catalog/pg_type.h // Fast scan to extract the types that shouldn't be read as string. @@ -246,6 +247,10 @@ bool PgDatabase::DoSimpleQuery(const char *query) IQuery *PgDatabase::DoQuery(const char *query) { PGresult *res = PQexec(m_pgsql, query); + if (res == NULL) + { + return NULL; + } ExecStatusType status = PQresultStatus(res); @@ -271,9 +276,17 @@ bool PgDatabase::DoSimpleQueryEx(const char *query, size_t len) IQuery *PgDatabase::DoQueryEx(const char *query, size_t len) { - // There is no way to send binary data like that in queries. - // You'd need to escape the value with PQescapeByteaConn first and use it in the query string as usual. - return DoQuery(query); + // PQexec only accepts a null-terminated SQL string. It can honor an + // explicit shorter length, but it cannot send embedded null bytes. + size_t queryLen = strlen(query); + if (queryLen < len) + return NULL; + + if (len == queryLen) + return DoQuery(query); + + std::string limitedQuery(query, len); + return DoQuery(limitedQuery.c_str()); } unsigned int PgDatabase::GetAffectedRowsForQuery(IQuery *query) @@ -288,14 +301,30 @@ unsigned int PgDatabase::GetInsertIDForQuery(IQuery *query) IPreparedQuery *PgDatabase::PrepareQuery(const char *query, char *error, size_t maxlength, int *errCode) { - char stmtName[10]; - // Maybe needs some locking around m_preparedStatementID++? + std::lock_guard lock(m_FullLock); + unsigned int stmtID = m_preparedStatementID; - snprintf(stmtName, 10, "%d", stmtID); + std::string stmtName = std::to_string(stmtID); m_preparedStatementID++; // Let postgresql guess the types of the arguments if there are any.. - PGresult *res = PQprepare(m_pgsql, stmtName, query, 0, NULL); + PGresult *res = PQprepare(m_pgsql, stmtName.c_str(), query, 0, NULL); + if (res == NULL) + { + if (error) + { + strncopy(error, PQerrorMessage(m_pgsql), maxlength); + } + + if (errCode) + { + // PostgreSQL only supports SQLSTATE error codes. + // https://www.postgresql.org/docs/9.6/errcodes-appendix.html + *errCode = -1; + } + + return NULL; + } if (PQresultStatus(res) != PGRES_COMMAND_OK) { @@ -317,7 +346,35 @@ IPreparedQuery *PgDatabase::PrepareQuery(const char *query, char *error, size_t // Only the statement name is of importance. free the PGresult here. PQclear(res); - return new PgStatement(this, stmtName); + + PGresult *desc = PQdescribePrepared(m_pgsql, stmtName.c_str()); + if (desc == NULL || PQresultStatus(desc) != PGRES_COMMAND_OK) + { + if (error) + { + const char *message = desc != NULL ? PQresultErrorMessage(desc) : PQerrorMessage(m_pgsql); + strncopy(error, message, maxlength); + } + + if (errCode) + { + // PostgreSQL only supports SQLSTATE error codes. + // https://www.postgresql.org/docs/9.6/errcodes-appendix.html + *errCode = -1; + } + + if (desc != NULL) + PQclear(desc); + PGresult *close = PQclosePrepared(m_pgsql, stmtName.c_str()); + if (close != NULL) + PQclear(close); + return NULL; + } + + unsigned int params = (unsigned int)PQnparams(desc); + PQclear(desc); + + return new PgStatement(this, stmtName.c_str(), params); } bool PgDatabase::LockForFullAtomicOperation() diff --git a/extensions/pgsql/pgsql/PgDriver.cpp b/extensions/pgsql/pgsql/PgDriver.cpp index cda7aceb14..14ccc11125 100644 --- a/extensions/pgsql/pgsql/PgDriver.cpp +++ b/extensions/pgsql/pgsql/PgDriver.cpp @@ -93,34 +93,54 @@ const char *PgDriver::GetProductName() PGconn *Connect(const DatabaseInfo *info, char *error, size_t maxlength) { - /* https://www.postgresql.org/docs/9.6/libpq-connect.html#LIBPQ-CONNSTRING */ - /* TODO: Switch to PQconnectdbParams to prevent escaping issues. */ - char *options = new char[1024]; - int offs = snprintf(options, 1024, "host='%s' dbname='%s'", info->host, info->database); + const char *keywords[7]; + const char *values[7]; + unsigned int count = 0; + + char port[16]; + char timeout[16]; + + keywords[count] = "host"; + values[count++] = info->host; + + keywords[count] = "dbname"; + values[count++] = info->database; if (info->port > 0) { - offs += snprintf(&options[offs], 1024 - offs, " port=%d", info->port); + snprintf(port, sizeof(port), "%u", info->port); + keywords[count] = "port"; + values[count++] = port; } if (info->user[0] != '\0') { - offs += snprintf(&options[offs], 1024 - offs, " user='%s'", info->user); + keywords[count] = "user"; + values[count++] = info->user; } if (info->pass[0] != '\0') { - offs += snprintf(&options[offs], 1024 - offs, " password='%s'", info->pass); + keywords[count] = "password"; + values[count++] = info->pass; } if (info->maxTimeout > 0) { - offs += snprintf(&options[offs], 1024 - offs, " connect_timeout=%d", info->maxTimeout); + snprintf(timeout, sizeof(timeout), "%u", info->maxTimeout); + keywords[count] = "connect_timeout"; + values[count++] = timeout; } - /* Make a connection to the database */ - PGconn *conn = PQconnectdb(options); + keywords[count] = NULL; + values[count] = NULL; - delete [] options; + /* Make a connection to the database */ + PGconn *conn = PQconnectdbParams(keywords, values, 0); + if (!conn) + { + snprintf(error, maxlength, "Failed to allocate memory for connection"); + return NULL; + } /* Check to see that the backend connection was successfully made */ if (PQstatus(conn) != CONNECTION_OK) diff --git a/extensions/pgsql/pgsql/PgStatement.cpp b/extensions/pgsql/pgsql/PgStatement.cpp index 0c920207d5..1e472936b8 100644 --- a/extensions/pgsql/pgsql/PgStatement.cpp +++ b/extensions/pgsql/pgsql/PgStatement.cpp @@ -29,25 +29,15 @@ * Version: $Id$ */ +#include +#include #include "PgStatement.h" -PgStatement::PgStatement(PgDatabase *db, const char* stmtName) - : m_pgsql(db->m_pgsql), m_pParent(db), m_insertID(0), m_affectedRows(0), m_rs(NULL), m_Results(false) +PgStatement::PgStatement(PgDatabase *db, const char* stmtName, unsigned int params) + : m_pgsql(db->m_pgsql), m_pParent(db), m_stmtName(stmtName), + m_pushinfo(NULL), m_Params(params), m_insertID(0), m_affectedRows(0), + m_rs(NULL), m_Results(false) { - m_stmtName = new char[10]; - strncopy(m_stmtName, stmtName, 10); - - PGresult *desc = PQdescribePrepared(m_pgsql, m_stmtName); - - // TODO: Proper error handling? - if (PQresultStatus(desc) != PGRES_COMMAND_OK) - { - PQclear(desc); - return; - } - - m_Params = (unsigned int)PQnparams(desc); - if (m_Params) { m_pushinfo = (ParamBind *)malloc(sizeof(ParamBind) * m_Params); @@ -64,15 +54,13 @@ PgStatement::~PgStatement() /* Free result set structures */ if (m_Results) { - if (m_rs->m_pRes != NULL) - PQclear(m_rs->m_pRes); delete m_rs; } /* Free old blobs */ for (unsigned int i=0; i= m_Params) @@ -157,6 +158,7 @@ bool PgStatement::BindParamString(unsigned int param, const char *text, bool cop final_ptr = CopyBlob(param, text, len+1); } else { len = strlen(text); + ReleaseBlob(param); final_ptr = text; } @@ -180,6 +182,7 @@ bool PgStatement::BindParamBlob(unsigned int param, const void *data, size_t len { final_ptr = CopyBlob(param, data, length); } else { + ReleaseBlob(param); final_ptr = data; } @@ -217,6 +220,7 @@ bool PgStatement::Execute() const char **paramValues = new const char*[m_Params]; int *paramLengths = new int[m_Params]; int *paramFormats = new int[m_Params]; + std::vector paramText(m_Params); for (unsigned int i=0; i + #include "PgDatabase.h" #include "PgBasicResults.h" @@ -45,12 +47,13 @@ struct ParamBind void *blob; size_t length; DBType type; + bool owns_blob; }; class PgStatement : public IPreparedQuery { public: - PgStatement(PgDatabase *db, const char* stmtName); + PgStatement(PgDatabase *db, const char* stmtName, unsigned int params); ~PgStatement(); public: //IQuery IResultSet *GetResultSet(); @@ -68,10 +71,11 @@ class PgStatement : public IPreparedQuery unsigned int GetInsertID(); private: void *CopyBlob(unsigned int param, const void *blobptr, size_t length); + void ReleaseBlob(unsigned int param); private: PGconn *m_pgsql; ke::RefPtr m_pParent; - char *m_stmtName; + std::string m_stmtName; ParamBind *m_pushinfo; unsigned int m_Params; diff --git a/extensions/pgsql/postgres_ext.h b/extensions/pgsql/postgres_ext.h index ae2f087798..6a8f25a990 100644 --- a/extensions/pgsql/postgres_ext.h +++ b/extensions/pgsql/postgres_ext.h @@ -19,11 +19,12 @@ * *------------------------------------------------------------------------- */ +/* IWYU pragma: always_keep */ #ifndef POSTGRES_EXT_H #define POSTGRES_EXT_H -#include "pg_config_ext.h" +#include /* * Object ID is a fundamental type in Postgres. @@ -39,10 +40,13 @@ typedef unsigned int Oid; #define OID_MAX UINT_MAX /* you will need to include to use the above #define */ -/* Define a signed 64-bit integer type for use in client API declarations. */ -typedef PG_INT64_TYPE pg_int64; +#define atooid(x) ((Oid) strtoul((x), NULL, 10)) +/* the above needs */ +/* deprecated name for int64_t, formerly used in client API declarations */ +typedef int64_t pg_int64; + /* * Identifiers of error message fields. Kept here to keep common * between frontend and backend, and also to export them to libpq @@ -67,4 +71,4 @@ typedef PG_INT64_TYPE pg_int64; #define PG_DIAG_SOURCE_LINE 'L' #define PG_DIAG_SOURCE_FUNCTION 'R' -#endif /* POSTGRES_EXT_H */ +#endif /* POSTGRES_EXT_H */