diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 88217d4506..65201ccf55 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,5 +1,5 @@ @@ -24,7 +24,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.124 2003/06/13 23:10:07 momj - Three short programs are included at the end of this chapter () to show how + Some short programs are included at the end of this chapter () to show how to write programs that use libpq. There are also several complete examples of libpq applications in the directory src/test/examples in the source code distribution. @@ -37,7 +37,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.124 2003/06/13 23:10:07 momj - Database Connection Functions + Database Connection Control Functions The following functions deal with making a connection to a @@ -68,8 +68,8 @@ PGconn *PQconnectdb(const char *conninfo); This function opens a new database connection using the parameters taken from the string conninfo. Unlike PQsetdbLogin below, the parameter set can be extended without changing the function signature, - so use either of this function or the nonblocking analogues PQconnectStart - and PQconnectPoll is preferred for new application programming. + so use of this function (or its nonblocking analogues PQconnectStart + and PQconnectPoll) is preferred for new application programming. @@ -86,7 +86,7 @@ PGconn *PQconnectdb(const char *conninfo); - The currently recognized parameter key words are: + The currently recognized parameter keywords are: @@ -107,11 +107,11 @@ PGconn *PQconnectdb(const char *conninfo); hostaddr - IP address of host to connect to. This should be in the + Numeric IP address of host to connect to. This should be in the standard IPv4 address format, e.g., 172.28.40.9. If - your machine supports IPv6, you can also use those addresses. If - a nonzero-length string is specified, TCP/IP communication is - used. + your machine supports IPv6, you can also use those addresses. + TCP/IP communication is + always used when a nonempty string is specified for this parameter. Using hostaddr instead of host allows the @@ -119,9 +119,10 @@ PGconn *PQconnectdb(const char *conninfo); applications with time constraints. However, Kerberos authentication requires the host name. The following therefore applies: If host is specified without hostaddr, a host name - lookup is forced. If hostaddr is specified without + lookup occurs. If hostaddr is specified without host, the value for hostaddr gives the remote - address; if Kerberos is used, this causes a reverse name query. If both + address. When Kerberos is used, a reverse name query occurs to obtain + the host name for Kerberos. If both host and hostaddr are specified, the value for hostaddr gives the remote address; the value for host is ignored, unless Kerberos is used, in which case that @@ -153,7 +154,7 @@ PGconn *PQconnectdb(const char *conninfo); dbname - The database name. + The database name. Defaults to be the same as the user name. @@ -162,7 +163,7 @@ PGconn *PQconnectdb(const char *conninfo); user - User name to connect as. + PostgreSQL user name to connect as. @@ -182,7 +183,7 @@ PGconn *PQconnectdb(const char *conninfo); Maximum wait for connection, in seconds (write as a decimal integer string). Zero or not specified means wait indefinitely. It is not - recommended to set the timeout to less than 2 seconds. + recommended to use a timeout of less than 2 seconds. @@ -237,7 +238,7 @@ PGconn *PQconnectdb(const char *conninfo); If any parameter is unspecified, then the corresponding environment variable (see ) is checked. If the environment variable is not set either, - then hardwired defaults are used. + then built-in defaults are used. @@ -260,8 +261,9 @@ PGconn *PQsetdbLogin(const char *pghost, This is the predecessor of PQconnectdb with a fixed - number of parameters. It has the same functionality except that the - missing parameters cannot be specified in the call. + set of parameters. It has the same functionality except that the + missing parameters will always take on default values. Write NULL or an + empty string for any one of the fixed parameters that is to be defaulted. @@ -306,6 +308,10 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); These two functions are used to open a connection to a database server such that your application's thread of execution is not blocked on remote I/O whilst doing so. + The point of this approach is that the waits for I/O to complete can occur + in the application's main loop, rather than down inside + PQconnectdb(), and so the application can manage this + operation in parallel with other activities. The database connection is made using the parameters taken from the string @@ -313,7 +319,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); the same format as described above for PQconnectdb. - Neither PQconnectStart nor PQconnectPoll will block, as long as a number of + Neither PQconnectStart nor PQconnectPoll will block, so long as a number of restrictions are met: @@ -326,14 +332,14 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); - If you call PQtrace, ensure that the stream object into which you trace - will not block. + If you call PQtrace, ensure that the stream object + into which you trace will not block. - You ensure for yourself that the socket is in the appropriate state + You ensure that the socket is in the appropriate state before calling PQconnectPoll, as described below. @@ -349,28 +355,31 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); CONNECTION_BAD, PQconnectStart has failed. - If PQconnectStart succeeds, the next stage is to poll libpq so that it may - proceed with the connection sequence. Loop thus: If PQconnectPoll(conn) last returned - PGRES_POLLING_READING, perform a select() for reading on the socket determined using PQsocket(conn). If - it last returned PGRES_POLLING_WRITING, perform a select() for writing on - that same socket. If you have yet to call PQconnectPoll, i.e., after the call - to PQconnectStart, behave as if it last returned PGRES_POLLING_WRITING. If - select() shows that the socket is ready, consider it active. If it has - been decided that this connection is active, call PQconnectPoll(conn) - again. If this call returns PGRES_POLLING_FAILED, the connection procedure - has failed. If this call returns PGRES_POLLING_OK, the connection has been - successfully made. - - - - Note that the use of select() to ensure that the socket is ready is merely - a (likely) example; those with other facilities available, such as a - poll() call, may of course use that instead. + If PQconnectStart succeeds, the next stage is to poll + libpq so that it may proceed with the connection sequence. + Use PQsocket(conn) to obtain the descriptor of the + socket underlying the database connection. + Loop thus: If PQconnectPoll(conn) last returned + PGRES_POLLING_READING, wait until the socket is ready to + read (as indicated by select(), poll(), or + similar system function). + Then call PQconnectPoll(conn) again. + Conversely, if PQconnectPoll(conn) last returned + PGRES_POLLING_WRITING, wait until the socket is ready + to write, then call PQconnectPoll(conn) again. + If you have yet to call + PQconnectPoll, i.e., just after the call to + PQconnectStart, behave as if it last returned + PGRES_POLLING_WRITING. Continue this loop until + PQconnectPoll(conn) returns + PGRES_POLLING_FAILED, indicating the connection procedure + has failed, or PGRES_POLLING_OK, indicating the connection + has been successfully made. At any time during connection, the status of the connection may be - checked, by calling PQstatus. If this gives CONNECTION_BAD, then the + checked by calling PQstatus. If this gives CONNECTION_BAD, then the connection procedure has failed; if it gives CONNECTION_OK, then the connection is ready. Both of these states are equally detectable from the return value of PQconnectPoll, described above. Other states may also occur @@ -410,17 +419,25 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); CONNECTION_AUTH_OK - Received authentication; waiting for connection start-up to continue. + Received authentication; waiting for backend start-up to finish. + + + + + + CONNECTION_SSL_STARTUP + + + Negotiating SSL encryption. CONNECTION_SETENV - - Negotiating environment (part of the connection start-up). + Negotiating environment-driven parameter settings. @@ -429,7 +446,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn); Note that, although these constants will remain (in order to maintain compatibility), an application should never rely upon these appearing in a particular order, or at all, or on the status always being one of these - documented values. An application may do something like this: + documented values. An application might do something like this: switch(PQstatus(conn)) { @@ -449,21 +466,20 @@ switch(PQstatus(conn)) + + The connect_timeout connection parameter is ignored + when using PQconnectPoll; it is the application's + responsibility to decide whether an excessive amount of time has elapsed. + Otherwise, PQconnectStart followed by a + PQconnectPoll loop is equivalent to + PQconnectdb. + + Note that if PQconnectStart returns a non-null pointer, you must call PQfinish when you are finished with it, in order to dispose of - the structure and any associated memory blocks. This must be done even if a - call to PQconnectStart or PQconnectPoll failed. - - - - PQconnectPoll will currently block if - libpq is compiled with SSL support. This restriction may be removed in the future. - - - - Finally, these functions leave the connection in a nonblocking state as if - PQsetnonblocking had been called. + the structure and any associated memory blocks. This must be done even if + the connection attempt fails or is abandoned. @@ -498,7 +514,8 @@ typedef struct be used to determine all possible PQconnectdb options and their current default values. The return value points to an array of PQconninfoOption structures, which ends with an entry having a null - key-word pointer. Note that the current default values (val fields) + keyword pointer. Note that the current default values + (val fields) will depend on environment variables and other context. Callers must treat the connection options data as read-only. @@ -527,7 +544,8 @@ void PQfinish(PGconn *conn); Note that even if the server connection attempt fails (as indicated by PQstatus), the application should call PQfinish to free the memory used by the PGconn object. - The PGconn pointer should not be used after PQfinish has been called. + The PGconn pointer must not be used again after + PQfinish has been called. @@ -584,12 +602,23 @@ PostgresPollingStatusType PQresetPoll(PGconn *conn); + + +Connection Status Functions + + + These functions may be used to interrogate the status + of an existing database connection object. + + + libpq-fe.h libpq-int.h libpq application programmers should be careful to -maintain the PGconn abstraction. Use the accessor functions below to get +maintain the PGconn abstraction. Use the accessor +functions described below to get at the contents of PGconn. Avoid directly referencing the fields of the PGconn structure because they are subject to change in the future. (Beginning in PostgreSQL release 6.4, the @@ -597,6 +626,12 @@ definition of the struct behind PGconn is not even p If you have old code that accesses PGconn fields directly, you can keep using it by including libpq-int.h too, but you are encouraged to fix the code soon.) + + + + +The following functions return parameter values established at connection. +These values are fixed for the life of the PGconn object. @@ -608,12 +643,6 @@ soon.) char *PQdb(const PGconn *conn); - - -PQdb and the next several functions return the values established -at connection. These values are fixed for the life of the PGconn -object. - @@ -691,7 +720,14 @@ char *PQoptions(const PGconn *conn); + + + +The following functions return status data that can change as operations +are executed on the PGconn object. + + PQstatus @@ -728,6 +764,91 @@ ConnStatusType PQstatus(const PGconn *conn); + +PQtransactionStatus + + + Returns the current in-transaction status of the server. + +PGTransactionStatusType PQtransactionStatus(const PGconn *conn); + + +The status can be PQTRANS_IDLE (currently idle), +PQTRANS_ACTIVE (a command is in progress), +PQTRANS_INTRANS (idle, in a valid transaction block), +or PQTRANS_INERROR (idle, in a failed transaction block). +PQTRANS_UNKNOWN is reported if the connection is bad. +PQTRANS_ACTIVE is reported only when a query +has been sent to the server and not yet completed. + + + +PQtransactionStatus will give incorrect results when using +a PostgreSQL 7.3 server that has AUTOCOMMIT +set to OFF. The server-side autocommit feature has been +deprecated and does not exist in later server versions. + + + + + + +PQparameterStatus + + + Looks up a current parameter setting of the server. + +const char *PQparameterStatus(const PGconn *conn, const char *paramName); + + +Certain parameter values are reported by the server automatically at +connection startup or whenever their values change. +PQparameterStatus can be used to interrogate these settings. +It returns the current value of a parameter if known, or NULL if the parameter +is not known. + + + +Parameters reported as of the current release include +server_version (cannot change after startup); +server_encoding (also not presently changeable after start); +client_encoding, and +DateStyle. + + + +Pre-3.0-protocol servers do not report parameter settings, +but libpq includes logic to obtain values for +server_version, server_encoding, and +client_encoding. Applications are encouraged to use +PQparameterStatus rather than ad-hoc code to determine these +values. (Beware however that on a pre-3.0 connection, changing +client_encoding via SET after connection startup +will not be reflected by PQparameterStatus.) + + + + + +PQprotocolVersion + + + Interrogates the frontend/backend protocol being used. + +int PQprotocolVersion(const PGconn *conn); + +Applications may wish to use this to determine whether certain features +are supported. +Currently, the possible values are 2 (2.0 protocol), 3 (3.0 protocol), +or zero (connection bad). This will not change after connection +startup is complete, but it could theoretically change during a reset. +The 3.0 protocol will normally be used when communicating with +PostgreSQL 7.4 or later servers; pre-7.4 servers support +only protocol 2.0. (Protocol 1.0 is obsolete and not supported by libpq.) + + + + PQerrorMessage @@ -757,7 +878,8 @@ char *PQerrorMessage(const PGconn* conn); Obtains the file descriptor number of the connection socket to the server. A valid descriptor will be greater than or equal to 0; a result of -1 indicates that no server connection is - currently open. + currently open. (This will not change during normal operation, + but could change during connection setup or reset.) int PQsocket(const PGconn *conn); @@ -812,9 +934,10 @@ SSL *PQgetssl(const PGconn *conn); - - - + + + + Command Execution Functions @@ -828,6 +951,7 @@ SQL queries and commands. Main Functions + PQexec @@ -836,8 +960,7 @@ SQL queries and commands. Submits a command to the server and waits for the result. -PGresult *PQexec(PGconn *conn, - const char *command); +PGresult *PQexec(PGconn *conn, const char *command); @@ -854,21 +977,95 @@ PGresult *PQexec(PGconn *conn, +It is allowed to include multiple SQL commands (separated by semicolons) in +the command string. Multiple queries sent in a single PQexec +call are processed in a single transaction, unless there are explicit +BEGIN/COMMIT commands included in the query string to divide it into multiple +transactions. Note however that the returned PGresult +structure describes only the result of the last command executed from the +string. Should one of the commands fail, processing of the string stops with +it and the returned PGresult describes the error +condition. + + -The PGresult structure encapsulates the result + + +PQexecParams + + + Submits a command to the server and waits for the result, + with the ability to pass parameters separately from the SQL + command text. + +PGresult *PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + + + + +PQexecParams is like PQexec, but offers additional +functionality: parameter values can be specified separately from the command +string proper, and query results can be requested in either text or binary +format. PQexecParams is supported only in protocol 3.0 and later +connections; it will fail when using protocol 2.0. + + + +If parameters are used, they are referred to in the command string +as $1, $2, etc. +nParams is the number of parameters supplied; it is the length +of the arrays paramTypes[], paramValues[], +paramLengths[], and paramFormats[]. (The +array pointers may be NULL when nParams is zero.) +paramTypes[] specifies, by OID, the datatypes to be assigned to +the parameter symbols. If paramTypes is NULL, or any particular +element in the array is zero, the backend assigns a datatype to the parameter +symbol in the same way it would do for an untyped literal string. +paramValues[] specifies the actual values of the parameters. +A NULL pointer in this array means the corresponding parameter is NULL; +otherwise the pointer points to a zero-terminated text string (for text +format) or binary data in the format expected by the backend (for binary +format). +paramLengths[] specifies the actual data lengths of +binary-format parameters. It is ignored for NULL parameters and text-format +parameters. The array pointer may be NULL when there are no binary +parameters. +paramFormats[] specifies whether parameters are text (put a zero +in the array) or binary (put a one in the array). If the array pointer is +NULL then all parameters are presumed to be text. +resultFormat is zero to obtain results in text format, or one to +obtain results in binary format. (There is not currently a provision to +obtain different result columns in different formats, although that is +possible in the underlying protocol.) + + + + + +The primary advantage of PQexecParams over PQexec +is that parameter values may be separated from the command string, thus +avoiding the need for tedious and error-prone quoting and escaping. + +Unlike PQexec, PQexecParams allows at most one SQL +command in the given string. (There can be semicolons in it, but not more +than one nonempty command.) This is a limitation of the underlying protocol, +but has some usefulness as an extra defense against SQL-injection attacks. + + + +The PGresult structure encapsulates the result returned by the server. libpq application programmers should be careful to maintain the PGresult abstraction. Use the accessor functions below to get at the contents of PGresult. Avoid directly referencing the fields of the PGresult structure because they are subject to change in the future. - - - -Multiple queries sent in a single -function call are processed in a single transaction, unless there are explicit -BEGIN/COMMIT commands included in the query string to divide it into multiple -transactions. - @@ -902,7 +1099,8 @@ ExecStatusType PQresultStatus(const PGresult *res); PGRES_TUPLES_OK - The query successfully executed. + Successful completion of a command returning data (such as + a SELECT or SHOW). @@ -930,7 +1128,7 @@ ExecStatusType PQresultStatus(const PGresult *res); PGRES_NONFATAL_ERROR - A nonfatal error occurred. + A nonfatal error (a notice or warning) occurred. @@ -948,8 +1146,15 @@ the query. Note that a SELECT command that happens to retrieve zero rows still shows PGRES_TUPLES_OK. PGRES_COMMAND_OK is for commands that can never return rows (INSERT, UPDATE, -etc.). A response of PGRES_EMPTY_QUERY often -exposes a bug in the client software. +etc.). A response of PGRES_EMPTY_QUERY may indicate +a bug in the client software. + + + +A result of status PGRES_NONFATAL_ERROR will never be +returned directly by PQexec or other query +execution functions; results of this kind are instead passed to the notice +processor (see ). @@ -976,6 +1181,7 @@ if there was no error. char *PQresultErrorMessage(const PGresult *res); +If there was an error, the returned string will include a trailing newline. @@ -991,6 +1197,39 @@ when you want to know the status from the latest operation on the connection. + +PQresultErrorField + + +Returns an individual field of an error report. + +char *PQresultErrorField(const PGresult *res, int fieldcode); + +fieldcode is an error field identifier defined by the +PostgreSQL protocol (see ), for example 'C' for +the SQLSTATE error code. NULL is returned if the +PGresult is not an error or warning result, +or does not include the specified field. Field values will normally +not include a trailing newline. + + + +Errors generated internally by libpq will have severity and primary message, +but typically no other fields. Errors returned by a pre-3.0-protocol server +will include severity and primary message, and sometimes a detail message, +but no other fields. + + + +Note that error fields are only available from +PGresult objects, not +PGconn objects; there is no +PQerrorField function. + + + + PQclear @@ -1008,7 +1247,7 @@ void PQclear(PQresult *res); need it; it does not go away when you issue a new command, nor even if you close the connection. To get rid of it, you must call PQclear. Failure to do this will - result in memory leaks in your client application. + result in memory leaks in your application. @@ -1035,161 +1274,20 @@ as with a PGresult returned by libpq - - - - Escaping Strings for Inclusion in SQL Commands - - escaping strings - - -PQescapeString escapes a string for use within an SQL commmand. - -size_t PQescapeString (char *to, const char *from, size_t length); - - - - -If you want to use strings that have been received -from a source that is not trustworthy (for example, because a random user -entered them), you should not directly include them in SQL -commands for security reasons. Instead, you have to escape certain -characters that are otherwise interpreted specially by the SQL parser. -PQescapeString performs this operation. - - -The parameter from points to the first character of the string that -is to be escaped, and the length parameter counts the -number of characters in this string. (A terminating zero byte is -neither necessary nor counted.) to shall point to a -buffer that is able to hold at least one more character than twice -the value of length, otherwise the behavior is -undefined. A call to PQescapeString writes an escaped -version of the from string to the to -buffer, replacing special characters so that they cannot cause any -harm, and adding a terminating zero byte. The single quotes that -must surround PostgreSQL string literals are not part of the result -string. - - -PQescapeString returns the number of characters written -to to, not including the terminating zero byte. -Behavior is undefined when the to and from -strings overlap. - - - Escaping Binary Strings for Inclusion in SQL Commands - - escaping binary strings - - - - - PQescapeBytea - - - Escapes binary data for use within an SQL command with the type bytea. - -unsigned char *PQescapeBytea(const unsigned char *from, - size_t from_length, - size_t *to_length); - - - - - Certain byte values must be escaped (but all - byte values may be escaped) when used as part - of a bytea literal in an SQL - statement. In general, to escape a byte, it is converted into the - three digit octal number equal to the octet value, and preceded by - two backslashes. The single quote (') and backslash - (\) characters have special alternative escape - sequences. See for more - information. PQescapeBytea performs this - operation, escaping only the minimally required bytes. - - - - The from parameter points to the first - byte of the string that is to be escaped, and the - from_length parameter reflects the number of - bytes in this binary string. (A terminating zero byte is - neither necessary nor counted.) The to_length - parameter points to a variable that will hold the resultant - escaped string length. The result string length includes the terminating - zero byte of the result. - - - - PQescapeBytea returns an escaped version of the - from parameter binary string in memory - allocated with malloc(), and must be freed using - PQfreemem(). - The return string has all special characters replaced - so that they can be properly processed by the PostgreSQL string literal - parser, and the bytea input function. A terminating zero - byte is also added. The single quotes that must surround - PostgreSQL string literals are not part of the result string. - - - - - - PQunescapeBytea - - - Converts an escaped string representation of binary data into binary - data --- the reverse of PQescapeBytea. - -unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length); - - - - - The from parameter points to an escaped string - such as might be returned by PQgetvalue when applied to a - bytea column. PQunescapeBytea converts - this string representation into its binary representation. - It returns a pointer to a buffer allocated with - malloc(), or null on error, and puts the size of - the buffer in to_length. The memory must be - freed using PQfreemem(). - - - - - - - PQfreemem - - - Frees memory allocated by libpq - -void PQfreemem(void *ptr); - - - - - Frees memory allocated by libpq, particularly - PQescapeBytea, - PQunescapeBytea, - and PQnotifies. - It is needed by Win32, which can not free memory across - DLL's, unless multithreaded DLL's (/MD in VC6) are used. - - - - - - - - Retrieving Query Result Information + +These functions are used to extract information from a +PGresult object that represents a successful +query result (that is, one that has status +PGRES_TUPLES_OK). For objects with other status +values they will act as though the result has zero rows and zero columns. + + PQntuples @@ -1228,6 +1326,10 @@ char *PQfname(const PGresult *res, int column_number); + + +NULL is returned if the column number is out of range. + @@ -1249,11 +1351,81 @@ int PQfnumber(const PGresult *res, + +PQftable + + + Returns the OID of the table from which the given column was fetched. + Column numbers start at 0. + +Oid PQftable(const PGresult *res, + int column_number); + + + + +InvalidOid is returned if the column number is out of range, +or if the specified column is not a simple reference to a table column, +or when using pre-3.0 protocol. +You can query the system table pg_class to determine +exactly which table is referenced. + + + + The type Oid and the constant + InvalidOid will be defined when you include + the libpq header file. They will + both be some integer type. + + + + + +PQftablecol + + + Returns the column number (within its table) of the column making up + the specified query result column. + Result column numbers start at 0. + +int PQftablecol(const PGresult *res, + int column_number); + + + + +Zero is returned if the column number is out of range, +or if the specified column is not a simple reference to a table column, +or when using pre-3.0 protocol. + + + + + +PQfformat + + + Returns the format code indicating the format of the given column. + Column numbers start at 0. + +int PQfformat(const PGresult *res, + int column_number); + + + + +Format code zero indicates textual data representation, while format +code one indicates binary representation. (Other codes are reserved +for future definition.) + + + + PQftype - Returns the column data type associated with the + Returns the data type associated with the given column number. The integer returned is the internal OID number of the type. Column numbers start at 0. @@ -1265,7 +1437,7 @@ Oid PQftype(const PGresult *res, You can query the system table pg_type to obtain -the name and properties of the various data types. The OIDs +the names and properties of the various data types. The OIDs of the built-in data types are defined in the file src/include/catalog/pg_type.h in the source tree. @@ -1276,7 +1448,7 @@ in the source tree. PQfmod - Returns the type-specific modification data of the column + Returns the type modifier of the column associated with the given column number. Column numbers start at 0. @@ -1284,6 +1456,13 @@ int PQfmod(const PGresult *res, int column_number); + + +The interpretation of modifier values is type-specific; they typically +indicate precision or size limits. The value -1 is used to indicate +no information available. Most data types do not use modifiers, +in which case the value is always -1. + @@ -1301,9 +1480,10 @@ int PQfsize(const PGresult *res, - PQfsize returns the space allocated for this column in a database - row, in other words the size of the server's binary representation - of the data type. -1 is returned if the column has a variable size. +PQfsize returns the space allocated for this column in a database +row, in other words the size of the server's internal representation +of the data type. (Accordingly, it is not really very useful to clients.) +A negative value indicates the data type is variable-length. @@ -1312,33 +1492,31 @@ int PQfsize(const PGresult *res, PQbinaryTuples - Returns 1 if the PGresult contains binary row data - and 0 if it contains text data. +Returns 1 if the PGresult contains binary data +and 0 if it contains text data. int PQbinaryTuples(const PGresult *res); -Currently, binary row data can only be returned by a query that -extracts data from a binary cursor. +This function is deprecated (except for its use in connection with +COPY), because it is possible for a single +PGresult +to contain text data in some columns and binary data in others. +PQfformat() is preferred. PQbinaryTuples +returns 1 only if all columns of the result are binary (format 1). - - - - Retrieving Query Result Values - - PQgetvalue - Returns a single column value of one row + Returns a single field value of one row of a PGresult. - Row and colums indices start at 0. + Row and column numbers start at 0. char* PQgetvalue(const PGresult *res, int row_number, @@ -1347,15 +1525,18 @@ char* PQgetvalue(const PGresult *res, -For most queries, the value returned by PQgetvalue +For data in text format, the value returned by PQgetvalue is a null-terminated character string representation -of the column value. But if PQbinaryTuples returns 1, -the value returned by PQgetvalue is the binary -representation of the -type in the internal format of the backend server -(but not including the size word, if the column is variable-length). -It is then the programmer's responsibility to cast and -convert the data to the correct C type. +of the field value. For data in binary format, the value is in the binary +representation determined by the datatype's typsend and +typreceive functions. (The value is actually followed by +a zero byte in this case too, but that is not ordinarily useful, since +the value is likely to contain embedded nulls.) + + + +An empty string is returned if the field value is NULL. See +PQgetisnull to distinguish NULLs from empty-string values. @@ -1373,7 +1554,7 @@ be used past the lifetime of the PGresult structure i PQgetisnull - Tests a column for a null value. + Tests a field for a null value. Row and column numbers start at 0. int PQgetisnull(const PGresult *res, @@ -1383,10 +1564,9 @@ int PQgetisnull(const PGresult *res, - This function returns 1 if the column is null and 0 if - it contains a non-null value. (Note that PQgetvalue - will return an empty string, not a null pointer, for a null - column.) +This function returns 1 if the field is null and 0 if +it contains a non-null value. (Note that PQgetvalue +will return an empty string, not a null pointer, for a null field.) @@ -1395,7 +1575,7 @@ int PQgetisnull(const PGresult *res, PQgetlength - Returns the length of a column value in bytes. + Returns the actual length of a field value in bytes. Row and column numbers start at 0. int PQgetlength(const PGresult *res, @@ -1406,8 +1586,10 @@ int PQgetlength(const PGresult *res, This is the actual data length for the particular data value, that is, the -size of the object pointed to by PQgetvalue. Note that for character-represented -values, this size has little to do with the binary size reported by PQfsize. +size of the object pointed to by PQgetvalue. For text +data format this is the same as strlen(). For binary format +this is essential information. Note that one should not rely +on PQfsize to obtain the actual data length. @@ -1440,8 +1622,8 @@ typedef struct { This function was formerly used by psql -to print query results, but this is no longer the case and this -function is no longer actively supported. +to print query results, but this is no longer the case. Note that it +assumes all the data is in text format. @@ -1451,17 +1633,27 @@ function is no longer actively supported. Retrieving Result Information for Other Commands + +These functions are used to extract information from +PGresult objects that are not SELECT +results. + + PQcmdStatus - Returns the command status string from the SQL command that + Returns the command status tag from the SQL command that generated the PGresult. char * PQcmdStatus(PGresult *res); + +Commonly this is just the name of the command, but it may include additional +data such as the number of rows processed. + @@ -1477,7 +1669,9 @@ char * PQcmdTuples(PGresult *res); If the SQL command that generated the - PGresult was INSERT, UPDATE, or DELETE, this returns a + PGresult was INSERT, + UPDATE, DELETE, MOVE, + or FETCH, this returns a string containing the number of rows affected. If the command was anything else, it returns the empty string. @@ -1496,13 +1690,6 @@ char * PQcmdTuples(PGresult *res); Oid PQoidValue(const PGresult *res); - - - The type Oid and the constant - InvalidOid will be defined if you include - the libpq header file. They will - both be some integer type. - @@ -1523,14 +1710,184 @@ char * PQoidStatus(const PGresult *res); -This function is deprecated in favor of PQoidValue -and is not thread-safe. +This function is deprecated in favor of PQoidValue. +It is not thread-safe. + + + Escaping Strings for Inclusion in SQL Commands + + escaping strings + + +PQescapeString escapes a string for use within an SQL +commmand. This is useful when inserting data values as literal constants +in SQL commands. Certain characters (such as quotes and backslashes) must +be escaped to prevent them from being interpreted specially by the SQL parser. +PQescapeString performs this operation. + + + + +It is especially important to do proper escaping when handling strings that +were received from an untrustworthy source. Otherwise there is a security +risk: you are vulnerable to SQL injection attacks wherein unwanted +SQL commands are fed to your database. + + + + +Note that it is not necessary nor correct to do escaping when a data +value is passed as a separate parameter in PQexecParams or +PQsendQueryParams. + + +size_t PQescapeString (char *to, const char *from, size_t length); + + + + +The parameter from points to the first character of the string +that +is to be escaped, and the length parameter gives the +number of characters in this string. (A terminating zero byte is +neither necessary nor counted.) to shall point to a +buffer that is able to hold at least one more character than twice +the value of length, otherwise the behavior is +undefined. A call to PQescapeString writes an escaped +version of the from string to the to +buffer, replacing special characters so that they cannot cause any +harm, and adding a terminating zero byte. The single quotes that +must surround PostgreSQL string literals are not +included in the result string; they should be provided in the SQL +command that the result is inserted into. + + +PQescapeString returns the number of characters written +to to, not including the terminating zero byte. + + +Behavior is undefined if the to and from +strings overlap. + + + + + + Escaping Binary Strings for Inclusion in SQL Commands + + escaping binary strings + + + + + PQescapeBytea + + + Escapes binary data for use within an SQL command with the type + bytea. As with PQescapeString, + this is only used when inserting data directly into an SQL command string. + +unsigned char *PQescapeBytea(const unsigned char *from, + size_t from_length, + size_t *to_length); + + + + + Certain byte values must be escaped (but all + byte values may be escaped) when used as part + of a bytea literal in an SQL + statement. In general, to escape a byte, it is converted into the + three digit octal number equal to the octet value, and preceded by + two backslashes. The single quote (') and backslash + (\) characters have special alternative escape + sequences. See for more + information. PQescapeBytea performs this + operation, escaping only the minimally required bytes. + + + + The from parameter points to the first + byte of the string that is to be escaped, and the + from_length parameter gives the number of + bytes in this binary string. (A terminating zero byte is + neither necessary nor counted.) The to_length + parameter points to a variable that will hold the resultant + escaped string length. The result string length includes the terminating + zero byte of the result. + + + + PQescapeBytea returns an escaped version of the + from parameter binary string in memory + allocated with malloc(). This memory must be freed + using PQfreemem() when the result is no longer needed. + The return string has all special characters replaced + so that they can be properly processed by the PostgreSQL string literal + parser, and the bytea input function. A terminating zero + byte is also added. The single quotes that must surround + PostgreSQL string literals are not part of the result string. + + + + + + PQunescapeBytea + + + Converts an escaped string representation of binary data into binary + data --- the reverse of PQescapeBytea. + This is needed when retrieving bytea data in text format, + but not when retrieving it in binary format. + + +unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length); + + + + + The from parameter points to an escaped string + such as might be returned by PQgetvalue when applied to a + bytea column. PQunescapeBytea converts + this string representation into its binary representation. + It returns a pointer to a buffer allocated with + malloc(), or null on error, and puts the size of + the buffer in to_length. The result must be + freed using PQfreemem() when it is no longer needed. + + + + + + PQfreemem + + + Frees memory allocated by libpq. + +void PQfreemem(void *ptr); + + + + + Frees memory allocated by libpq, particularly + PQescapeBytea, + PQunescapeBytea, + and PQnotifies. + It is needed by Win32, which can not free memory across + DLL's, unless multithreaded DLL's (/MD in VC6) are used. + On other platforms it is the same as free(). + + + + + + @@ -1573,70 +1930,11 @@ discarded by PQexec. Applications that do not like these limitations can instead use the underlying functions that PQexec is built from: PQsendQuery and PQgetResult. - - -Older programs that used this functionality as well as -PQputline and PQputnbytes -could block waiting to send data to the server. To -address that issue, the function PQsetnonblocking -was added. -Old applications can neglect to use PQsetnonblocking -and get the old potentially blocking behavior. Newer programs can use -PQsetnonblocking to achieve a completely nonblocking -connection to the server. +There is also PQsendQueryParams, which can be +used with PQgetResult to duplicate the functionality +of PQexecParams. - - PQsetnonblocking - - - Sets the nonblocking status of the connection. - -int PQsetnonblocking(PGconn *conn, int arg); - - - - - Sets the state of the connection to nonblocking if arg is 1 and - blocking if arg is 0. Returns 0 if OK, -1 if error. - - - In the nonblocking state, calls to - PQputline, PQputnbytes, - PQsendQuery, and PQendcopy - will not block but instead return an error if they need to be called - again. - - - When a database connection has been set to nonblocking mode and - PQexec is called, it will temporarily set the state - of the connection to blocking until the PQexec call - completes. - - - More of libpq is expected to be made safe for - the nonblocking mode in the future. - - - - - -PQisnonblocking - - - Returns the blocking status of the database connection. - -int PQisnonblocking(const PGconn *conn); - - - - - Returns 1 if the connection is set to nonblocking mode and - 0 if blocking. - - - - PQsendQuery @@ -1646,12 +1944,9 @@ int PQisnonblocking(const PGconn *conn); successfully dispatched and 0 if not (in which case, use PQerrorMessage to get more information about the failure). -int PQsendQuery(PGconn *conn, - const char *command); +int PQsendQuery(PGconn *conn, const char *command); - - After successfully calling PQsendQuery, call PQgetResult one or more times to obtain the results. PQsendQuery may not be called @@ -1661,12 +1956,41 @@ int PQsendQuery(PGconn *conn, + +PQsendQueryParams + + + Submits a command and separate parameters to the server without + waiting for the result(s). + +int PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + + + This is equivalent to PQsendQuery except that + query parameters can be specified separately from the query string. + The function's parameters are handled identically to + PQexecParams. Like + PQexecParams, it will not work on 2.0-protocol + connections, and it allows only one command in the query string. + + + + PQgetResult - Waits for the next result from a prior PQsendQuery, - and return it. A null pointer is returned when the command is complete + Waits for the next result from a prior + PQsendQuery or + PQsendQueryParams, + and returns it. A null pointer is returned when the command is complete and there will be no more results. PGresult *PQgetResult(PGconn *conn); @@ -1697,8 +2021,8 @@ overlapped processing, by the way: the client can be handling the results of one command while the server is still working on later queries in the same command string.) However, calling PQgetResult will still cause the client to block until the server completes the -next SQL command. This can be avoided by proper use of three more -functions: +next SQL command. This can be avoided by proper use of two +more functions: @@ -1714,7 +2038,8 @@ int PQconsumeInput(PGconn *conn); PQconsumeInput normally returns 1 indicating no error, but returns 0 if there was some kind of trouble (in which case -PQerrorMessage can be used). Note that the result does not say +PQerrorMessage can be consulted). Note that the result +does not say whether any input data was actually collected. After calling PQconsumeInput, the application may check PQisBusy and/or PQnotifies to see if @@ -1750,35 +2075,13 @@ state will never end. - - -PQflush - - -Attempts to flush any data queued to the server. -Returns 0 if successful (or if the send queue is empty), -1 if it failed for -some reason, or 1 if it was unable to send all the data in the send queue yet -(this case can only occur if the connection is nonblocking). - -int PQflush(PGconn *conn); - - - - -PQflush needs to be called on a nonblocking connection -before calling select() to determine if a response has -arrived. If 0 is returned it ensures that there is no data queued to the -server that has not actually been sent. Only applications that have used -PQsetnonblocking have a need for this. - - - A typical application using these functions will have a main loop that uses -select() to wait for all the conditions that it must +select() or poll() to wait for all the +conditions that it must respond to. One of the conditions will be input available from the server, which in terms of select() means readable data on the file descriptor identified by PQsocket. @@ -1789,13 +2092,6 @@ if PQisBusy returns false (0). It can also call PQnotifies to detect NOTIFY messages (see ). - -Nonblocking connections (that have used PQsetnonblocking) -should not use select() until PQflush -has returned 0 indicating that there is no buffered data waiting to be sent -to the server. - - A client that uses PQsendQuery/PQgetResult can also attempt to cancel a command that is still being processed by the server. @@ -1843,6 +2139,89 @@ interactive cancellation of commands that it issues through PQexec + +By using the functions described above, it is possible to avoid blocking +while waiting for input from the database server. However, it is still +possible that the application will block waiting to send output to the +server. This is relatively uncommon but can happen if very long SQL commands +or data values are sent. (It is much more probable if the application +sends data via COPY IN, however.) To prevent this possibility and achieve +completely nonblocking database operation, the following additional +functions may be used. + + + + PQsetnonblocking + + + Sets the nonblocking status of the connection. + +int PQsetnonblocking(PGconn *conn, int arg); + + + + + Sets the state of the connection to nonblocking if + arg is 1, or + blocking if arg is 0. Returns 0 if OK, -1 if error. + + + In the nonblocking state, calls to + PQsendQuery, + PQputline, PQputnbytes, + and PQendcopy + will not block but instead return an error if they need to be called + again. + + + Note that PQexec does not honor nonblocking mode; + if it is called, it will act in blocking fashion anyway. + + + + + +PQisnonblocking + + + Returns the blocking status of the database connection. + +int PQisnonblocking(const PGconn *conn); + + + + + Returns 1 if the connection is set to nonblocking mode and + 0 if blocking. + + + + + +PQflush + + +Attempts to flush any queued output data to the server. +Returns 0 if successful (or if the send queue is empty), -1 if it failed for +some reason, or 1 if it was unable to send all the data in the send queue yet +(this case can only occur if the connection is nonblocking). + +int PQflush(PGconn *conn); + + + + + + + + +After sending any command or data on a nonblocking connection, call +PQflush. If it returns 1, wait for the socket to be +write-ready and call it again; repeat until it returns 0. Once +PQflush returns 0, wait for the socket to be read-ready +and then read the response as described above. + + @@ -1850,10 +2229,18 @@ interactive cancellation of commands that it issues through PQexec PostgreSQL provides a fast-path interface to send -function calls to the server. This is a trapdoor into system internals and -can be a potential security hole. Most users will not need this feature. +simple function calls to the server. + + +This interface is somewhat obsolete, as one may achieve similar performance +and greater functionality by setting up a prepared statement to define the +function call. Then, executing the statement with binary transmission of +parameters and results substitutes for a fast-path function call. + + + The function PQfn requests execution of a server function via the fast-path interface: @@ -1879,25 +2266,42 @@ typedef struct { The fnid argument is the OID of the function to be - executed. + executed. args and nargs define the + parameters to be passed to the function; they must match the declared + function argument list. When the isint field of a + parameter + struct is true, + the u.integer value is sent to the server as an integer + of the indicated length (this must be 1, 2, or 4 bytes); proper + byte-swapping occurs. When isint is false, the + indicated number of bytes at *u.ptr are sent with no + processing; the data must be in the format expected by the server for + binary transmission of the function's argument datatype. result_buf is the buffer in which to place the return value. The caller must have allocated sufficient space to store the return value. (There is no check!) The actual result length will be returned in the integer pointed - to by result_len. If a 4-byte integer result is expected, set - result_is_int to 1, otherwise set it to 0. (Setting result_is_int to 1 - tells libpq to byte-swap the value if necessary, so that it is + to by result_len. + If a 1, 2, or 4-byte integer result is expected, set + result_is_int to 1, otherwise set it to 0. + Setting result_is_int to 1 + causes libpq to byte-swap the value if necessary, so that + it is delivered as a proper int value for the client machine. When - result_is_int is 0, the byte string sent by the server is returned - unmodified.) - args and nargs specify the arguments to be passed to the function. + result_is_int is 0, the binary-format byte string sent by + the server is returned unmodified. - PQfn always returns a valid PGresult pointer. The result status - should be checked before the result is used. The - caller is responsible for freeing the PGresult with - PQclear when it is no longer needed. +PQfn always returns a valid PGresult pointer. The result status +should be checked before the result is used. The +caller is responsible for freeing the PGresult with +PQclear when it is no longer needed. + + + +Note that it is not possible to handle NULL arguments, NULL results, nor +set-valued results when using this interface. @@ -1909,7 +2313,7 @@ typedef struct { PostgreSQL offers asynchronous notification via the -LISTEN and NOTIFY commands. A server-side session registers its interest in a particular +LISTEN and NOTIFY commands. A client session registers its interest in a particular notification condition with the LISTEN command (and can stop listening with the UNLISTEN command). All sessions listening on a particular condition will be notified asynchronously when a NOTIFY command with that @@ -1922,7 +2326,7 @@ not necessary for there to be any associated table. libpq applications submit LISTEN and UNLISTEN -commands as ordinary SQL command. The arrival of NOTIFY +commands as ordinary SQL commands. The arrival of NOTIFY messages can subsequently be detected by calling PQnotifies. @@ -1937,18 +2341,30 @@ The function PQnotifies PGnotify* PQnotifies(PGconn *conn); typedef struct pgNotify { - char *relname; /* notification name */ + char *relname; /* notification condition name */ int be_pid; /* process ID of server process */ + char *extra; /* notification parameter */ } PGnotify; -After processing a PGnotify object returned by PQnotifies, -be sure to free it with PQfreemem(). +After processing a PGnotify object returned by +PQnotifies, be sure to free it with +PQfreemem. It is sufficient to free the +PGnotify pointer; the +relname and extra fields +do not represent separate allocations. + + + At present the extra field is unused and will + always point to an empty string. + + + In PostgreSQL 6.4 and later, - the be_pid is that of the notifying backend process, + the be_pid is that of the notifying backend process, whereas in earlier versions it was always the PID of your own backend process. @@ -1996,19 +2412,263 @@ if any notifications came in during the processing of the command. - The COPY command in PostgreSQL has options to read from - or write to the network connection used by libpq. - Therefore, functions are necessary to access this network - connection directly so applications may take advantage of this capability. + The COPY command in PostgreSQL + has options to read from or write to the network connection used by + libpq. The functions described in this section + allow applications to take advantage of this capability by supplying or + consuming copied data. - These functions should be executed only after obtaining a result - status of PGRES_COPY_OUT or + The overall process is that the application first issues the SQL + COPY command via PQexec or one + of the equivalent functions. The response to this (if there is no error + in the command) will be a PGresult object bearing a status + code of PGRES_COPY_OUT or PGRES_COPY_IN + (depending on the specified copy direction). The application should then + use the functions of this section to receive or transmit data rows. + When the data transfer is complete, another PGresult object + is returned to indicate success or failure of the transfer. Its status + will be PGRES_COMMAND_OK for success or + PGRES_FATAL_ERROR if some problem was encountered. + At this point further SQL commands may be issued via + PQexec. (It is not possible to execute other SQL + commands using the same connection while the COPY + operation is in progress.) + + + + If a COPY command is issued via + PQexec in a string that could contain additional + commands, the application must continue fetching results via + PQgetResult after completing the COPY + sequence. Only when PQgetResult returns NULL is it certain + that the PQexec command string is done and it is + safe to issue more commands. + + + + The functions of this section should be executed only after obtaining a + result status of PGRES_COPY_OUT or PGRES_COPY_IN from PQexec or PQgetResult. + + A PGresult object bearing one of these status values + carries some additional data about the COPY operation that + is starting. This additional data is available using functions that are + also used in connection with query results: + + + +PQnfields + + + Returns the number of columns (fields) to be copied. + + + + + +PQbinaryTuples + + + 0 indicates the overall copy format is textual (rows + separated by newlines, columns separated by separator + characters, etc). + 1 indicates the overall copy format is binary. + See + for more information. + + + + + +PQfformat + + + Returns the format code (0 for text, 1 for binary) associated + with each column of the copy operation. The per-column format + codes will always be zero when the overall copy format is textual, + but the binary format can support both text and binary columns. + (However, as of the current implementation of COPY, + only binary columns appear in a binary copy; so the per-column + formats always match the overall format at present.) + + + + + + + + +These additional data values are only available when using protocol 3.0. +When using protocol 2.0, all these functions will return 0. + + + + + Functions for Sending <command>COPY</command> Data + + + These functions are used to send data during COPY FROM STDIN. + They will fail if called when the connection is not in COPY_IN + state. + + + + +PQputCopyData + + + Sends data to the server during COPY_IN state. + +int PQputCopyData(PGconn *conn, + const char *buffer, + int nbytes); + + + + +Transmits the COPY data in the specified buffer, of length +nbytes, to the server. The result is 1 if the data was sent, +zero if it was not sent because the attempt would block (this case is only +possible if the connection is in nonblock mode), or -1 if an error occurred. +(Use PQerrorMessage to retrieve details if the return +value is -1. If the value is zero, wait for write-ready and try again.) + + + +The application may divide the COPY datastream into bufferloads of any +convenient size. Bufferload boundaries have no semantic significance when +sending. The contents of the datastream must match the data format expected +by the COPY command; see + for details. + + + + + +PQputCopyEnd + + + Sends end-of-data indication to the server during COPY_IN state. + +int PQputCopyEnd(PGconn *conn, + const char *errormsg); + + + + +Ends the COPY_IN operation successfully if errormsg +is NULL. If errormsg is not NULL then the COPY +is forced to fail, with the string pointed to by errormsg +used as the error message. (One should not assume that this exact error +message will come back from the server, however, as the server might have +already failed the COPY for its own reasons. Also note that the +option to force failure does not work when using pre-3.0-protocol connections.) + + + +The result is 1 if the termination data was sent, +zero if it was not sent because the attempt would block (this case is only +possible if the connection is in nonblock mode), or -1 if an error occurred. +(Use PQerrorMessage to retrieve details if the return +value is -1. If the value is zero, wait for write-ready and try again.) + + + +After successfully calling PQputCopyEnd, call +PQgetResult to obtain the final result status of the +COPY command. One may wait for +this result to be available in the usual way. Then return to normal +operation. + + + + + + + + + Functions for Receiving <command>COPY</command> Data + + + These functions are used to receive data during COPY TO STDOUT. + They will fail if called when the connection is not in COPY_OUT + state. + + + + +PQgetCopyData + + + Receives data from the server during COPY_OUT state. + +int PQgetCopyData(PGconn *conn, + char **buffer, + int async); + + + + +Attempts to obtain another row of data from the server during a COPY. +Data is always returned one data row at a time; if only a partial row +is available, it is not returned. Successful return of a data row +involves allocating a chunk of memory to hold the data. The +buffer parameter must be non-NULL. *buffer +is set to point to the allocated memory, or to NULL in cases where no +buffer is returned. A non-NULL result buffer must be freed using +PQfreemem when no longer needed. + + + +When a row is successfully returned, the return value is the number of +data bytes in the row (this will always be greater than zero). The +returned string is always null-terminated, though this is probably only +useful for textual COPY. A result of zero indicates that the COPY is +still in progress, but no row is yet available (this is only possible +when async is true). A +result of -1 indicates that the COPY is done. +A result of -2 indicates that an error occurred (consult +PQerrorMessage for the reason). + + + +When async is true (not zero), PQgetCopyData +will not block waiting for input; it will return zero if the COPY is still +in progress but no complete row is available. (In this case wait for +read-ready before trying again; it does not matter whether you call +PQconsumeInput.) When async is +false (zero), PQgetCopyData will block until data is available +or the operation completes. + + + +After PQgetCopyData returns -1, call +PQgetResult to obtain the final result status of the +COPY command. One may wait for +this result to be available in the usual way. Then return to normal +operation. + + + + + + + + + Obsolete Functions for <command>COPY</command> + + + These functions represent older methods of handling COPY. + Although they still work, they are deprecated due to poor error handling, + inconvenient methods of detecting end-of-data, and lack of support for binary + or nonblocking transfers. + + PQgetline @@ -2042,9 +2702,6 @@ receive lines that are more than length-1 characters long, care is needed to be sure it recognizes the \. line correctly (and does not, for example, mistake the end of a long data line for a terminator line). -The code in the file -src/bin/psql/copy.c -contains example functions that correctly handle the COPY protocol. @@ -2123,13 +2780,13 @@ call; it is okay to send a partial line or multiple lines per call. -Before PostgreSQL 7.4, it was necessary for the -application to explicitly send the two characters \. as a -final line to indicate to the server that it had finished sending COPY data. -While this still works, it is deprecated and the special meaning of -\. can be expected to be removed in a future release. -It is sufficient to call PQendcopy after having sent the -actual data. +Before PostgreSQL protocol 3.0, it was necessary +for the application to explicitly send the two characters +\. as a final line to indicate to the server that it had +finished sending COPY data. While this still works, it is deprecated and the +special meaning of \. can be expected to be removed in a +future release. It is sufficient to call PQendcopy after +having sent the actual data. @@ -2202,33 +2859,52 @@ This will work correctly only if the COPY is the only - -An example: - - -PQexec(conn, "CREATE TABLE foo (a integer, b varchar(16), d double precision);"); -PQexec(conn, "COPY foo FROM STDIN;"); -PQputline(conn, "3\thello world\t4.5\n"); -PQputline(conn, "4\tgoodbye world\t7.11\n"); -... -PQendcopy(conn); - - + - -Tracing Functions + +Control Functions + + +These functions control miscellaneous details of +libpq's behavior. + + +PQsetErrorVerbosity + + +Determines the verbosity of messages returned by +PQerrorMessage and PQresultErrorMessage. + +typedef enum { + PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE +} PGVerbosity; + +PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); + +PQsetErrorVerbosity sets the verbosity mode, returning the +connection's previous setting. +In TERSE mode, returned messages include severity, primary text, and position +only; this will normally fit on a single line. The DEFAULT mode produces +messages that include the above plus any detail, hint, or context fields +(these may span multiple lines). The VERBOSE mode includes all available +fields. Changing the verbosity does not affect the messages available from +already-existing PGresult objects, only subsequently-created +ones. + + + + PQtrace Enables tracing of the client/server communication to a debugging file stream. -void PQtrace(PGconn *conn - FILE *stream); +void PQtrace(PGconn *conn, FILE *stream); @@ -2249,14 +2925,43 @@ void PQuntrace(PGconn *conn); - + Notice Processing -The function PQsetNoticeProcessor +Notice and warning messages generated by the server are not returned by the +query execution functions, since they do not imply failure of the query. +Instead they are passed to a notice handling function, and execution continues +normally after the handler returns. The default notice handling function +prints the message on stderr, but the application can +override this behavior by supplying its own handling function. + + + +For historical reasons, there are two levels of notice handling, called the +notice receiver and notice processor. The default behavior is for the notice +receiver to format the notice and pass a string to the notice processor +for printing. However, an application that chooses to provide its own notice +receiver will typically ignore the notice processor layer and just do all the +work in the notice receiver. + + + +The function PQsetNoticeReceiver +notice receiver +sets or examines the current notice receiver for a connection object. +Similarly, PQsetNoticeProcessor notice processor -controls the reporting of notice and warning messages generated by the server. +sets or examines the current notice processor. + +typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); + +PQnoticeReceiver +PQsetNoticeReceiver(PGconn *conn, + PQnoticeReceiver proc, + void *arg); + typedef void (*PQnoticeProcessor) (void *arg, const char *message); PQnoticeProcessor @@ -2264,19 +2969,41 @@ PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg); + +Each of these functions returns the previous notice receiver or processor +function pointer, and sets the new value. +If you supply a null function pointer, no action is taken, +but the current pointer is returned. -By default, libpq prints notice messages -from the server, as well as a few error messages that it generates by -itself, on stderr. -This behavior can be overridden by supplying a callback function that -does something else with the messages, a so-called notice processor. -The callback function is passed -the text of the message (which includes a trailing newline), plus +When a notice or warning message is received from the server, or generated +internally by libpq, the notice receiver function +is called. It is passed the message in the form of a +PGRES_NONFATAL_ERROR PGresult. +(This allows the receiver to extract individual fields using +PQresultErrorField, or the complete preformatted message using +PQresultErrorMessage.) +The same void pointer passed to +PQsetNoticeReceiver is also passed. +(This pointer can be used to access application-specific state if needed.) + + + +The default notice receiver simply extracts the message (using +PQresultErrorMessage) and passes it to the notice processor. + + + +The notice processor is responsible for handling a notice or warning message +given in text form. It is passed the string text of the message +(including a trailing newline), plus a void pointer that is the same one passed to PQsetNoticeProcessor. (This pointer can be used to access application-specific state if needed.) + + + The default notice processor is simply static void @@ -2285,22 +3012,14 @@ defaultNoticeProcessor(void * arg, const char * message) fprintf(stderr, "%s", message); } -To use a special notice processor, call -PQsetNoticeProcessor just after -creation of a new PGconn object. -The return value is the pointer to the previous notice processor. -If you supply a null callback function pointer, no action is taken, -but the current pointer is returned. - - - -Once you have set a notice processor, you should expect that that function -could be called as long as either the PGconn object or PGresult objects -made from it exist. At creation of a PGresult, the PGconn's current -notice processor pointer is copied into the PGresult for possible use by +Once you have set a notice receiver or processor, you should expect that that +function could be called as long as either the PGconn object or +PGresult objects made from it exist. At creation of a +PGresult, the PGconn's current notice handling +pointers are copied into the PGresult for possible use by functions like PQgetvalue. @@ -2449,7 +3168,10 @@ It is not recommended to set the timeout to less than 2 seconds. The following environment variables can be used to specify default -behavior for every PostgreSQL session. +behavior for each PostgreSQL session. +(See also the ALTER USER and ALTER DATABASE +commands for ways to set default behavior on a per-user or per-database +basis.) @@ -2545,8 +3267,8 @@ If the permissions are less strict than this, the file will be ignored. libpq is thread-safe if the library is -compiled using the --with-threads -configure command-line option. (You might need to +compiled using configure's --with-threads +command-line option. (In addition, you might need to use other threading command-line options to compile your client code.) diff --git a/doc/src/sgml/lobj.sgml b/doc/src/sgml/lobj.sgml index 35c909931f..019c715c25 100644 --- a/doc/src/sgml/lobj.sgml +++ b/doc/src/sgml/lobj.sgml @@ -1,5 +1,5 @@ @@ -181,7 +181,8 @@ int lo_open(PGconn *conn, Oid lobjId, int mode); lo_open returns a large object descriptor for later use in lo_read, lo_write, lo_lseek, lo_tell, and - lo_close. + lo_close. The descriptor is only valid for + the duration of the current transaction. @@ -256,6 +257,11 @@ int lo_close(PGconn *conn, int fd); lo_open. On success, lo_close returns zero. On error, the return value is negative. + + + Any large object descriptors that remain open at the end of a + transaction will be closed automatically. + @@ -296,6 +302,14 @@ SELECT lo_export(image.raster, '/tmp/motd') FROM image WHERE name = 'beautiful image'; + + +These functions read and write files in the server's filesystem, using the +permissions of the database's owning user. Therefore, their use is restricted +to superusers. (In contrast, the client-side import and export functions +read and write files in the client's filesystem, using the permissions of +the client program. Their use is not restricted.) + diff --git a/src/interfaces/libpq/blibpqdll.def b/src/interfaces/libpq/blibpqdll.def index 8445a29b1b..ff85e9cdfc 100644 --- a/src/interfaces/libpq/blibpqdll.def +++ b/src/interfaces/libpq/blibpqdll.def @@ -97,6 +97,20 @@ EXPORTS _pg_utf_mblen @ 93 _PQunescapeBytea @ 94 _PQfreemem @ 95 + _PQtransactionStatus @ 96 + _PQparameterStatus @ 97 + _PQprotocolVersion @ 98 + _PQsetErrorVerbosity @ 99 + _PQsetNoticeReceiver @ 100 + _PQexecParams @ 101 + _PQsendQueryParams @ 102 + _PQputCopyData @ 103 + _PQputCopyEnd @ 104 + _PQgetCopyData @ 105 + _PQresultErrorField @ 106 + _PQftable @ 107 + _PQftablecol @ 108 + _PQfformat @ 109 ; Aliases for MS compatible names PQconnectdb = _PQconnectdb @@ -194,4 +208,17 @@ EXPORTS pg_utf_mblen = _pg_utf_mblen PQunescapeBytea = _PQunescapeBytea PQfreemem = _PQfreemem - + PQtransactionStatus = _PQtransactionStatus + PQparameterStatus = _PQparameterStatus + PQprotocolVersion = _PQprotocolVersion + PQsetErrorVerbosity = _PQsetErrorVerbosity + PQsetNoticeReceiver = _PQsetNoticeReceiver + PQexecParams = _PQexecParams + PQsendQueryParams = _PQsendQueryParams + PQputCopyData = _PQputCopyData + PQputCopyEnd = _PQputCopyEnd + PQgetCopyData = _PQgetCopyData + PQresultErrorField = _PQresultErrorField + PQftable = _PQftable + PQftablecol = _PQftablecol + PQfformat = _PQfformat diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a58caa3f3a..7008d99601 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.249 2003/06/20 04:09:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.250 2003/06/21 21:51:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -176,6 +176,7 @@ static PQconninfoOption *conninfo_parse(const char *conninfo, PQExpBuffer errorMessage); static char *conninfo_getval(PQconninfoOption *connOptions, const char *keyword); +static void defaultNoticeReceiver(void *arg, const PGresult *res); static void defaultNoticeProcessor(void *arg, const char *message); static int parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage); @@ -1804,11 +1805,14 @@ makeEmptyPGconn(void) /* Zero all pointers and booleans */ MemSet((char *) conn, 0, sizeof(PGconn)); - conn->noticeHook = defaultNoticeProcessor; + conn->noticeHooks.noticeRec = defaultNoticeReceiver; + conn->noticeHooks.noticeProc = defaultNoticeProcessor; conn->status = CONNECTION_BAD; conn->asyncStatus = PGASYNC_IDLE; + conn->xactStatus = PQTRANS_IDLE; conn->setenv_state = SETENV_STATE_IDLE; conn->client_encoding = PG_SQL_ASCII; + conn->verbosity = PQERRORS_DEFAULT; conn->notifyList = DLNewList(); conn->sock = -1; #ifdef USE_SSL @@ -1850,7 +1854,6 @@ makeEmptyPGconn(void) /* * freePGconn * - free the PGconn data structure - * */ static void freePGconn(PGconn *conn) @@ -1899,9 +1902,9 @@ freePGconn(PGconn *conn) } /* - closePGconn - - properly close a connection to the backend -*/ + * closePGconn + * - properly close a connection to the backend + */ static void closePGconn(PGconn *conn) { @@ -2662,6 +2665,41 @@ PQstatus(const PGconn *conn) return conn->status; } +PGTransactionStatusType +PQtransactionStatus(const PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return PQTRANS_UNKNOWN; + if (conn->asyncStatus != PGASYNC_IDLE) + return PQTRANS_ACTIVE; + return conn->xactStatus; +} + +const char * +PQparameterStatus(const PGconn *conn, const char *paramName) +{ + const pgParameterStatus *pstatus; + + if (!conn || !paramName) + return NULL; + for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next) + { + if (strcmp(pstatus->name, paramName) == 0) + return pstatus->value; + } + return NULL; +} + +int +PQprotocolVersion(const PGconn *conn) +{ + if (!conn) + return 0; + if (conn->status == CONNECTION_BAD) + return 0; + return PG_PROTOCOL_MAJOR(conn->pversion); +} + char * PQerrorMessage(const PGconn *conn) { @@ -2731,11 +2769,22 @@ PQsetClientEncoding(PGconn *conn, const char *encoding) return (status); } +PGVerbosity +PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity) +{ + PGVerbosity old; + + if (!conn) + return PQERRORS_DEFAULT; + old = conn->verbosity; + conn->verbosity = verbosity; + return old; +} + void PQtrace(PGconn *conn, FILE *debug_port) { - if (conn == NULL || - conn->status == CONNECTION_BAD) + if (conn == NULL) return; PQuntrace(conn); conn->Pfdebug = debug_port; @@ -2744,7 +2793,6 @@ PQtrace(PGconn *conn, FILE *debug_port) void PQuntrace(PGconn *conn) { - /* note: better allow untrace even when connection bad */ if (conn == NULL) return; if (conn->Pfdebug) @@ -2754,6 +2802,23 @@ PQuntrace(PGconn *conn) } } +PQnoticeReceiver +PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg) +{ + PQnoticeReceiver old; + + if (conn == NULL) + return NULL; + + old = conn->noticeHooks.noticeRec; + if (proc) + { + conn->noticeHooks.noticeRec = proc; + conn->noticeHooks.noticeRecArg = arg; + } + return old; +} + PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) { @@ -2762,22 +2827,35 @@ PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) if (conn == NULL) return NULL; - old = conn->noticeHook; + old = conn->noticeHooks.noticeProc; if (proc) { - conn->noticeHook = proc; - conn->noticeArg = arg; + conn->noticeHooks.noticeProc = proc; + conn->noticeHooks.noticeProcArg = arg; } return old; } /* - * The default notice/error message processor just prints the + * The default notice message receiver just gets the standard notice text + * and sends it to the notice processor. This two-level setup exists + * mostly for backwards compatibility; perhaps we should deprecate use of + * PQsetNoticeProcessor? + */ +static void +defaultNoticeReceiver(void *arg, const PGresult *res) +{ + (void) arg; /* not used */ + (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg, + PQresultErrorMessage(res)); +} + +/* + * The default notice message processor just prints the * message on stderr. Applications can override this if they * want the messages to go elsewhere (a window, for example). * Note that simply discarding notices is probably a bad idea. */ - static void defaultNoticeProcessor(void *arg, const char *message) { diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 45db359bde..4c96bbd386 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.138 2003/06/12 01:17:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.139 2003/06/21 21:51:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,7 +43,10 @@ char *const pgresStatus[] = { +static bool PQsendQueryStart(PGconn *conn); static void parseInput(PGconn *conn); +static bool PQexecStart(PGconn *conn); +static PGresult *PQexecFinish(PGconn *conn); /* ---------------- @@ -137,16 +140,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) result->cmdStatus[0] = '\0'; result->binary = 0; result->errMsg = NULL; - result->errSeverity = NULL; - result->errCode = NULL; - result->errPrimary = NULL; - result->errDetail = NULL; - result->errHint = NULL; - result->errPosition = NULL; - result->errContext = NULL; - result->errFilename = NULL; - result->errLineno = NULL; - result->errFuncname = NULL; + result->errFields = NULL; result->null_field[0] = '\0'; result->curBlock = NULL; result->curOffset = 0; @@ -155,8 +149,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) if (conn) { /* copy connection data we might need for operations on PGresult */ - result->noticeHook = conn->noticeHook; - result->noticeArg = conn->noticeArg; + result->noticeHooks = conn->noticeHooks; result->client_encoding = conn->client_encoding; /* consider copying conn's errorMessage */ @@ -177,9 +170,11 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) else { /* defaults... */ - result->noticeHook = NULL; - result->noticeArg = NULL; - result->client_encoding = 0; /* should be SQL_ASCII */ + result->noticeHooks.noticeRec = NULL; + result->noticeHooks.noticeRecArg = NULL; + result->noticeHooks.noticeProc = NULL; + result->noticeHooks.noticeProcArg = NULL; + result->client_encoding = PG_SQL_ASCII; } return result; @@ -444,6 +439,41 @@ pqPrepareAsyncResult(PGconn *conn) return res; } +/* + * pqInternalNotice - helper routine for internally-generated notices + * + * The supplied text is taken as primary message (ie., it should not include + * a trailing newline, and should not be more than one line). + */ +void +pqInternalNotice(const PGNoticeHooks *hooks, const char *msgtext) +{ + PGresult *res; + + if (hooks->noticeRec == NULL) + return; /* nobody home? */ + + /* Make a PGresult to pass to the notice receiver */ + res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR); + res->noticeHooks = *hooks; + /* + * Set up fields of notice. + */ + pqSaveMessageField(res, 'M', msgtext); + pqSaveMessageField(res, 'S', libpq_gettext("NOTICE")); + /* XXX should provide a SQLSTATE too? */ + /* + * Result text is always just the primary message + newline. + */ + res->errMsg = (char *) pqResultAlloc(res, strlen(msgtext) + 2, FALSE); + sprintf(res->errMsg, "%s\n", msgtext); + /* + * Pass to receiver, then free it. + */ + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + PQclear(res); +} + /* * pqAddTuple * add a row pointer to the PGresult structure, growing it if necessary @@ -484,6 +514,25 @@ pqAddTuple(PGresult *res, PGresAttValue *tup) return TRUE; } +/* + * pqSaveMessageField - save one field of an error or notice message + */ +void +pqSaveMessageField(PGresult *res, char code, const char *value) +{ + PGMessageField *pfield; + + pfield = (PGMessageField *) + pqResultAlloc(res, + sizeof(PGMessageField) + strlen(value), + TRUE); + if (!pfield) + return; /* out of memory? */ + pfield->code = code; + strcpy(pfield->contents, value); + pfield->next = res->errFields; + res->errFields = pfield; +} /* * pqSaveParameterStatus - remember parameter status sent by backend @@ -543,26 +592,6 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value) StrNCpy(conn->sversion, value, sizeof(conn->sversion)); } -/* - * pqGetParameterStatus - fetch parameter value, if available - * - * Returns NULL if info not available - * - * XXX this probably should be exported for client use - */ -const char * -pqGetParameterStatus(PGconn *conn, const char *name) -{ - pgParameterStatus *pstatus; - - for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next) - { - if (strcmp(pstatus->name, name) == 0) - return pstatus->value; - } - return NULL; -} - /* * PQsendQuery @@ -574,12 +603,9 @@ pqGetParameterStatus(PGconn *conn, const char *name) int PQsendQuery(PGconn *conn, const char *query) { - if (!conn) + if (!PQsendQueryStart(conn)) return 0; - /* clear the error string */ - resetPQExpBuffer(&conn->errorMessage); - if (!query) { printfPQExpBuffer(&conn->errorMessage, @@ -587,25 +613,6 @@ PQsendQuery(PGconn *conn, const char *query) return 0; } - /* Don't try to send if we know there's no live connection. */ - if (conn->status != CONNECTION_OK) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no connection to the server\n")); - return 0; - } - /* Can't send while already busy, either. */ - if (conn->asyncStatus != PGASYNC_IDLE) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("another command is already in progress\n")); - return 0; - } - - /* initialize async result-accumulation state */ - conn->result = NULL; - conn->curTuple = NULL; - /* construct the outgoing Query message */ if (pqPutMsgStart('Q', false, conn) < 0 || pqPuts(query, conn) < 0 || @@ -617,7 +624,7 @@ PQsendQuery(PGconn *conn, const char *query) /* * Give the data a push. In nonblock mode, don't complain if we're - * unable to send it all; PQconsumeInput() will do any additional flushing + * unable to send it all; PQgetResult() will do any additional flushing * needed. */ if (pqFlush(conn) < 0) @@ -631,6 +638,194 @@ PQsendQuery(PGconn *conn, const char *query) return 1; } +/* + * PQsendQueryParams + * Like PQsendQuery, but use 3.0 protocol so we can pass parameters + */ +int +PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + int i; + + if (!PQsendQueryStart(conn)) + return 0; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least 3.0 protocol\n")); + return 0; + } + + if (!command) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + + /* + * We will send Parse, Bind, Describe Portal, Execute, Sync, using + * unnamed statement and portal. + */ + + /* construct the Parse message */ + if (pqPutMsgStart('P', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPuts(command, conn) < 0) + goto sendFailed; + if (nParams > 0 && paramTypes) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramTypes[i], 4, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Bind message */ + if (pqPutMsgStart('B', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPuts("", conn) < 0) + goto sendFailed; + if (nParams > 0 && paramFormats) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramFormats[i], 2, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (paramValues && paramValues[i]) + { + int nbytes; + + if (paramFormats && paramFormats[i] != 0) + { + /* binary parameter */ + nbytes = paramLengths[i]; + } + else + { + /* text parameter, do not use paramLengths */ + nbytes = strlen(paramValues[i]); + } + if (pqPutInt(nbytes, 4, conn) < 0 || + pqPutnchar(paramValues[i], nbytes, conn) < 0) + goto sendFailed; + } + else + { + /* take the param as NULL */ + if (pqPutInt(-1, 4, conn) < 0) + goto sendFailed; + } + } + if (pqPutInt(1, 2, conn) < 0 || + pqPutInt(resultFormat, 2, conn)) + goto sendFailed; + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Describe Portal message */ + if (pqPutMsgStart('D', false, conn) < 0 || + pqPutc('P', conn) < 0 || + pqPuts("", conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Execute message */ + if (pqPutMsgStart('E', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPutInt(0, 4, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* + * Give the data a push. In nonblock mode, don't complain if we're + * unable to send it all; PQgetResult() will do any additional flushing + * needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * Common startup code for PQsendQuery and PQsendQueryParams + */ +static bool +PQsendQueryStart(PGconn *conn) +{ + if (!conn) + return false; + + /* clear the error string */ + resetPQExpBuffer(&conn->errorMessage); + + /* Don't try to send if we know there's no live connection. */ + if (conn->status != CONNECTION_OK) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no connection to the server\n")); + return false; + } + /* Can't send while already busy, either. */ + if (conn->asyncStatus != PGASYNC_IDLE) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("another command is already in progress\n")); + return false; + } + + /* initialize async result-accumulation state */ + conn->result = NULL; + conn->curTuple = NULL; + + /* ready to send command message */ + return true; +} + /* * pqHandleSendFailure: try to clean up after failure to send command. * @@ -746,8 +941,24 @@ PQgetResult(PGconn *conn) /* If not ready to return something, block until we are. */ while (conn->asyncStatus == PGASYNC_BUSY) { + int flushResult; + + /* + * If data remains unsent, send it. Else we might be waiting + * for the result of a command the backend hasn't even got yet. + */ + while ((flushResult = pqFlush(conn)) > 0) + { + if (pqWait(FALSE, TRUE, conn)) + { + flushResult = -1; + break; + } + } + /* Wait for some more data, and load it. */ - if (pqWait(TRUE, FALSE, conn) || + if (flushResult || + pqWait(TRUE, FALSE, conn) || pqReadData(conn) < 0) { /* @@ -758,6 +969,7 @@ PQgetResult(PGconn *conn) conn->asyncStatus = PGASYNC_IDLE; return pqPrepareAsyncResult(conn); } + /* Parse it. */ parseInput(conn); } @@ -774,10 +986,16 @@ PQgetResult(PGconn *conn) conn->asyncStatus = PGASYNC_BUSY; break; case PGASYNC_COPY_IN: - res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN); + if (conn->result && conn->result->resultStatus == PGRES_COPY_IN) + res = pqPrepareAsyncResult(conn); + else + res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN); break; case PGASYNC_COPY_OUT: - res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT); + if (conn->result && conn->result->resultStatus == PGRES_COPY_OUT) + res = pqPrepareAsyncResult(conn); + else + res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT); break; default: printfPQExpBuffer(&conn->errorMessage, @@ -806,18 +1024,46 @@ PQgetResult(PGconn *conn) PGresult * PQexec(PGconn *conn, const char *query) { - PGresult *result; - PGresult *lastResult; - bool savedblocking; - - /* - * we assume anyone calling PQexec wants blocking behaviour, we force - * the blocking status of the connection to blocking for the duration - * of this function and restore it on return - */ - savedblocking = pqIsnonblocking(conn); - if (PQsetnonblocking(conn, FALSE) == -1) + if (!PQexecStart(conn)) return NULL; + if (!PQsendQuery(conn, query)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQexecParams + * Like PQexec, but use 3.0 protocol so we can pass parameters + */ +PGresult * +PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char * const *paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQueryParams(conn, command, + nParams, paramTypes, paramValues, paramLengths, + paramFormats, resultFormat)) + return NULL; + return PQexecFinish(conn); +} + +/* + * Common code for PQexec and PQexecParams: prepare to send command + */ +static bool +PQexecStart(PGconn *conn) +{ + PGresult *result; + + if (!conn) + return false; /* * Silently discard any prior query result that application didn't @@ -832,15 +1078,23 @@ PQexec(PGconn *conn, const char *query) PQclear(result); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("COPY state must be terminated first\n")); - /* restore blocking status */ - goto errout; + return false; } PQclear(result); } - /* OK to send the message */ - if (!PQsendQuery(conn, query)) - goto errout; /* restore blocking status */ + /* OK to send a command */ + return true; +} + +/* + * Common code for PQexec and PQexecParams: wait for command result + */ +static PGresult * +PQexecFinish(PGconn *conn) +{ + PGresult *result; + PGresult *lastResult; /* * For backwards compatibility, return the last result if there are @@ -848,7 +1102,7 @@ PQexec(PGconn *conn, const char *query) * error result. * * We have to stop if we see copy in/out, however. We will resume parsing - * when application calls PQendcopy. + * after application performs the data transfer. */ lastResult = NULL; while ((result = PQgetResult(conn)) != NULL) @@ -874,14 +1128,7 @@ PQexec(PGconn *conn, const char *query) break; } - if (PQsetnonblocking(conn, savedblocking) == -1) - return NULL; return lastResult; - -errout: - if (PQsetnonblocking(conn, savedblocking) == -1) - return NULL; - return NULL; } /* @@ -894,7 +1141,6 @@ errout: * * the CALLER is responsible for FREE'ing the structure returned */ - PGnotify * PQnotifies(PGconn *conn) { @@ -916,6 +1162,156 @@ PQnotifies(PGconn *conn) return event; } +/* + * PQputCopyData - send some data to the backend during COPY IN + * + * Returns 1 if successful, 0 if data could not be sent (only possible + * in nonblock mode), or -1 if an error occurs. + */ +int +PQputCopyData(PGconn *conn, const char *buffer, int nbytes) +{ + if (!conn) + return -1; + if (conn->asyncStatus != PGASYNC_COPY_IN) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -1; + } + if (nbytes > 0) + { + /* + * Try to flush any previously sent data in preference to growing + * the output buffer. If we can't enlarge the buffer enough to hold + * the data, return 0 in the nonblock case, else hard error. + * (For simplicity, always assume 5 bytes of overhead even in + * protocol 2.0 case.) + */ + if ((conn->outBufSize - conn->outCount - 5) < nbytes) + { + if (pqFlush(conn) < 0) + return -1; + if (pqCheckOutBufferSpace(conn->outCount + 5 + nbytes, conn)) + return pqIsnonblocking(conn) ? 0 : -1; + } + /* Send the data (too simple to delegate to fe-protocol files) */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (pqPutMsgStart('d', false, conn) < 0 || + pqPutnchar(buffer, nbytes, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + else + { + if (pqPutMsgStart(0, false, conn) < 0 || + pqPutnchar(buffer, nbytes, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + return 1; +} + +/* + * PQputCopyEnd - send EOF indication to the backend during COPY IN + * + * After calling this, use PQgetResult() to check command completion status. + * + * Returns 1 if successful, 0 if data could not be sent (only possible + * in nonblock mode), or -1 if an error occurs. + */ +int +PQputCopyEnd(PGconn *conn, const char *errormsg) +{ + if (!conn) + return -1; + if (conn->asyncStatus != PGASYNC_COPY_IN) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -1; + } + /* + * Send the COPY END indicator. This is simple enough that we don't + * bother delegating it to the fe-protocol files. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (errormsg) + { + /* Send COPY FAIL */ + if (pqPutMsgStart('f', false, conn) < 0 || + pqPuts(errormsg, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + else + { + /* Send COPY DONE */ + if (pqPutMsgStart('c', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + else + { + if (errormsg) + { + /* Ooops, no way to do this in 2.0 */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least 3.0 protocol\n")); + return -1; + } + else + { + /* Send old-style end-of-data marker */ + if (pqPutMsgStart(0, false, conn) < 0 || + pqPuts("\\.\n", conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + + /* Return to active duty */ + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* Try to flush data */ + if (pqFlush(conn) < 0) + return -1; + + return 1; +} + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +PQgetCopyData(PGconn *conn, char **buffer, int async) +{ + *buffer = NULL; /* for all failure cases */ + if (!conn) + return -2; + if (conn->asyncStatus != PGASYNC_COPY_OUT) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -2; + } + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetCopyData3(conn, buffer, async); + else + return pqGetCopyData2(conn, buffer, async); +} + /* * PQgetline - gets a newline-terminated string from the backend. * @@ -1002,11 +1398,12 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize) } /* - * PQputline -- sends a string to the backend. + * PQputline -- sends a string to the backend during COPY IN. * Returns 0 if OK, EOF if not. * - * This exists to support "COPY from stdin". The backend will ignore - * the string if not doing COPY. + * This is deprecated primarily because the return convention doesn't allow + * caller to tell the difference between a hard error and a nonblock-mode + * send failure. */ int PQputline(PGconn *conn, const char *s) @@ -1021,27 +1418,10 @@ PQputline(PGconn *conn, const char *s) int PQputnbytes(PGconn *conn, const char *buffer, int nbytes) { - if (!conn || conn->sock < 0) + if (PQputCopyData(conn, buffer, nbytes) > 0) + return 0; + else return EOF; - if (nbytes > 0) - { - /* This is too simple to bother with separate subroutines */ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - if (pqPutMsgStart('d', false, conn) < 0 || - pqPutnchar(buffer, nbytes, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return EOF; - } - else - { - if (pqPutMsgStart(0, false, conn) < 0 || - pqPutnchar(buffer, nbytes, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return EOF; - } - } - return 0; } /* @@ -1049,6 +1429,11 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes) * After completing the data transfer portion of a copy in/out, * the application must call this routine to finish the command protocol. * + * When using 3.0 protocol this is deprecated; it's cleaner to use PQgetResult + * to get the transfer status. Note however that when using 2.0 protocol, + * recovering from a copy failure often requires a PQreset. PQendcopy will + * take care of that, PQgetResult won't. + * * RETURNS: * 0 on success * 1 on failure @@ -1133,7 +1518,7 @@ ExecStatusType PQresultStatus(const PGresult *res) { if (!res) - return PGRES_NONFATAL_ERROR; + return PGRES_FATAL_ERROR; return res->resultStatus; } @@ -1153,6 +1538,21 @@ PQresultErrorMessage(const PGresult *res) return res->errMsg; } +char * +PQresultErrorField(const PGresult *res, int fieldcode) +{ + PGMessageField *pfield; + + if (!res) + return NULL; + for (pfield = res->errFields; pfield != NULL; pfield = pfield->next) + { + if (pfield->code == fieldcode) + return pfield->contents; + } + return NULL; +} + int PQntuples(const PGresult *res) { @@ -1191,13 +1591,10 @@ check_field_number(const PGresult *res, int field_num) return FALSE; /* no way to display error message... */ if (field_num < 0 || field_num >= res->numAttributes) { - if (res->noticeHook) - { - snprintf(noticeBuf, sizeof(noticeBuf), - libpq_gettext("column number %d is out of range 0..%d\n"), - field_num, res->numAttributes - 1); - PGDONOTICE(res, noticeBuf); - } + snprintf(noticeBuf, sizeof(noticeBuf), + libpq_gettext("column number %d is out of range 0..%d"), + field_num, res->numAttributes - 1); + PGDONOTICE(res, noticeBuf); return FALSE; } return TRUE; @@ -1213,32 +1610,26 @@ check_tuple_field_number(const PGresult *res, return FALSE; /* no way to display error message... */ if (tup_num < 0 || tup_num >= res->ntups) { - if (res->noticeHook) - { - snprintf(noticeBuf, sizeof(noticeBuf), - libpq_gettext("row number %d is out of range 0..%d\n"), - tup_num, res->ntups - 1); - PGDONOTICE(res, noticeBuf); - } + snprintf(noticeBuf, sizeof(noticeBuf), + libpq_gettext("row number %d is out of range 0..%d"), + tup_num, res->ntups - 1); + PGDONOTICE(res, noticeBuf); return FALSE; } if (field_num < 0 || field_num >= res->numAttributes) { - if (res->noticeHook) - { - snprintf(noticeBuf, sizeof(noticeBuf), - libpq_gettext("column number %d is out of range 0..%d\n"), - field_num, res->numAttributes - 1); - PGDONOTICE(res, noticeBuf); - } + snprintf(noticeBuf, sizeof(noticeBuf), + libpq_gettext("column number %d is out of range 0..%d"), + field_num, res->numAttributes - 1); + PGDONOTICE(res, noticeBuf); return FALSE; } return TRUE; } /* - returns NULL if the field_num is invalid -*/ + * returns NULL if the field_num is invalid + */ char * PQfname(const PGresult *res, int field_num) { @@ -1251,8 +1642,8 @@ PQfname(const PGresult *res, int field_num) } /* - returns -1 on a bad field name -*/ + * returns -1 on a bad field name + */ int PQfnumber(const PGresult *res, const char *field_name) { @@ -1290,6 +1681,39 @@ PQfnumber(const PGresult *res, const char *field_name) return -1; } +Oid +PQftable(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return InvalidOid; + if (res->attDescs) + return res->attDescs[field_num].tableid; + else + return InvalidOid; +} + +int +PQftablecol(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].columnid; + else + return 0; +} + +int +PQfformat(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].format; + else + return 0; +} + Oid PQftype(const PGresult *res, int field_num) { @@ -1332,10 +1756,10 @@ PQcmdStatus(PGresult *res) } /* - PQoidStatus - - if the last command was an INSERT, return the oid string - if not, return "" -*/ + * PQoidStatus - + * if the last command was an INSERT, return the oid string + * if not, return "" + */ char * PQoidStatus(const PGresult *res) { @@ -1360,10 +1784,10 @@ PQoidStatus(const PGresult *res) } /* - PQoidValue - - a perhaps preferable form of the above which just returns - an Oid type -*/ + * PQoidValue - + * a perhaps preferable form of the above which just returns + * an Oid type + */ Oid PQoidValue(const PGresult *res) { @@ -1388,13 +1812,13 @@ PQoidValue(const PGresult *res) /* - PQcmdTuples - - If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a - string containing the number of inserted/affected tuples. If not, - return "". - - XXX: this should probably return an int -*/ + * PQcmdTuples - + * If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a + * string containing the number of inserted/affected tuples. If not, + * return "". + * + * XXX: this should probably return an int + */ char * PQcmdTuples(PGresult *res) { @@ -1426,13 +1850,10 @@ PQcmdTuples(PGresult *res) if (*p == 0) { - if (res->noticeHook) - { - snprintf(noticeBuf, sizeof(noticeBuf), - libpq_gettext("could not interpret result from server: %s\n"), - res->cmdStatus); - PGDONOTICE(res, noticeBuf); - } + snprintf(noticeBuf, sizeof(noticeBuf), + libpq_gettext("could not interpret result from server: %s"), + res->cmdStatus); + PGDONOTICE(res, noticeBuf); return ""; } @@ -1440,15 +1861,9 @@ PQcmdTuples(PGresult *res) } /* - PQgetvalue: - return the value of field 'field_num' of row 'tup_num' - - If res is binary, then the value returned is NOT a null-terminated - ASCII string, but the binary representation in the server's native - format. - - if res is not binary, a null-terminated ASCII string is returned. -*/ + * PQgetvalue: + * return the value of field 'field_num' of row 'tup_num' + */ char * PQgetvalue(const PGresult *res, int tup_num, int field_num) { @@ -1458,11 +1873,8 @@ PQgetvalue(const PGresult *res, int tup_num, int field_num) } /* PQgetlength: - returns the length of a field value in bytes. If res is binary, - i.e. a result of a binary portal, then the length returned does - NOT include the size field of the varlena. (The data returned - by PQgetvalue doesn't either.) -*/ + * returns the actual length of a field value in bytes. + */ int PQgetlength(const PGresult *res, int tup_num, int field_num) { @@ -1475,8 +1887,8 @@ PQgetlength(const PGresult *res, int tup_num, int field_num) } /* PQgetisnull: - returns the null status of a field value. -*/ + * returns the null status of a field value. + */ int PQgetisnull(const PGresult *res, int tup_num, int field_num) { @@ -1489,16 +1901,17 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num) } /* PQsetnonblocking: - sets the PGconn's database connection non-blocking if the arg is TRUE - or makes it non-blocking if the arg is FALSE, this will not protect - you from PQexec(), you'll only be safe when using the non-blocking - API - Needs to be called only on a connected database connection. -*/ - + * sets the PGconn's database connection non-blocking if the arg is TRUE + * or makes it non-blocking if the arg is FALSE, this will not protect + * you from PQexec(), you'll only be safe when using the non-blocking API. + * Needs to be called only on a connected database connection. + */ int PQsetnonblocking(PGconn *conn, int arg) { + if (!conn || conn->status == CONNECTION_BAD) + return -1; + arg = (arg == TRUE) ? 1 : 0; /* early out if the socket is already in the state requested */ if (arg == conn->nonblocking) @@ -1520,9 +1933,10 @@ PQsetnonblocking(PGconn *conn, int arg) return (0); } -/* return the blocking status of the database connection, TRUE == nonblocking, - FALSE == blocking -*/ +/* + * return the blocking status of the database connection + * TRUE == nonblocking, FALSE == blocking + */ int PQisnonblocking(const PGconn *conn) { diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 3856145a4e..f10c3112d7 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -23,7 +23,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.96 2003/06/14 17:49:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.97 2003/06/21 21:51:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -197,7 +197,7 @@ pqPutnchar(const char *s, size_t len, PGconn *conn) } /* - * pgGetInt + * pqGetInt * read a 2 or 4 byte integer and convert from network byte order * to local byte order */ @@ -226,7 +226,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn) break; default: snprintf(noticeBuf, sizeof(noticeBuf), - libpq_gettext("integer of size %lu not supported by pqGetInt\n"), + libpq_gettext("integer of size %lu not supported by pqGetInt"), (unsigned long) bytes); PGDONOTICE(conn, noticeBuf); return EOF; @@ -239,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn) } /* - * pgPutInt + * pqPutInt * write an integer of 2 or 4 bytes, converting from host byte order * to network byte order. */ @@ -264,7 +264,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn) break; default: snprintf(noticeBuf, sizeof(noticeBuf), - libpq_gettext("integer of size %lu not supported by pqPutInt\n"), + libpq_gettext("integer of size %lu not supported by pqPutInt"), (unsigned long) bytes); PGDONOTICE(conn, noticeBuf); return EOF; @@ -282,7 +282,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn) * * Returns 0 on success, EOF if failed to enlarge buffer */ -static int +int pqCheckOutBufferSpace(int bytes_needed, PGconn *conn) { int newsize = conn->outBufSize; @@ -748,7 +748,7 @@ pqSendSome(PGconn *conn, int len) if (sent < 0) { /* - * Anything except EAGAIN or EWOULDBLOCK is trouble. If it's + * Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble. If it's * EPIPE or ECONNRESET, assume we've lost the backend * connection permanently. */ @@ -804,25 +804,17 @@ pqSendSome(PGconn *conn, int len) if (len > 0) { - /* We didn't send it all, wait till we can send more */ - /* - * if the socket is in non-blocking mode we may need to abort - * here and return 1 to indicate that data is still pending. + * We didn't send it all, wait till we can send more. + * + * If the connection is in non-blocking mode we don't wait, + * but return 1 to indicate that data is still pending. */ -#ifdef USE_SSL - /* can't do anything for our SSL users yet */ - if (conn->ssl == NULL) + if (pqIsnonblocking(conn)) { -#endif - if (pqIsnonblocking(conn)) - { - result = 1; - break; - } -#ifdef USE_SSL + result = 1; + break; } -#endif if (pqWait(FALSE, TRUE, conn)) { diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c index 2a7b6b43bf..6b90933407 100644 --- a/src/interfaces/libpq/fe-protocol2.c +++ b/src/interfaces/libpq/fe-protocol2.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.1 2003/06/08 17:43:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.2 2003/06/21 21:51:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ static int getRowDescriptions(PGconn *conn); static int getAnotherTuple(PGconn *conn, bool binary); static int pqGetErrorNotice2(PGconn *conn, bool isError); +static void checkXactStatus(PGconn *conn, const char *cmdTag); static int getNotify(PGconn *conn); @@ -312,7 +313,7 @@ pqSetenvPoll(PGconn *conn) val); else { - val = pqGetParameterStatus(conn, "server_encoding"); + val = PQparameterStatus(conn, "server_encoding"); if (val && *val) pqSaveParameterStatus(conn, "client_encoding", val); @@ -424,7 +425,7 @@ pqParseInput2(PGconn *conn) else { snprintf(noticeWorkspace, sizeof(noticeWorkspace), - libpq_gettext("message type 0x%02x arrived from server while idle\n"), + libpq_gettext("message type 0x%02x arrived from server while idle"), id); PGDONOTICE(conn, noticeWorkspace); /* Discard the unexpected message; good idea?? */ @@ -447,6 +448,7 @@ pqParseInput2(PGconn *conn) PGRES_COMMAND_OK); strncpy(conn->result->cmdStatus, conn->workBuffer.data, CMDSTATUS_LEN); + checkXactStatus(conn, conn->workBuffer.data); conn->asyncStatus = PGASYNC_READY; break; case 'E': /* error return */ @@ -464,7 +466,7 @@ pqParseInput2(PGconn *conn) if (id != '\0') { snprintf(noticeWorkspace, sizeof(noticeWorkspace), - libpq_gettext("unexpected character %c following empty query response (\"I\" message)\n"), + libpq_gettext("unexpected character %c following empty query response (\"I\" message)"), id); PGDONOTICE(conn, noticeWorkspace); } @@ -521,7 +523,7 @@ pqParseInput2(PGconn *conn) else { snprintf(noticeWorkspace, sizeof(noticeWorkspace), - libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n")); + libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)")); PGDONOTICE(conn, noticeWorkspace); /* Discard the unexpected message; good idea?? */ conn->inStart = conn->inEnd; @@ -538,7 +540,7 @@ pqParseInput2(PGconn *conn) else { snprintf(noticeWorkspace, sizeof(noticeWorkspace), - libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n")); + libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)")); PGDONOTICE(conn, noticeWorkspace); /* Discard the unexpected message; good idea?? */ conn->inStart = conn->inEnd; @@ -628,6 +630,9 @@ getRowDescriptions(PGconn *conn) result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); + result->attDescs[i].tableid = 0; + result->attDescs[i].columnid = 0; + result->attDescs[i].format = 0; result->attDescs[i].typid = typid; result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; @@ -674,6 +679,15 @@ getAnotherTuple(PGconn *conn, bool binary) if (conn->curTuple == NULL) goto outOfMemory; MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue)); + /* + * If it's binary, fix the column format indicators. We assume + * the backend will consistently send either B or D, not a mix. + */ + if (binary) + { + for (i = 0; i < nfields; i++) + result->attDescs[i].format = 1; + } } tup = conn->curTuple; @@ -778,6 +792,8 @@ pqGetErrorNotice2(PGconn *conn, bool isError) { PGresult *res; PQExpBufferData workBuf; + char *startp; + char *splitp; /* * Since the message might be pretty long, we create a temporary @@ -800,19 +816,63 @@ pqGetErrorNotice2(PGconn *conn, bool isError) res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; res->errMsg = pqResultStrdup(res, workBuf.data); + /* + * Break the message into fields. We can't do very much here, but we + * can split the severity code off, and remove trailing newlines. Also, + * we use the heuristic that the primary message extends only to the + * first newline --- anything after that is detail message. (In some + * cases it'd be better classed as hint, but we can hardly be expected + * to guess that here.) + */ + while (workBuf.len > 0 && workBuf.data[workBuf.len-1] == '\n') + workBuf.data[--workBuf.len] = '\0'; + splitp = strstr(workBuf.data, ": "); + if (splitp) + { + /* what comes before the colon is severity */ + *splitp = '\0'; + pqSaveMessageField(res, 'S', workBuf.data); + startp = splitp + 3; + } + else + { + /* can't find a colon? oh well... */ + startp = workBuf.data; + } + splitp = strchr(startp, '\n'); + if (splitp) + { + /* what comes before the newline is primary message */ + *splitp++ = '\0'; + pqSaveMessageField(res, 'M', startp); + /* the rest is detail; strip any leading whitespace */ + while (*splitp && isspace((unsigned char) *splitp)) + splitp++; + pqSaveMessageField(res, 'D', splitp); + } + else + { + /* single-line message, so all primary */ + pqSaveMessageField(res, 'M', startp); + } + /* * Either save error as current async result, or just emit the notice. + * Also, if it's an error and we were in a transaction block, assume + * the server has now gone to error-in-transaction state. */ if (isError) { pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); - appendPQExpBufferStr(&conn->errorMessage, workBuf.data); + appendPQExpBufferStr(&conn->errorMessage, res->errMsg); + if (conn->xactStatus == PQTRANS_INTRANS) + conn->xactStatus = PQTRANS_INERROR; } else { - PGDONOTICE(conn, workBuf.data); + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); PQclear(res); } @@ -820,6 +880,37 @@ pqGetErrorNotice2(PGconn *conn, bool isError) return 0; } +/* + * checkXactStatus - attempt to track transaction-block status of server + * + * This is called each time we receive a command-complete message. By + * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do + * a passable job of tracking the server's xact status. BUT: this does + * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that + * feature ever a mistake.) Caveat user. + * + * The tags known here are all those used as far back as 7.0; is it worth + * adding those from even-older servers? + */ +static void +checkXactStatus(PGconn *conn, const char *cmdTag) +{ + if (strcmp(cmdTag, "BEGIN") == 0) + conn->xactStatus = PQTRANS_INTRANS; + else if (strcmp(cmdTag, "COMMIT") == 0) + conn->xactStatus = PQTRANS_IDLE; + else if (strcmp(cmdTag, "ROLLBACK") == 0) + conn->xactStatus = PQTRANS_IDLE; + else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */ + conn->xactStatus = PQTRANS_INTRANS; + /* + * Normally we get into INERROR state by detecting an Error message. + * However, if we see one of these tags then we know for sure the + * server is in abort state ... + */ + else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */ + conn->xactStatus = PQTRANS_INERROR; +} /* * Attempt to read a Notify response message. @@ -832,6 +923,7 @@ static int getNotify(PGconn *conn) { int be_pid; + int nmlen; PGnotify *newNotify; if (pqGetInt(&be_pid, 4, conn)) @@ -844,12 +936,14 @@ getNotify(PGconn *conn) * can all be freed at once. We don't use NAMEDATALEN because we * don't want to tie this interface to a specific server name length. */ - newNotify = (PGnotify *) malloc(sizeof(PGnotify) + - strlen(conn->workBuffer.data) +1); + nmlen = strlen(conn->workBuffer.data); + newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1); if (newNotify) { newNotify->relname = (char *) newNotify + sizeof(PGnotify); strcpy(newNotify->relname, conn->workBuffer.data); + /* fake up an empty-string extra field */ + newNotify->extra = newNotify->relname + nmlen; newNotify->be_pid = be_pid; DLAddTail(conn->notifyList, DLNewElem(newNotify)); } @@ -858,6 +952,84 @@ getNotify(PGconn *conn) } +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +pqGetCopyData2(PGconn *conn, char **buffer, int async) +{ + bool found; + int msgLength; + + for (;;) + { + /* + * Do we have a complete line of data? + */ + conn->inCursor = conn->inStart; + found = false; + while (conn->inCursor < conn->inEnd) + { + char c = conn->inBuffer[conn->inCursor++]; + + if (c == '\n') + { + found = true; + break; + } + } + if (!found) + goto nodata; + msgLength = conn->inCursor - conn->inStart; + + /* + * If it's the end-of-data marker, consume it, exit COPY_OUT mode, + * and let caller read status with PQgetResult(). + */ + if (msgLength == 3 && + strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0) + { + conn->inStart = conn->inCursor; + conn->asyncStatus = PGASYNC_BUSY; + return -1; + } + + /* + * Pass the line back to the caller. + */ + *buffer = (char *) malloc(msgLength + 1); + if (*buffer == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -2; + } + memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength); + (*buffer)[msgLength] = '\0'; /* Add terminating null */ + + /* Mark message consumed */ + conn->inStart = conn->inCursor; + + return msgLength; + + nodata: + /* Don't block if async read requested */ + if (async) + return 0; + /* Need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + return -2; + } +} + + /* * PQgetline - gets a newline-terminated string from the backend. * @@ -1020,7 +1192,7 @@ pqEndcopy2(PGconn *conn) if (conn->errorMessage.len > 0) PGDONOTICE(conn, conn->errorMessage.data); - PGDONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection\n")); + PGDONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection")); /* * Users doing non-blocking connections need to handle the reset diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 2fbfa01566..05543f8e76 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.1 2003/06/08 17:43:00 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.2 2003/06/21 21:51:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,8 @@ static int getRowDescriptions(PGconn *conn); static int getAnotherTuple(PGconn *conn, int msgLength); static int getParameterStatus(PGconn *conn); static int getNotify(PGconn *conn); +static int getCopyStart(PGconn *conn, ExecStatusType copytype); +static int getReadyForQuery(PGconn *conn); static int build_startup_packet(const PGconn *conn, char *packet, const PQEnvironmentOption *options); @@ -171,7 +173,7 @@ pqParseInput3(PGconn *conn) else { snprintf(noticeWorkspace, sizeof(noticeWorkspace), - libpq_gettext("message type 0x%02x arrived from server while idle\n"), + libpq_gettext("message type 0x%02x arrived from server while idle"), id); PGDONOTICE(conn, noticeWorkspace); /* Discard the unexpected message */ @@ -201,7 +203,7 @@ pqParseInput3(PGconn *conn) conn->asyncStatus = PGASYNC_READY; break; case 'Z': /* backend is ready for new query */ - if (pqGetc(&conn->xact_status, conn)) + if (getReadyForQuery(conn)) return; conn->asyncStatus = PGASYNC_IDLE; break; @@ -211,6 +213,11 @@ pqParseInput3(PGconn *conn) PGRES_EMPTY_QUERY); conn->asyncStatus = PGASYNC_READY; break; + case '1': /* Parse Complete */ + case '2': /* Bind Complete */ + case '3': /* Close Complete */ + /* Nothing to do for these message types */ + break; case 'S': /* parameter status */ if (getParameterStatus(conn)) return; @@ -276,17 +283,13 @@ pqParseInput3(PGconn *conn) } break; case 'G': /* Start Copy In */ - if (pqGetc(&conn->copy_is_binary, conn)) + if (getCopyStart(conn, PGRES_COPY_IN)) return; - /* XXX we currently ignore the rest of the message */ - conn->inCursor = conn->inStart + 5 + msgLength; conn->asyncStatus = PGASYNC_COPY_IN; break; case 'H': /* Start Copy Out */ - if (pqGetc(&conn->copy_is_binary, conn)) + if (getCopyStart(conn, PGRES_COPY_OUT)) return; - /* XXX we currently ignore the rest of the message */ - conn->inCursor = conn->inStart + 5 + msgLength; conn->asyncStatus = PGASYNC_COPY_OUT; conn->copy_already_done = 0; break; @@ -398,6 +401,9 @@ getRowDescriptions(PGconn *conn) MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } + /* result->binary is true only if ALL columns are binary */ + result->binary = (nfields > 0) ? 1 : 0; + /* get type info */ for (i = 0; i < nfields; i++) { @@ -430,10 +436,15 @@ getRowDescriptions(PGconn *conn) result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); + result->attDescs[i].tableid = tableid; + result->attDescs[i].columnid = columnid; + result->attDescs[i].format = format; result->attDescs[i].typid = typid; result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; - /* XXX todo: save tableid/columnid, format too */ + + if (format != 1) + result->binary = 0; } /* Success! */ @@ -503,7 +514,9 @@ getAnotherTuple(PGconn *conn, int msgLength) vlen = 0; if (tup[i].value == NULL) { - tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false); + bool isbinary = (result->attDescs[i].format != 0); + + tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary); if (tup[i].value == NULL) goto outOfMemory; } @@ -553,6 +566,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError) PGresult *res; PQExpBufferData workBuf; char id; + const char *val; /* * Make a PGresult to hold the accumulated fields. We temporarily @@ -580,68 +594,63 @@ pqGetErrorNotice3(PGconn *conn, bool isError) break; /* terminator found */ if (pqGets(&workBuf, conn)) goto fail; - switch (id) - { - case 'S': - res->errSeverity = pqResultStrdup(res, workBuf.data); - break; - case 'C': - res->errCode = pqResultStrdup(res, workBuf.data); - break; - case 'M': - res->errPrimary = pqResultStrdup(res, workBuf.data); - break; - case 'D': - res->errDetail = pqResultStrdup(res, workBuf.data); - break; - case 'H': - res->errHint = pqResultStrdup(res, workBuf.data); - break; - case 'P': - res->errPosition = pqResultStrdup(res, workBuf.data); - break; - case 'W': - res->errContext = pqResultStrdup(res, workBuf.data); - break; - case 'F': - res->errFilename = pqResultStrdup(res, workBuf.data); - break; - case 'L': - res->errLineno = pqResultStrdup(res, workBuf.data); - break; - case 'R': - res->errFuncname = pqResultStrdup(res, workBuf.data); - break; - default: - /* silently ignore any other field type */ - break; - } + pqSaveMessageField(res, id, workBuf.data); } /* * Now build the "overall" error message for PQresultErrorMessage. - * - * XXX this should be configurable somehow. */ resetPQExpBuffer(&workBuf); - if (res->errSeverity) - appendPQExpBuffer(&workBuf, "%s: ", res->errSeverity); - if (res->errPrimary) - appendPQExpBufferStr(&workBuf, res->errPrimary); - /* translator: %s represents a digit string */ - if (res->errPosition) - appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), - res->errPosition); + val = PQresultErrorField(res, 'S'); /* Severity */ + if (val) + appendPQExpBuffer(&workBuf, "%s: ", val); + if (conn->verbosity == PQERRORS_VERBOSE) + { + val = PQresultErrorField(res, 'C'); /* SQLSTATE Code */ + if (val) + appendPQExpBuffer(&workBuf, "%s: ", val); + } + val = PQresultErrorField(res, 'M'); /* Primary message */ + if (val) + appendPQExpBufferStr(&workBuf, val); + val = PQresultErrorField(res, 'P'); /* Position */ + if (val) + { + /* translator: %s represents a digit string */ + appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), val); + } appendPQExpBufferChar(&workBuf, '\n'); - if (res->errDetail) - appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), - res->errDetail); - if (res->errHint) - appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), - res->errHint); - if (res->errContext) - appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), - res->errContext); + if (conn->verbosity != PQERRORS_TERSE) + { + val = PQresultErrorField(res, 'D'); /* Detail */ + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val); + val = PQresultErrorField(res, 'H'); /* Hint */ + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val); + val = PQresultErrorField(res, 'W'); /* Where */ + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), val); + } + if (conn->verbosity == PQERRORS_VERBOSE) + { + const char *valf; + const char *vall; + + valf = PQresultErrorField(res, 'F'); /* File */ + vall = PQresultErrorField(res, 'L'); /* Line */ + val = PQresultErrorField(res, 'R'); /* Routine */ + if (val || valf || vall) + { + appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: ")); + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val); + if (valf && vall) /* unlikely we'd have just one */ + appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"), + valf, vall); + appendPQExpBufferChar(&workBuf, '\n'); + } + } /* * Either save error as current async result, or just emit the notice. @@ -656,7 +665,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError) } else { - PGDONOTICE(conn, workBuf.data); + /* We can cheat a little here and not copy the message. */ + res->errMsg = workBuf.data; + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); PQclear(res); } @@ -710,35 +721,216 @@ static int getNotify(PGconn *conn) { int be_pid; + char *svname; + int nmlen; + int extralen; PGnotify *newNotify; if (pqGetInt(&be_pid, 4, conn)) return EOF; if (pqGets(&conn->workBuffer, conn)) return EOF; + /* must save name while getting extra string */ + svname = strdup(conn->workBuffer.data); + if (!svname) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + { + free(svname); + return EOF; + } /* - * Store the relation name right after the PQnotify structure so it + * Store the strings right after the PQnotify structure so it * can all be freed at once. We don't use NAMEDATALEN because we * don't want to tie this interface to a specific server name length. */ - newNotify = (PGnotify *) malloc(sizeof(PGnotify) + - strlen(conn->workBuffer.data) +1); + nmlen = strlen(svname); + extralen = strlen(conn->workBuffer.data); + newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2); if (newNotify) { newNotify->relname = (char *) newNotify + sizeof(PGnotify); - strcpy(newNotify->relname, conn->workBuffer.data); + strcpy(newNotify->relname, svname); + newNotify->extra = newNotify->relname + nmlen + 1; + strcpy(newNotify->extra, conn->workBuffer.data); newNotify->be_pid = be_pid; DLAddTail(conn->notifyList, DLNewElem(newNotify)); } - /* Swallow extra string (not presently used) */ - if (pqGets(&conn->workBuffer, conn)) + free(svname); + return 0; +} + +/* + * getCopyStart - process CopyInResponse or CopyOutResponse message + * + * parseInput already read the message type and length. + */ +static int +getCopyStart(PGconn *conn, ExecStatusType copytype) +{ + PGresult *result; + int nfields; + int i; + + result = PQmakeEmptyPGresult(conn, copytype); + + if (pqGetc(&conn->copy_is_binary, conn)) + { + PQclear(result); return EOF; + } + result->binary = conn->copy_is_binary; + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + { + PQclear(result); + return EOF; + } + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + for (i = 0; i < nfields; i++) + { + int format; + + if (pqGetInt(&format, 2, conn)) + { + PQclear(result); + return EOF; + } + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce these results to signed form. + */ + format = (int) ((int16) format); + + result->attDescs[i].format = format; + } + + /* Success! */ + conn->result = result; + return 0; +} + +/* + * getReadyForQuery - process ReadyForQuery message + */ +static int +getReadyForQuery(PGconn *conn) +{ + char xact_status; + + if (pqGetc(&xact_status, conn)) + return EOF; + switch (xact_status) + { + case 'I': + conn->xactStatus = PQTRANS_IDLE; + break; + case 'T': + conn->xactStatus = PQTRANS_INTRANS; + break; + case 'E': + conn->xactStatus = PQTRANS_INERROR; + break; + default: + conn->xactStatus = PQTRANS_UNKNOWN; + break; + } return 0; } +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +pqGetCopyData3(PGconn *conn, char **buffer, int async) +{ + char id; + int msgLength; + int avail; + + for (;;) + { + /* + * Do we have the next input message? To make life simpler for async + * callers, we keep returning 0 until the next message is fully + * available, even if it is not Copy Data. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + goto nodata; + if (pqGetInt(&msgLength, 4, conn)) + goto nodata; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength - 4) + goto nodata; + + /* + * If it's anything except Copy Data, exit COPY_OUT mode and let + * caller read status with PQgetResult(). The normal case is that + * it's Copy Done, but we let parseInput read that. + */ + if (id != 'd') + { + conn->asyncStatus = PGASYNC_BUSY; + return -1; + } + + /* + * Drop zero-length messages (shouldn't happen anyway). Otherwise + * pass the data back to the caller. + */ + msgLength -= 4; + if (msgLength > 0) + { + *buffer = (char *) malloc(msgLength + 1); + if (*buffer == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -2; + } + memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength); + (*buffer)[msgLength] = '\0'; /* Add terminating null */ + + /* Mark message consumed */ + conn->inStart = conn->inCursor + msgLength; + + return msgLength; + } + + /* Empty, so drop it and loop around for another */ + conn->inStart = conn->inCursor; + continue; + + nodata: + /* Don't block if async read requested */ + if (async) + return 0; + /* Need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + return -2; + } +} /* * PQgetline - gets a newline-terminated string from the backend. @@ -1108,7 +1300,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid, continue; break; case 'Z': /* backend is ready for new query */ - if (pqGetc(&conn->xact_status, conn)) + if (getReadyForQuery(conn)) continue; /* consume the message and exit */ conn->inStart += 5 + msgLength; diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index da61b77090..d8ff52d6b0 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-fe.h,v 1.93 2003/06/08 17:43:00 tgl Exp $ + * $Id: libpq-fe.h,v 1.94 2003/06/21 21:51:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,6 +88,22 @@ typedef enum PGRES_FATAL_ERROR /* query failed */ } ExecStatusType; +typedef enum +{ + PQTRANS_IDLE, /* connection idle */ + PQTRANS_ACTIVE, /* command in progress */ + PQTRANS_INTRANS, /* idle, within transaction block */ + PQTRANS_INERROR, /* idle, within failed transaction */ + PQTRANS_UNKNOWN /* cannot determine status */ +} PGTransactionStatusType; + +typedef enum +{ + PQERRORS_TERSE, /* single-line error messages */ + PQERRORS_DEFAULT, /* recommended style */ + PQERRORS_VERBOSE /* all the facts, ma'am */ +} PGVerbosity; + /* PGconn encapsulates a connection to the backend. * The contents of this struct are not supposed to be known to applications. */ @@ -108,12 +124,13 @@ typedef struct pg_result PGresult; */ typedef struct pgNotify { - char *relname; /* name of relation containing data */ - int be_pid; /* process id of backend */ + char *relname; /* notification condition name */ + int be_pid; /* process ID of server process */ + char *extra; /* notification parameter */ } PGnotify; -/* PQnoticeProcessor is the function type for the notice-message callback. - */ +/* Function types for notice-handling callbacks */ +typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); typedef void (*PQnoticeProcessor) (void *arg, const char *message); /* Print options for PQprint() */ @@ -227,6 +244,10 @@ 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); +extern int PQprotocolVersion(const PGconn *conn); extern char *PQerrorMessage(const PGconn *conn); extern int PQsocket(const PGconn *conn); extern int PQbackendPID(const PGconn *conn); @@ -238,42 +259,58 @@ extern int PQsetClientEncoding(PGconn *conn, const char *encoding); extern SSL *PQgetssl(PGconn *conn); #endif +/* Set verbosity for PQerrorMessage and PQresultErrorMessage */ +extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); /* Enable/disable tracing */ extern void PQtrace(PGconn *conn, FILE *debug_port); extern void PQuntrace(PGconn *conn); -/* Override default notice processor */ +/* Override default notice handling routines */ +extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, + PQnoticeReceiver proc, + void *arg); extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg); /* === in fe-exec.c === */ -/* Quoting strings before inclusion in queries. */ -extern size_t PQescapeString(char *to, const char *from, size_t length); -extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen, - size_t *bytealen); -extern unsigned char *PQunescapeBytea(const unsigned char *strtext, - size_t *retbuflen); -extern void PQfreemem(void *ptr); - - /* Simple synchronous query */ extern PGresult *PQexec(PGconn *conn, const char *query); -extern PGnotify *PQnotifies(PGconn *conn); -/* Exists for backward compatibility. bjm 2003-03-24 */ -#define PQfreeNotify(ptr) PQfreemem(ptr) +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); /* Interface for multiple-result or asynchronous queries */ 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 PGresult *PQgetResult(PGconn *conn); /* Routines for managing an asynchronous query */ extern int PQisBusy(PGconn *conn); extern int PQconsumeInput(PGconn *conn); +/* LISTEN/NOTIFY support */ +extern PGnotify *PQnotifies(PGconn *conn); + /* Routines for copy in/out */ +extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes); +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 PQputline(PGconn *conn, const char *string); extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize); @@ -303,11 +340,15 @@ extern PGresult *PQfn(PGconn *conn, extern ExecStatusType PQresultStatus(const PGresult *res); extern char *PQresStatus(ExecStatusType status); extern char *PQresultErrorMessage(const PGresult *res); +extern char *PQresultErrorField(const PGresult *res, int fieldcode); extern int PQntuples(const PGresult *res); extern int PQnfields(const PGresult *res); extern int PQbinaryTuples(const PGresult *res); extern char *PQfname(const PGresult *res, int field_num); extern int PQfnumber(const PGresult *res, const char *field_name); +extern Oid PQftable(const PGresult *res, int field_num); +extern int PQftablecol(const PGresult *res, int field_num); +extern int PQfformat(const PGresult *res, int field_num); extern Oid PQftype(const PGresult *res, int field_num); extern int PQfsize(const PGresult *res, int field_num); extern int PQfmod(const PGresult *res, int field_num); @@ -322,6 +363,12 @@ extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); /* Delete a PGresult */ extern void PQclear(PGresult *res); +/* For freeing other alloc'd results, such as PGnotify structs */ +extern void PQfreemem(void *ptr); + +/* Exists for backward compatibility. bjm 2003-03-24 */ +#define PQfreeNotify(ptr) PQfreemem(ptr) + /* * Make an empty PGresult with given status (some apps find this * useful). If conn is not NULL and status indicates an error, the @@ -329,26 +376,33 @@ extern void PQclear(PGresult *res); */ extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); + +/* Quoting strings before inclusion in queries. */ +extern size_t PQescapeString(char *to, const char *from, size_t length); +extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen, + size_t *bytealen); +extern unsigned char *PQunescapeBytea(const unsigned char *strtext, + size_t *retbuflen); + + + /* === 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 *ps); /* option structure */ /* * really old printing routines */ -extern void -PQdisplayTuples(const PGresult *res, +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); -extern void -PQprintTuples(const PGresult *res, +extern void PQprintTuples(const PGresult *res, FILE *fout, /* output stream */ int printAttName, /* print attribute names */ int terseOutput, /* delimiter bars */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 019683ac26..f651320e1c 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.74 2003/06/14 17:49:54 momjian Exp $ + * $Id: libpq-int.h,v 1.75 2003/06/21 21:51:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,9 @@ #ifndef LIBPQ_INT_H #define LIBPQ_INT_H +/* We assume libpq-fe.h has already been included. */ +#include "postgres_fe.h" + #include #include #ifndef WIN32 @@ -28,13 +31,10 @@ #if defined(WIN32) && (!defined(ssize_t)) -typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast +typedef int ssize_t; /* ssize_t doesn't exist in VC (at least * not VC6) */ #endif -/* We assume libpq-fe.h has already been included. */ -#include "postgres_fe.h" - /* include stuff common to fe and be */ #include "getaddrinfo.h" #include "libpq/pqcomm.h" @@ -78,7 +78,10 @@ union pgresult_data typedef struct pgresAttDesc { - char *name; /* type name */ + char *name; /* column name */ + Oid tableid; /* source table, if known */ + int columnid; /* source column, if known */ + int format; /* format code for value (text/binary) */ Oid typid; /* type id */ int typlen; /* type size */ int atttypmod; /* type-specific modifier info */ @@ -91,7 +94,7 @@ typedef struct pgresAttDesc * * The value pointer always points to a null-terminated area; we add a * null (zero) byte after whatever the backend sends us. This is only - * particularly useful for text tuples ... with a binary value, the + * particularly useful for text values ... with a binary value, the * value might have embedded nulls, so the application can't use C string * operators on it. But we add a null anyway for consistency. * Note that the value itself does not contain a length word. @@ -111,6 +114,23 @@ typedef struct pgresAttValue * byte */ } PGresAttValue; +/* Typedef for message-field list entries */ +typedef struct pgMessageField +{ + struct pgMessageField *next; /* list link */ + char code; /* field code */ + char contents[1]; /* field value (VARIABLE LENGTH) */ +} PGMessageField; + +/* Fields needed for notice handling */ +typedef struct +{ + PQnoticeReceiver noticeRec; /* notice message receiver */ + void *noticeRecArg; + PQnoticeProcessor noticeProc; /* notice message processor */ + void *noticeProcArg; +} PGNoticeHooks; + struct pg_result { int ntups; @@ -118,10 +138,10 @@ struct pg_result PGresAttDesc *attDescs; PGresAttValue **tuples; /* each PGresTuple is an array of * PGresAttValue's */ - int tupArrSize; /* size of tuples array allocated */ + int tupArrSize; /* allocated size of tuples array */ ExecStatusType resultStatus; char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the - * last query */ + * query */ int binary; /* binary tuple values if binary == 1, * otherwise text */ @@ -129,35 +149,23 @@ struct pg_result * These fields are copied from the originating PGconn, so that * operations on the PGresult don't have to reference the PGconn. */ - PQnoticeProcessor noticeHook; /* notice/error message processor */ - void *noticeArg; + PGNoticeHooks noticeHooks; int client_encoding; /* encoding id */ /* * Error information (all NULL if not an error result). errMsg is the * "overall" error message returned by PQresultErrorMessage. If we - * got a field-ized error from the server then the additional fields - * may be set. + * have per-field info then it is stored in a linked list. */ char *errMsg; /* error message, or NULL if no error */ - - char *errSeverity; /* severity code */ - char *errCode; /* SQLSTATE code */ - char *errPrimary; /* primary message text */ - char *errDetail; /* detail text */ - char *errHint; /* hint text */ - char *errPosition; /* cursor position */ - char *errContext; /* location information */ - char *errFilename; /* source-code file name */ - char *errLineno; /* source-code line number */ - char *errFuncname; /* source-code function name */ + PGMessageField *errFields; /* message broken into fields */ /* All NULL attributes in the query result point to this null string */ char null_field[1]; /* - * Space management information. Note that attDescs and errMsg, if - * not null, point into allocated blocks. But tuples points to a + * Space management information. Note that attDescs and error stuff, + * if not null, point into allocated blocks. But tuples points to a * separately malloc'd block, so that we can realloc it. */ PGresult_data *curBlock; /* most recently allocated block */ @@ -245,18 +253,18 @@ struct pg_conn /* Optional file to write trace info to */ FILE *Pfdebug; - /* Callback procedure for notice/error message processing */ - PQnoticeProcessor noticeHook; - void *noticeArg; + /* Callback procedures for notice message processing */ + PGNoticeHooks noticeHooks; /* Status indicators */ ConnStatusType status; PGAsyncStatusType asyncStatus; - char xact_status; /* status flag from latest ReadyForQuery */ - char copy_is_binary; /* 1 = copy binary, 0 = copy text */ - int copy_already_done; /* # bytes already returned in COPY OUT */ + PGTransactionStatusType xactStatus; + /* note: xactStatus never changes to ACTIVE */ int nonblocking; /* whether this connection is using a * blocking socket to the backend or not */ + char copy_is_binary; /* 1 = copy binary, 0 = copy text */ + int copy_already_done; /* # bytes already returned in COPY OUT */ Dllist *notifyList; /* Notify msgs not yet handed to * application */ @@ -281,6 +289,7 @@ struct pg_conn char cryptSalt[2]; /* password salt received from backend */ pgParameterStatus *pstatus; /* ParameterStatus data */ int client_encoding; /* encoding id */ + PGVerbosity verbosity; /* error/notice message verbosity */ PGlobjfuncs *lobjfuncs; /* private state for large-object access * fns */ @@ -351,10 +360,12 @@ extern char *pqResultStrdup(PGresult *res, const char *str); extern void pqClearAsyncResult(PGconn *conn); extern void pqSaveErrorResult(PGconn *conn); extern PGresult *pqPrepareAsyncResult(PGconn *conn); +extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *msgtext); extern int pqAddTuple(PGresult *res, PGresAttValue *tup); +extern void pqSaveMessageField(PGresult *res, char code, + const char *value); extern void pqSaveParameterStatus(PGconn *conn, const char *name, const char *value); -extern const char *pqGetParameterStatus(PGconn *conn, const char *name); extern void pqHandleSendFailure(PGconn *conn); /* === in fe-protocol2.c === */ @@ -364,6 +375,7 @@ extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn); extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen, const PQEnvironmentOption *options); extern void pqParseInput2(PGconn *conn); +extern int pqGetCopyData2(PGconn *conn, char **buffer, int async); extern int pqGetline2(PGconn *conn, char *s, int maxlen); extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize); extern int pqEndcopy2(PGconn *conn); @@ -378,6 +390,7 @@ extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen, const PQEnvironmentOption *options); extern void pqParseInput3(PGconn *conn); extern int pqGetErrorNotice3(PGconn *conn, bool isError); +extern int pqGetCopyData3(PGconn *conn, char **buffer, int async); extern int pqGetline3(PGconn *conn, char *s, int maxlen); extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize); extern int pqEndcopy3(PGconn *conn); @@ -393,6 +406,7 @@ extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid, * for Get, EOF merely means the buffer is exhausted, not that there is * necessarily any error. */ +extern int pqCheckOutBufferSpace(int bytes_needed, PGconn *conn); extern int pqCheckInBufferSpace(int bytes_needed, PGconn *conn); extern int pqGetc(char *result, PGconn *conn); extern int pqPutc(char c, PGconn *conn); @@ -423,10 +437,10 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len); /* Note: PGDONOTICE macro will work if applied to either PGconn or PGresult */ #define PGDONOTICE(conn,message) \ - ((*(conn)->noticeHook) ((conn)->noticeArg, (message))) + pqInternalNotice(&(conn)->noticeHooks, (message)) /* - * this is so that we can check is a connection is non-blocking internally + * this is so that we can check if a connection is non-blocking internally * without the overhead of a function call */ #define pqIsnonblocking(conn) ((conn)->nonblocking) diff --git a/src/interfaces/libpq/libpqdll.def b/src/interfaces/libpq/libpqdll.def index f8432fc713..8ff902f521 100644 --- a/src/interfaces/libpq/libpqdll.def +++ b/src/interfaces/libpq/libpqdll.def @@ -97,4 +97,17 @@ EXPORTS pg_utf_mblen @ 93 PQunescapeBytea @ 94 PQfreemem @ 95 - + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109