<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.46 2003/08/01 03:10:04 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.47 2003/08/01 13:53:36 petere Exp $
-->
<chapter id="ecpg">
Embedded <acronym>SQL</acronym> has advantages over other methods
for handling <acronym>SQL</acronym> commands from C code. First, it
takes care of the tedious passing of information to and from
- variables in your <acronym>C</acronym> program. Secondly, embedded
- <acronym>SQL</acronym> in C is defined in the
- <acronym>SQL</acronym> standard and supported by many other
- <acronym>SQL</acronym> databases. The <productname>PostgreSQL</>
- implementation is designed to match this standard as much as
- possible, and it is usually possible to port embedded
- <acronym>SQL</acronym> programs written for other
- SQL databases to <productname>PostgreSQL</productname>
- with relative ease.
+ variables in your <acronym>C</acronym> program. Second, the SQL
+ code in the program is checked at build time for syntactical
+ correctness. Third, embedded <acronym>SQL</acronym> in C is
+ specified in the <acronym>SQL</acronym> standard and supported by
+ many other <acronym>SQL</acronym> database systems. The
+ <productname>PostgreSQL</> implementation is designed to match this
+ standard as much as possible, and it is usually possible to port
+ embedded <acronym>SQL</acronym> programs written for other SQL
+ databases to <productname>PostgreSQL</productname> with relative
+ ease.
</para>
<para>
<listitem>
<simpara>
- <literal><replaceable>character variable</replaceable></literal>
+ an SQL string literal containing one of the above forms
</simpara>
</listitem>
<listitem>
<simpara>
- <literal><replaceable>character string</replaceable></literal>
+ a reference to a character variable containing one of the above forms (see examples)
</simpara>
</listitem>
</simpara>
</listitem>
</itemizedlist>
+
+ If you specify the connection target literally (that is, not
+ through a variable reference) and you don't quote the value, then
+ the case-insensitivity rules of normal SQL are applied. In that
+ case you can also double-quote the individual parameters separately
+ as needed. In practice, it is probably less error-prone to use a
+ (single-quoted) string literal or a variable reference. The
+ connection target <literal>DEFAULT</literal> initiates a connection
+ to the default database under the default user name. No separate
+ user name or connection name may be specified in that case.
</para>
<para>
</simpara>
</listitem>
</itemizedlist>
- The <replaceable>username</replaceable> and
- <replaceable>password</replaceable> may be an SQL name, a
- character variable, or a character string.
+
+ As above, the parameters <replaceable>username</replaceable> and
+ <replaceable>password</replaceable> may be an SQL identifier, an
+ SQL string literal, or a reference to a character variable.
</para>
<para>
The <replaceable>connection-name</replaceable> is used to handle
multiple connections in one program. It can be omitted if a
- program uses only one connection.
+ program uses only one connection. The most recently opened
+ connection becomes the current connection, which is used by default
+ when an SQL statement is to be executed (see later in this
+ chapter).
+ </para>
+
+ <para>
+ Here are some examples of <command>CONNECT</command> statements:
+<programlisting>
+EXEC SQL CONNECT TO mydb@sql.mydomain.com;
+
+EXEC SQL CONNECT TO 'unix:postgresql://sql.mydomain.com/mydb' AS myconnection USER john;
+
+EXEC SQL BEGIN DECLARE SECTION;
+const char *target = "mydb@sql.mydomain.com";
+const char *user = "john";
+EXEC SQL END DECLARE SECTION;
+ ...
+EXEC SQL CONNECT TO :target USER :user;
+</programlisting>
+ The last form makes use of the variant referred to above as
+ character variable reference. You will see in later sections how C
+ variables can be used in SQL statements when you prefix them with a
+ colon.
+ </para>
+
+ <para>
+ Be advised that the format of the connection target is not
+ specified in the SQL standard. So if you want to develop portable
+ applications, you might want to use something based on the last
+ example above to encapsulate the connection target string
+ somewhere.
</para>
</sect1>
</simpara>
</listitem>
</itemizedlist>
+
+ If no connection name is specified, the current connection is
+ closed.
+ </para>
+
+ <para>
+ It is good style that an application always explicitly disconnect
+ from every connection it opened.
</para>
</sect1>
</para>
<para>
- Singleton Select:
+ Single-row Select:
<programlisting>
EXEC SQL SELECT foo INTO :FooBar FROM table1 WHERE ascii = 'doodad';
</programlisting>
</para>
<para>
- Select using Cursors:
+ Select using cursors:
<programlisting>
EXEC SQL DECLARE foo_bar CURSOR FOR
SELECT number, ascii FROM foo
The tokens of the form
<literal>:<replaceable>something</replaceable></literal> are
<firstterm>host variables</firstterm>, that is, they refer to
- variables in the C program. They are explained in the next
- section.
+ variables in the C program. They are explained in <xref
+ linkend="ecpg-variables">.
</para>
<para>
</para>
</sect1>
- <sect1 id="ecpg-variables">
- <title>Passing Data</title>
+ <sect1 id="ecpg-set-connection">
+ <title>Choosing a Connection</title>
+
+ <para>
+ The SQL statements shown in the previous section are executed on
+ the current connection, that is, the most recently opened one. If
+ an application needs to manage multiple connections, then there are
+ two ways to handle this.
+ </para>
+
+ <para>
+ The first option is to explicitly choose a connection for each SQL
+ statement, for example
+<programlisting>
+EXEC SQL AT <replaceable>connection-name</replaceable> SELECT ...;
+</programlisting>
+ This option is particularly suitable if the application needs to
+ use several connections in mixed order.
+ </para>
<para>
- To pass data from the program to the database, for example as
- parameters in a query, or to pass data from the database back to
- the program, the C variables that are intended to contain this data
- need to be declared in a specially marked section, so the embedded
- SQL preprocessor is made aware of them.
+ The second option is to execute a statement to switch the current
+ connection. That statement is:
+<programlisting>
+SET CONNECTION <replaceable>connection-name</replaceable>;
+</programlisting>
+ This option is particularly convenient if many statements are to be
+ executed on the same connection.
</para>
+ </sect1>
+
+ <sect1 id="ecpg-variables">
+ <title>Using Host Variables</title>
<para>
- This section starts with
+ In <xref linkend="ecpg-commands"> you saw how you can execute SQL
+ statements from an embedded SQL program. Some of those statements
+ only used fixed values and did not provide a way to insert
+ user-supplied values into statements or have the program process
+ the values returned by the query. Those kinds of statements are
+ not really useful in real applications. This section explains in
+ detail how you can pass data between your C program and the
+ embedded SQL statements using a simple mechanism called
+ <firstterm>host variables</firstterm>.
+ </para>
+
+ <sect2>
+ <title>Overview</title>
+
+ <para>
+ Passing data between the C program and the SQL statements is
+ particularly simple in embedded SQL. Instead of having the
+ program paste the data into the statement, which entails various
+ complications, such as properly quoting the value, you can simply
+ write the name of a C variable into the SQL statement, prefixed by
+ a colon. For example:
+<programlisting>
+INSERT INTO sometable VALUES (:v1, 'foo', :v2);
+</programlisting>
+ This statements refers to two C variables named
+ <varname>v1</varname> and <varname>v2</varname> and also uses a
+ regular SQL string literal, to illustrate that you are not
+ restricted to use one kind of data or the other.
+ </para>
+
+ <para>
+ This style of inserting C variables in SQL statements works
+ anywhere a value expression is expected in an SQL statement. In
+ the SQL environment we call the references to C variables
+ <firstterm>host variables</firstterm>.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Declare Sections</title>
+
+ <para>
+ To pass data from the program to the database, for example as
+ parameters in a query, or to pass data from the database back to
+ the program, the C variables that are intended to contain this
+ data need to be declared in specially marked sections, so the
+ embedded SQL preprocessor is made aware of them.
+ </para>
+
+ <para>
+ This section starts with
<programlisting>
EXEC SQL BEGIN DECLARE SECTION;
</programlisting>
- and ends with
+ and ends with
<programlisting>
EXEC SQL END DECLARE SECTION;
</programlisting>
- Between those lines, there must be normal C variable declarations, such as
+ Between those lines, there must be normal C variable declarations,
+ such as
<programlisting>
int x;
char foo[16], bar[16];
</programlisting>
+ You can have as many declare sections in a program as you like.
+ </para>
+
+ <para>
+ The declarations are also echoed to the output file as a normal C
+ variables, so there's no need to declare them again. Variables
+ that are not intended to be used with SQL commands can be declared
+ normally outside these special sections.
+ </para>
+
+ <para>
+ The definition of a structure or union also must be listed inside
+ a <literal>DECLARE</> section. Otherwise the preprocessor cannot
+ handle these types since it does not know the definition.
+ </para>
+
+ <para>
+ The special types <type>VARCHAR</type> and <type>VARCHAR2</type>
+ are converted into a named <type>struct</> for every variable. A
+ declaration like
+<programlisting>
+VARCHAR var[180];
+</programlisting>
+ is converted into
+<programlisting>
+struct varchar_var { int len; char arr[180]; } var;
+</programlisting>
+ This structure is suitable for interfacing with SQL datums of type
+ <type>varchar</type>.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title><command>SELECT INTO</command> and <command>FETCH INTO</command></title>
+
+ <para>
+ Now you should be able to pass data generated by your program into
+ an SQL command. But how do you retrieve the results of a query?
+ For that purpose, embedded SQL provides special variants of the
+ usual commands <command>SELECT</command> and
+ <command>FETCH</command>. These commands have a special
+ <literal>INTO</literal> clause that specifies which host variables
+ the retrieved values are to be stored in.
+ </para>
+
+ <para>
+ Here is an example:
+<programlisting>
+/*
+ * assume this table:
+ * CREATE TABLE test1 (a int, b varchar(50));
+ */
+
+EXEC SQL BEGIN DECLARE SECTION;
+int v1;
+VARCHAR v2;
+EXEC SQL END DECLARE SECTION;
+
+ ...
+
+EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;
+</programlisting>
+ So the <literal>INTO</literal> clause appears between the select
+ list and the <literal>FROM</literal> clause. The number of
+ elements in the select list and the list after
+ <literal>INTO</literal> (also called the target list) must be
+ equal.
+ </para>
+
+ <para>
+ Here is an example using the command <command>FETCH</command>:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int v1;
+VARCHAR v2;
+EXEC SQL END DECLARE SECTION;
+
+ ...
+
+EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;
+
+ ...
+
+do {
+ ...
+ EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
+ ...
+} while (...);
+</programlisting>
+ Here the <literal>INTO</literal> clause appears after all the
+ normal clauses.
+ </para>
+
+ <para>
+ Both of these methods only allow retrieving one row at a time. If
+ you need to process result sets that potentially contain more than
+ one row, you need to use a cursor, as shown in the second example.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Indicators</title>
+
+ <para>
+ The examples above do not handle null values. In fact, the
+ retrieval examples will raise an error if they fetch a null value
+ from the database. To be able to pass null values to the database
+ or retrieve null values from the database, you need to append a
+ second host variable specification to each host variable that
+ contains data. This second host variable is called the
+ <firstterm>indicator</firstterm> and contains a flag that tells
+ whether the datums is null, in which case the value of the real
+ host variable is ignored. Here is an example that handles the
+ retrieval of null values correctly:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+VARCHAR val;
+int val_ind;
+EXEC SQL END DECLARE SECTION:
+
+ ...
+
+EXEC SQL SELECT b INTO :val :val_ind FROM test1;
+</programlisting>
+ The indicator variable <varname>val_ind</varname> will be zero if
+ the value was not null, and it will be negative if the value was
+ null.
+ </para>
+
+ <para>
+ The indicator has another function: if the indicator value is
+ positive, it means that the value is not null, but it was
+ truncated when it was stored in the host variable.
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="ecpg-dynamic">
+ <title>Dynamic SQL</title>
+
+ <para>
+ In many cases, the particular SQL statements that an application
+ has to execute are known at the time the application is written.
+ In some cases, however, the SQL statements are composed at run time
+ or provided by an external source. In these cases you cannot embed
+ the SQL statements directly into the C source code, but there is a
+ facility that allows you to call arbitrary SQL statements that you
+ provide in a string variable.
</para>
<para>
- The declarations are also echoed to the output file as a normal C
- variables, so there's no need to declare them again. Variables
- that are not intended to be used with SQL commands can be declared
- normally outside these special sections.
+ The simplest way to execute an arbitrary SQL statement is to use
+ the command <command>EXECUTE IMMEDIATE</command>. For example:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "CREATE TABLE test1 (...);";
+EXEC SQL END DECLARE SECTION;
+
+EXECUTE IMMEDIATE :stmt;
+</programlisting>
+ You may not execute statements that retrieve data (e.g.,
+ <command>SELECT</command>) this way.
</para>
<para>
- The definition of a structure or union also must be listed inside a
- <literal>DECLARE</> section. Otherwise the preprocessor cannot
- handle these types since it does not know the definition.
+ A more powerful way to execute arbitrary SQL statements is to
+ prepare them once and execute the prepared statement as often as
+ you like. It is also possible to prepare a generalized version of
+ a statement and then execute specific versions of it by
+ substituting parameters. When preparing the statement, write
+ question marks where you want to substitute parameters later. For
+ example:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
+EXEC SQL END DECLARE SECTION;
+
+PREPARE mystmt FROM :stmt;
+ ...
+EXECUTE mystmt USING 42, 'foobar';
+</programlisting>
+ If the statement you are executing returns values, then add an
+ <literal>INTO</literal> clause:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
+int v1, v2;
+VARCHAR v3;
+EXEC SQL END DECLARE SECTION;
+
+PREPARE mystmt FROM :stmt;
+ ...
+EXECUTE mystmt INTO v1, v2, v3 USING 37;
+</programlisting>
+ An <command>EXECUTE</command> command may have an
+ <literal>INTO</literal> clause, a <literal>USING</literal> clause,
+ both, or neither.
</para>
<para>
- The special types <type>VARCHAR</type> and <type>VARCHAR2</type>
- are converted into a named <type>struct</> for every variable. A
- declaration like
+ When you don't need the prepared statement anymore, you should
+ deallocate it:
<programlisting>
-VARCHAR var[180];
+EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;
</programlisting>
- is converted into
+ </para>
+ </sect1>
+
+ <sect1 id="ecpg-descriptors">
+ <title>Using SQL Descriptor Areas</title>
+
+ <para>
+ An SQL descriptor area is a more sophisticated method for
+ processing the result of a <command>SELECT</command> or
+ <command>FETCH</command> statement. An SQL descriptor area groups
+ the data of one row of data together with metadata items into one
+ data structure. The metadata is particularly useful when executing
+ dynamic SQL statements, where the nature of the result columns may
+ not be known ahead of time.
+ </para>
+
+ <para>
+ An SQL descriptor area consists of a header, which contains
+ information concerning the entire descriptor, and one or more item
+ descriptor areas, which basically each describe one column in the
+ result row.
+ </para>
+
+ <para>
+ Before you can use an SQL descriptor area, you need to allocate one:
<programlisting>
-struct varchar_var { int len; char arr[180]; } var;
+EXEC SQL ALLOCATE DESCRIPTOR <replaceable>identifier</replaceable>;
+</programlisting>
+ The identifier serves as the <quote>variable name</quote> of the
+ descriptor area. The scope of the allocated descriptor is WHAT?.
+ When you don't need the descriptor anymore, you should deallocate
+ it:
+<programlisting>
+EXEC SQL DEALLOCATE DESCRIPTOR <replaceable>identifier</replaceable>;
</programlisting>
- This structure is suitable for interfacing with SQL datums of type
- <type>varchar</type>.
</para>
<para>
- To use a properly declared C variable in an SQL statement, write
- <literal>:<replaceable>varname</></literal> where an expression is
- expected. See the previous section for some examples.
+ To use a descriptor area, specify it as the storage target in an
+ <literal>INTO</literal> clause, instead of listing host variables:
+<programlisting>
+EXEC SQL FETCH NEXT FROM mycursor INTO DESCRIPTOR mydesc;
+</programlisting>
+ </para>
+
+ <para>
+ Now how do you get the data out of the descriptor area? You can
+ think of the descriptor area as a structure with named fields. To
+ retrieve the value of a field from the header and store it into a
+ host variable, use the following command:
+<programlisting>
+EXEC SQL GET DESCRIPTOR <replaceable>name</replaceable> :<replaceable>hostvar</replaceable> = <replaceable>field</replaceable>;
+</programlisting>
+ Currently, there is only one header field defined:
+ <replaceable>COUNT</replaceable>, which tells how many item
+ descriptor areas exist (that is, how many columns are contained in
+ the result). The host variable needs to be of an integer type. To
+ get a field from the item descriptor area, use the following
+ command:
+<programlisting>
+EXEC SQL GET DESCRIPTOR <replaceable>name</replaceable> VALUE <replaceable>num</replaceable> :<replaceable>hostvar</replaceable> = <replaceable>field</replaceable>;
+</programlisting>
+ <replaceable>num</replaceable> can be a literal integer or a host
+ variable containing an integer. Possible fields are:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>CARDINALITY</literal> (integer)<term>
+ <listitem>
+ <para>
+ number of rows in the result set
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DATA</literal><term>
+ <listitem>
+ <para>
+ actual data item (therefore, the data type of this field
+ depends on the query)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DATETIME_INTERVAL_CODE</literal> (integer)<term>
+ <listitem>
+ <para>
+ ?
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DATETIME_INTERVAL_PRECISION</literal> (integer)<term>
+ <listitem>
+ <para>
+ not implemented
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>INDICATOR</literal> (integer)<term>
+ <listitem>
+ <para>
+ the indicator (indicating a null value or a value truncation)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>KEY_MEMBER</literal> (integer)<term>
+ <listitem>
+ <para>
+ not implemented
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>LENGTH</literal> (integer)<term>
+ <listitem>
+ <para>
+ length of the datum in characters
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NAME</literal> (string)<term>
+ <listitem>
+ <para>
+ name of the column
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NULLABLE</literal> (integer)<term>
+ <listitem>
+ <para>
+ not implemented
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>OCTET_LENGTH</literal> (integer)<term>
+ <listitem>
+ <para>
+ length of the character representation of the datum in bytes
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PRECISION</literal> (integer)<term>
+ <listitem>
+ <para>
+ precision (for type <type>numeric</type>)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RETURNED_LENGTH</literal> (integer)<term>
+ <listitem>
+ <para>
+ length of the datum in characters
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RETURNED_OCTET_LENGTH</literal> (integer)<term>
+ <listitem>
+ <para>
+ length of the character representation of the datum in bytes
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SCALE</literal> (integer)<term>
+ <listitem>
+ <para>
+ scale (for type <type>numeric</type>)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>TYPE</literal> (integer)<term>
+ <listitem>
+ <para>
+ numeric code of the data type of the column
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</para>
</sect1>
<title>Error Handling</title>
<para>
- The embedded SQL interface provides a simplistic and a complex way
- to handle exceptional conditions in a program. The first method
- causes a message to printed automatically when a certain condition
- occurs. For example:
+ This section describes how you can handle exceptional conditions
+ and warnings in an embedded SQL program. There are several
+ nonexclusive facilities for this.
+ </para>
+
+ <sect2>
+ <title>Setting Callbacks</title>
+
+ <para>
+ One simple method to catch errors and warnings is to set a
+ specific action to be executed whenever a particular condition
+ occurs. In general:
<programlisting>
-EXEC SQL WHENEVER sqlerror sqlprint;
+EXEC SQL WHENEVER <replaceable>condition</replaceable> <replaceable>action</replaceable>;
</programlisting>
- or
+ </para>
+
+ <para>
+ <replaceable>condition</replaceable> can be one of the following:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>SQLERROR</literal></term>
+ <listitem>
+ <para>
+ The specified action is called whenever an error occurs during
+ the execution of an SQL statement.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SQLWARNING</literal></term>
+ <listitem>
+ <para>
+ The specified action is called whenever a warning occurs
+ during the execution of an SQL statement.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NOT FOUND</literal></term>
+ <listitem>
+ <para>
+ The specified action is called whenever an SQL statement
+ retrieves or affects zero rows. (This condition is not an
+ error, but you might be interested in handling it specially.)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ <replaceable>action</replaceable> can be one of the following:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>CONTINUE</literal></term>
+ <listitem>
+ <para>
+ This effectively means that the condition is ignored. This is
+ the default.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>GOTO <replaceable>label</replaceable></literal></term>
+ <term><literal>GO TO <replaceable>label</replaceable></literal></term>
+ <listitem>
+ <para>
+ Jump to the specified label (using a C <literal>goto</literal>
+ statement).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SQLPRINT</literal></term>
+ <listitem>
+ <para>
+ Print a message to standard error. This is useful for simple
+ programs or during prototyping. The details of the message
+ cannot be configured.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>STOP</literal></term>
+ <listitem>
+ <para>
+ Call <literal>exit(1)</literal>, which will terminate the
+ program.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>BREAK</literal></term>
+ <listitem>
+ <para>
+ Execute the C statement <literal>break</literal>. This should
+ only be used in loops or <literal>switch</literal> statements.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CALL <replaceable>name</replaceable> (<replaceable>args</replaceable>)</literal></term>
+ <term><literal>DO <replaceable>name</replaceable> (<replaceable>args</replaceable>)</literal></term>
+ <listitem>
+ <para>
+ Call the specified C functions with the specified arguments.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ The SQL standard only provides for the actions
+ <literal>CONTINUE</literal> and <literal>GOTO</literal> (and
+ <literal>GO TO</literal>).
+ </para>
+
+ <para>
+ Here is an example that you might want to use in a simple program.
+ It prints a simple message when a warning occurs and aborts the
+ program when an error happens.
<programlisting>
-EXEC SQL WHENEVER not found sqlprint;
+EXEC SQL WHENEVER SQLWARNING SQLPRINT;
+EXEC SQL WHENEVER SQLERROR STOP;
</programlisting>
- This error handling remains enabled throughout the entire program.
- </para>
+ </para>
- <note>
<para>
- This is <emphasis>not</emphasis> an exhaustive example of usage
- for the <command>EXEC SQL WHENEVER</command> statement. Further
- examples of usage may be found in SQL manuals (e.g.,
- <citetitle>The LAN TIMES Guide to SQL</> by Groff and Weinberg).
+ The statement <literal>EXEC SQL WHENEVER</literal> is a directive
+ of the SQL preprocessor, not a C statement. The error or warning
+ actions that it sets apply to all embedded SQL statements that
+ appear below the point where the handler is set, unless a
+ different action was set for the same condition between the first
+ <literal>EXEC SQL WHENEVER</literal> and the SQL statement causing
+ the condition, regardless of the flow of control in the C program.
+ So neither of the two following C program exerpts will have the
+ desired effect.
+<programlisting>
+/*
+ * WRONG
+ */
+int main(int argc, char *argv[])
+{
+ ...
+ if (verbose) {
+ EXEC SQL WHENEVER SQLWARNING SQLPRINT;
+ }
+ ...
+ EXEC SQL SELECT ...;
+ ...
+}
+</programlisting>
+
+<programlisting>
+/*
+ * WRONG
+ */
+int main(int argc, char *argv[])
+{
+ ...
+ set_error_handler();
+ ...
+ EXEC SQL SELECT ...;
+ ...
+}
+
+static void set_error_handler(void)
+{
+ EXEC SQL WHENEVER SQLERROR STOP;
+}
+</programlisting>
</para>
- </note>
+ </sect2>
- <para>
- For a more powerful error handling, the embedded SQL interface
- provides a <type>struct</> and a variable with the name
- <varname>sqlca</varname> as follows:
+ <sect2>
+ <title>sqlca</title>
+
+ <para>
+ For a more powerful error handling, the embedded SQL interface
+ provides a global variable with the name <varname>sqlca</varname>
+ that has the following structure:
<programlisting>
-struct sqlca
+struct
{
char sqlcaid[8];
long sqlabc;
char sqlerrmc[70];
} sqlerrm;
char sqlerrp[8];
-
long sqlerrd[6];
- /* 0: empty */
- /* 1: OID of processed row if applicable */
- /* 2: number of rows processed in an INSERT, UPDATE */
- /* or DELETE statement */
- /* 3: empty */
- /* 4: empty */
- /* 5: empty */
-
char sqlwarn[8];
- /* 0: set to 'W' if at least one other is 'W' */
- /* 1: if 'W' at least one character string */
- /* value was truncated when it was */
- /* stored into a host variable */
- /* 2: empty */
- /* 3: empty */
- /* 4: empty */
- /* 5: empty */
- /* 6: empty */
- /* 7: empty */
-
- char sqlext[8];
+ char sqlstate[5];
} sqlca;
</programlisting>
- (Many of the empty fields may be used in a future release.)
- </para>
+ (In a multithreaded program, every thread automatically gets its
+ own copy of <varname>sqlca</varname>. This works similar to the
+ handling of the standard C global variable
+ <varname>errno</varname>.)
+ </para>
- <para>
- If no error occurred in the last <acronym>SQL</acronym> statement,
- <literal>sqlca.sqlcode</literal> will be 0
- (<symbol>ECPG_NO_ERROR</>). If <literal>sqlca.sqlcode</literal> is
- less than zero, this is a serious error, like the database
- definition does not match the query. If it is greater than zero, it
- is a normal error like the table did not contain the requested row.
- </para>
+ <para>
+ <varname>sqlca</varname> covers both warnings and errors. If
+ multiple warnings or errors occur during the execution of a
+ statement, then <varname>sqlca</varname> will only contain
+ information about the last one.
+ </para>
- <para>
- <literal>sqlca.sqlerrm.sqlerrmc</literal> will contain a string
- that describes the error. The string ends with the line number in
- the source file.
- </para>
+ <para>
+ If no error occurred in the last <acronym>SQL</acronym> statement,
+ <literal>sqlca.sqlcode</literal> will be 0 and
+ <literal>sqlca.sqlstate</literal> will be
+ <literal>"00000"</literal>. If a warning or error occured, then
+ <literal>sqlca.sqlcode</literal> will be negative and
+ <literal>sqlca.sqlstate</literal> will be different from
+ <literal>"00000"</literal>. A positive
+ <literal>sqlca.sqlcode</literal> indicates a harmless condition,
+ such as that the last query returned zero rows.
+ <literal>sqlcode</literal> and <literal>sqlstate</literal> are two
+ different error code schemes; details appear below.
+ </para>
- <para>
- These are the errors that can occur:
+ <para>
+ If the last SQL statement was successful, then
+ <literal>sqlca.sqlerrd[1]</literal> contains the OID of the
+ processed row, if applicable, and
+ <literal>sqlca.sqlerrd[2]</literal> contains the number of
+ processed or returned rows, if applicable to the command.
+ </para>
- <variablelist>
- <varlistentry>
- <term><computeroutput>-12: Out of memory in line %d.</computeroutput></term>
- <listitem>
- <para>
- Should not normally occur. This indicates your virtual memory
- is exhausted.
+ <para>
+ In case of an error or warning,
+ <literal>sqlca.sqlerrm.sqlerrmc</literal> will contain a string
+ that describes the error. The field
+ <literal>sqlca.sqlerrm.sqlerrml</literal> contains the length of
+ the error message that is stored in
+ <literal>sqlca.sqlerrm.sqlerrmc</literal> (the result of
+ <function>strlen()</function>, not really interesting for a C
+ programmer).
+ </para>
+
+ <para>
+ In case of a warning, <literal>sqlca.sqlwarn[2]</literal> is set
+ to <literal>W</literal>. (In all other cases, it is set to
+ something different from <literal>W</literal>.) If
+ <literal>sqlca.sqlwarn[1]</literal> is set to
+ <literal>W</literal>, then a value was truncated when it was
+ stored in a host variable. <literal>sqlca.sqlwarn[0]</literal> is
+ set to <literal>W</literal> if any of the other elements are set
+ to indicate a warning.
+ </para>
+
+ <para>
+ The fields <structfield>sqlcaid</structfield>,
+ <structfield>sqlcabc</structfield>,
+ <structfield>sqlerrp</structfield>, and the remaining elements of
+ <structfield>sqlerrd</structfield> and
+ <structfield>sqlwarn</structfield> currently contain no useful
+ information.
+ </para>
+
+ <para>
+ The structure <varname>sqlca</varname> is not defined in the SQL
+ standard, but is implemented in several other SQL database
+ systems. The definitions are similar in the core, but if you want
+ to write portable applications, then you should investigate the
+ different implementations carefully.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title><literal>SQLSTATE</literal> vs <literal>SQLCODE</literal></title>
+
+ <para>
+ The fields <literal>sqlca.sqlstate</literal> and
+ <literal>sqlca.sqlcode</literal> are two different schemes that
+ provide error codes. Both are specified in the SQL standard, but
+ <literal>SQLCODE</literal> has been marked deprecated in the 1992
+ edition of the standard and has been dropped in the 1999 edition.
+ Therefore, new applications are strongly encouraged to use
+ <literal>SQLSTATE</literal>.
+ </para>
+
+ <para>
+ <literal>SQLSTATE</literal> is a five-character array. The five
+ characters contain digits or upper-case letters that represent
+ codes of various error and warning conditions.
+ <literal>SQLSTATE</literal> has a hierarchical scheme: the first
+ two characters indicate the general class of the condition, the
+ last three characters indicate a subclass of the general
+ condition. A successful state is indicated by the code
+ <literal>00000</literal>. Further information about the codes can
+ be found XXX. The <literal>SQLSTATE</literal> codes are for the
+ most part defined in the SQL standard. The PostgreSQL server
+ natively supports <literal>SQLSTATE</literal> error codes;
+ therefore a high degree of consistency can be achieved by using
+ this error code scheme throughout all applications.
+ </para>
+
+ <para>
+ <literal>SQLCODE</literal>, the deprecated error code scheme, is a
+ simple integer. A value of 0 indicates success, a positive value
+ indicates success with additional information, a negative value
+ indicates an error. The SQL standard only defines the positive
+ value +100, which indicates that the last command returned or
+ affected zero rows, and no specific negative values. Therefore,
+ this scheme can only achieve poor portability and does not have a
+ hierarchical code assignment. Historically, the embedded SQL
+ processor for PostgreSQL has assigned some specific
+ <literal>SQLCODE</literal> values for its use, which are listed
+ below with their numeric value and their symbolic name. Remember
+ that these are not portable to other SQL implementations. To
+ simplify the porting of applications to the
+ <literal>SQLSTATE</literal> scheme, the corresponding
+ <literal>SQLSTATE</literal> is also listed. There is, however, no
+ one-to-one or one-to-many mapping between the two schemes (indeed
+ it is many-to-many), so you should consult the global
+ <literal>SQLSTATE</literal> listing in XXX in each case.
+ </para>
+
+ <para>
+ These are the assigned <literal>SQLCODE</literal> values:
+
+ <variablelist>
+ <varlistentry>
+ <term>-12 (<symbol>ECPG_OUT_OF_MEMORY</symbol>)</term>
+ <listitem>
+ <para>
+ Indicates that your virtual memory is exhausted. (SQLSTATE
+ YE001)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-200 (ECPG_UNSUPPORTED): Unsupported type %s on line %d.</computeroutput></term>
+ <term>-200 (<symbol>ECPG_UNSUPPORTED</symbol>)</term>
<listitem>
<para>
- Should not normally occur. This indicates the preprocessor has
- generated something that the library does not know about.
- Perhaps you are running incompatible versions of the
- preprocessor and the library.
+ Indicates the preprocessor has generated something that the
+ library does not know about. Perhaps you are running
+ incompatible versions of the preprocessor and the
+ library. (SQLSTATE YE002)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-201 (ECPG_TOO_MANY_ARGUMENTS): Too many arguments line %d.</computeroutput></term>
+ <term>-201 (<symbol>ECPG_TOO_MANY_ARGUMENTS</symbol>)</term>
<listitem>
<para>
- This means that the server has returned more arguments than we
- have matching variables. Perhaps you have forgotten a couple
- of the host variables in the <command>INTO
- :var1, :var2</command> list.
+ This means that the command specified more host variables than
+ the command expected. (SQLSTATE 07001 or 07002)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-202 (ECPG_TOO_FEW_ARGUMENTS): Too few arguments line %d.</computeroutput></term>
+ <term>-202 (<symbol>ECPG_TOO_FEW_ARGUMENTS</symbol>)</term>
<listitem>
<para>
- This means that the server has returned fewer arguments than we
- have host variables. Perhaps you have too many host variables
- in the <command>INTO :var1,:var2</command> list. </para>
+ This means that the command specified fewer host variables than
+ the command expected. (SQLSTATE 07001 or 07002)
+ </para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-203 (ECPG_TOO_MANY_MATCHES): Too many matches line %d.</computeroutput></term>
+ <term>-203 (<symbol>ECPG_TOO_MANY_MATCHES</symbol>)</term>
<listitem>
<para>
- This means the query has returned multiple rows but the
- variables specified are not arrays. The
- <command>SELECT</command> command was not unique.
+ This means a query has returned multiple rows but the statement
+ was only prepared to store one result row (for example, because
+ the specified variables are not arrays). (SQLSTATE 21000)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-204 (ECPG_INT_FORMAT): Not correctly formatted int type: %s line %d.</computeroutput></term>
+ <term>-204 (<symbol>ECPG_INT_FORMAT</symbol>)</term>
<listitem>
<para>
- This means the host variable is of type <type>int</type> and
- the field in the <productname>PostgreSQL</productname> database
- is of another type and contains a value that cannot be
- interpreted as an <type>int</type>. The library uses
- <function>strtol()</function> for this conversion.
+ The host variable is of type <type>int</type> and the datum in
+ the database is of a different type and contains a value that
+ cannot be interpreted as an <type>int</type>. The library uses
+ <function>strtol()</function> for this conversion. (SQLSTATE
+ 42804)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-205 (ECPG_UINT_FORMAT): Not correctly formatted unsigned type: %s line %d.</computeroutput></term>
+ <term>-205 (<symbol>ECPG_UINT_FORMAT</symbol>)</term>
<listitem>
<para>
- This means the host variable is of type <type>unsigned
- int</type> and the field in the
- <productname>PostgreSQL</productname> database is of another
- type and contains a value that cannot be interpreted as an
- <type>unsigned int</type>. The library uses
- <function>strtoul()</function> for this conversion.
+ The host variable is of type <type>unsigned int</type> and the
+ datum in the database is of a different type and contains a
+ value that cannot be interpreted as an <type>unsigned
+ int</type>. The library uses <function>strtoul()</function>
+ for this conversion. (SQLSTATE 42804)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-206 (ECPG_FLOAT_FORMAT): Not correctly formatted floating-point type: %s line %d.</computeroutput></term>
+ <term>-206 (<symbol>ECPG_FLOAT_FORMAT</symbol>)</term>
<listitem>
<para>
- This means the host variable is of type <type>float</type> and
- the field in the <productname>PostgreSQL</productname> database
- is of another type and contains a value that cannot be
- interpreted as a <type>float</type>. The library uses
- <function>strtod()</function> for this conversion.
+ The host variable is of type <type>float</type> and the datum
+ in the database is of another type and contains a value that
+ cannot be interpreted as a <type>float</type>. The library
+ uses <function>strtod()</function> for this conversion.
+ (SQLSTATE 42804)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-207 (ECPG_CONVERT_BOOL): Unable to convert %s to bool on line %d.</computeroutput></term>
+ <term>-207 (<symbol>ECPG_CONVERT_BOOL</symbol>)</term>
<listitem>
<para>
This means the host variable is of type <type>bool</type> and
- the field in the <productname>PostgreSQL</productname> database
- is neither <literal>'t'</> nor <literal>'f'</>.
+ the datum in the database is neither <literal>'t'</> nor
+ <literal>'f'</>. (SQLSTATE 42804)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-208 (ECPG_EMPTY): Empty query line %d.</computeroutput></term>
+ <term>-208 (<symbol>ECPG_EMPTY</symbol>)</term>
<listitem>
<para>
- The query was empty. (This cannot normally happen in an
- embedded SQL program, so it may point to an internal error.)
+ The statement sent to the PostgreSQL server was empty. (This
+ cannot normally happen in an embedded SQL program, so it may
+ point to an internal error.) (SQLSTATE YE002)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-209 (ECPG_MISSING_INDICATOR): NULL value without indicator in line %d.</computeroutput></term>
+ <term>-209 (<symbol>ECPG_MISSING_INDICATOR</symbol>)</term>
<listitem>
<para>
A null value was returned and no null indicator variable was
- supplied.
+ supplied. (SQLSTATE 22002)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-210 (ECPG_NO_ARRAY): Variable is not an array in line %d.</computeroutput></term>
+ <term>-210 (<symbol>ECPG_NO_ARRAY</symbol>)</term>
<listitem>
<para>
An ordinary variable was used in a place that requires an
- array.
+ array. (SQLSTATE 42804)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-211 (ECPG_DATA_NOT_ARRAY): Data read from backend is not an array in line %d.</computeroutput></term>
+ <term>-211 (<symbol>ECPG_DATA_NOT_ARRAY</symbol>)</term>
<listitem>
<para>
The database returned an ordinary variable in a place that
- requires array value.
+ requires array value. (SQLSTATE 42804)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-220 (ECPG_NO_CONN): No such connection %s in line %d.</computeroutput></term>
+ <term>-220 (<symbol>ECPG_NO_CONN</symbol>)</term>
<listitem>
<para>
The program tried to access a connection that does not exist.
+ (SQLSTATE 08003)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-221 (ECPG_NOT_CONN): Not connected in line %d.</computeroutput></term>
+ <term>-221 (<symbol>ECPG_NOT_CONN</symbol>)</term>
<listitem>
<para>
The program tried to access a connection that does exist but is
- not open.
+ not open. (This is an internal error.) (SQLSTATE YE002)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-230 (ECPG_INVALID_STMT): Invalid statement name %s in line %d.</computeroutput></term>
+ <term>-230 (<symbol>ECPG_INVALID_STMT</symbol>)</term>
<listitem>
<para>
The statement you are trying to use has not been prepared.
+ (SQLSTATE 26000)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-240 (ECPG_UNKNOWN_DESCRIPTOR): Descriptor %s not found in line %d.</computeroutput></term>
+ <term>-240 (<symbol>ECPG_UNKNOWN_DESCRIPTOR</symbol>)</term>
<listitem>
<para>
- The descriptor specified was not found. The statement you are
- trying to use has not been prepared.
+ The descriptor specified was not found. The statement you are
+ trying to use has not been prepared. (SQLSTATE 33000)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-241 (ECPG_INVALID_DESCRIPTOR_INDEX): Descriptor index out of range in line %d.</computeroutput></term>
+ <term>-241 (<symbol>ECPG_INVALID_DESCRIPTOR_INDEX</symbol>)</term>
<listitem>
<para>
- The descriptor index specified was out of range.
+ The descriptor index specified was out of range. (SQLSTATE
+ 07009)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM): Unknown descriptor item %s in line %d.</computeroutput></term>
+ <term>-242 (<symbol>ECPG_UNKNOWN_DESCRIPTOR_ITEM</symbol>)</term>
<listitem>
<para>
- The descriptor specified was not found. The statement you are trying to use has not been prepared.
+ An invalid descriptor item was requested. (This is an internal
+ error.) (SQLSTATE YE002)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-243 (ECPG_VAR_NOT_NUMERIC): Variable is not a numeric type in line %d.</computeroutput></term>
+ <term>-243 (<symbol>ECPG_VAR_NOT_NUMERIC</symbol>)</term>
<listitem>
<para>
- The database returned a numeric value and the variable was not
- numeric.
+ During the execution of a dynamic statement, the database
+ returned a numeric value and the host variable was not numeric.
+ (SQLSTATE 07006)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-244 (ECPG_VAR_NOT_CHAR): Variable is not a character type in line %d.</computeroutput></term>
+ <term>-244 (<symbol>ECPG_VAR_NOT_CHAR</symbol>)</term>
<listitem>
<para>
- The database returned a non-numeric value and the variable was
- numeric.
+ During the execution of a dynamic statement, the database
+ returned a non-numeric value and the host variable was numeric.
+ (SQLSTATE 07006)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-400 (ECPG_PGSQL): '%s' in line %d.</computeroutput></term>
+ <term>-400 (<symbol>ECPG_PGSQL</symbol>)</term>
<listitem>
<para>
- Some <productname>PostgreSQL</productname> error. The message
- contains the error message from the
+ Some error caused by the <productname>PostgreSQL</productname>
+ server. The message contains the error message from the
<productname>PostgreSQL</productname> server.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-401 (ECPG_TRANS): Error in transaction processing line %d.</computeroutput></term>
+ <term>-401 (<symbol>ECPG_TRANS</symbol>)</term>
<listitem>
<para>
- The <productname>PostgreSQL</productname> server signaled that we cannot
- start, commit, or rollback the transaction.
+ The <productname>PostgreSQL</productname> server signaled that
+ we cannot start, commit, or rollback the transaction.
+ (SQLSTATE 08007)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>-402 (ECPG_CONNECT): Could not connect to database %s in line %d.</computeroutput></term>
+ <term>-402 (<symbol>ECPG_CONNECT</symbol>)</term>
<listitem>
<para>
- The connection attempt to the database did not work.
+ The connection attempt to the database did not succeed.
+ (SQLSTATE 08001)
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><computeroutput>100 (ECPG_NOT_FOUND): Data not found line %d.</computeroutput></term>
+ <term>100 (<symbol>ECPG_NOT_FOUND</symbol>)</term>
<listitem>
<para>
- This is a <quote>normal</quote> error that tells you that what
- you are querying cannot be found or you are at the end of the
- cursor.
+ This is a harmless condition indicating that the last command
+ retrieved or processed zero rows, or that you are at the end of
+ the cursor. (SQLSTATE 02000)
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
+ </sect2>
</sect1>
<sect1 id="ecpg-include">
in the arguments specified for output.
</para>
- <para>
- <application>ecpg</application> is thread-safe if it is compiled using
- the <literal>--enable-thread-safety</> <filename>configure</filename>
- command-line option. (You might need to use other threading
- command-line options to compile your client code.)
- </para>
-
<para>
The preprocessor program is called <filename>ecpg</filename> and is
included in a normal <productname>PostgreSQL</> installation.
The complete syntax of the <command>ecpg</command> command is
detailed in <xref linkend="app-ecpg">.
</para>
+
+ <para>
+ <application>ecpg</application> is thread-safe if it is compiled using
+ the <option>--enable-thread-safety</> <filename>configure</filename>
+ command-line option. (You might need to use other threading
+ command-line options to compile your client code.)
+ </para>
</sect1>
<sect1 id="ecpg-library">
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.13 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.14 2003/08/01 13:53:36 petere Exp $ */
#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"
{
if ((results = PQexec(con->connection, "begin transaction")) == NULL)
{
- ECPGraise(lineno, ECPG_TRANS, NULL, ECPG_COMPAT_PGSQL);
+ ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL);
return false;
}
PQclear(results);
{
if ((results = PQexec(con->connection, "commit")) == NULL)
{
- ECPGraise(lineno, ECPG_TRANS, NULL, ECPG_COMPAT_PGSQL);
+ ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL);
return false;
}
PQclear(results);
return true;
}
-static void
-ECPGnoticeProcessor_raise(int code, const char *message)
-{
- struct sqlca_t *sqlca = ECPGget_sqlca();
- sqlca->sqlcode = code;
- strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof(sqlca->sqlerrm.sqlerrmc));
- sqlca->sqlerrm.sqlerrmc[sizeof(sqlca->sqlerrm.sqlerrmc) - 1] = 0;
- sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
-
- /* remove trailing newline */
- if (sqlca->sqlerrm.sqlerrml
- && sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] == '\n')
- {
- sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] = 0;
- sqlca->sqlerrm.sqlerrml--;
- }
-
- ECPGlog("raising sqlcode %d\n", code);
-}
-
-/*
- * I know this is a mess, but we can't redesign the backend
- */
static void
-ECPGnoticeProcessor(void *arg, const char *message)
+ECPGnoticeReceiver(void *arg, const PGresult *result)
{
+ char *sqlstate = PQresultErrorField(result, 'C');
+ char *message = PQresultErrorField(result, 'M');
struct sqlca_t *sqlca = ECPGget_sqlca();
- /* these notices raise an error */
- if (strncmp(message, "WARNING: ", 9) && strncmp(message, "NOTICE: ", 8))
- {
- ECPGlog("ECPGnoticeProcessor: strange warning '%s'\n", message);
- ECPGnoticeProcessor_raise(ECPG_WARNING_UNRECOGNIZED, message);
- return;
- }
-
- message += 8;
- while (*message == ' ')
- message++;
- ECPGlog("WARNING: %s", message);
-
- /* WARNING: (transaction aborted): queries ignored until END */
-
- /*
- * WARNING: current transaction is aborted, queries ignored until end
- * of transaction block
- */
- if (strstr(message, "queries ignored") && strstr(message, "transaction")
- && strstr(message, "aborted"))
- {
- ECPGnoticeProcessor_raise(ECPG_WARNING_QUERY_IGNORED, message);
- return;
- }
+ int sqlcode;
- /* WARNING: PerformPortalClose: portal "*" not found */
- if ((!strncmp(message, "PerformPortalClose: portal", 26)
- || !strncmp(message, "PerformPortalFetch: portal", 26))
- && strstr(message + 26, "not found"))
- {
- ECPGnoticeProcessor_raise(ECPG_WARNING_UNKNOWN_PORTAL, message);
+ /* these are not warnings */
+ if (strncmp(sqlstate, "00", 2)==0)
return;
- }
- /* WARNING: BEGIN: already a transaction in progress */
- if (!strncmp(message, "BEGIN: already a transaction in progress", 40))
- {
- ECPGnoticeProcessor_raise(ECPG_WARNING_IN_TRANSACTION, message);
- return;
- }
-
- /* WARNING: AbortTransaction and not in in-progress state */
- /* WARNING: COMMIT: no transaction in progress */
- /* WARNING: ROLLBACK: no transaction in progress */
- if (!strncmp(message, "AbortTransaction and not in in-progress state", 45)
- || !strncmp(message, "COMMIT: no transaction in progress", 34)
- || !strncmp(message, "ROLLBACK: no transaction in progress", 36))
- {
- ECPGnoticeProcessor_raise(ECPG_WARNING_NO_TRANSACTION, message);
- return;
- }
-
- /* WARNING: BlankPortalAssignName: portal * already exists */
- if (!strncmp(message, "BlankPortalAssignName: portal", 29)
- && strstr(message + 29, "already exists"))
- {
- ECPGnoticeProcessor_raise(ECPG_WARNING_PORTAL_EXISTS, message);
- return;
- }
-
- /* these are harmless - do nothing */
-
- /*
- * WARNING: CREATE TABLE / PRIMARY KEY will create implicit index '*'
- * for table '*'
- */
-
- /*
- * WARNING: ALTER TABLE ... ADD CONSTRAINT will create implicit
- * trigger(s) for FOREIGN KEY check(s)
- */
-
- /*
- * WARNING: CREATE TABLE will create implicit sequence '*' for SERIAL
- * column '*.*'
- */
-
- /*
- * WARNING: CREATE TABLE will create implicit trigger(s) for FOREIGN
- * KEY check(s)
- */
- if ((!strncmp(message, "CREATE TABLE", 12) || !strncmp(message, "ALTER TABLE", 11))
- && strstr(message + 11, "will create implicit"))
- return;
-
- /* WARNING: QUERY PLAN: */
- if (!strncmp(message, "QUERY PLAN:", 11)) /* do we really see these? */
- return;
-
- /*
- * WARNING: DROP TABLE implicitly drops referential integrity trigger
- * from table "*"
- */
- if (!strncmp(message, "DROP TABLE implicitly drops", 27))
- return;
-
- /*
- * WARNING: Caution: DROP INDEX cannot be rolled back, so don't abort
- * now
- */
- if (strstr(message, "cannot be rolled back"))
- return;
+ ECPGlog("%s", message);
+
+ /* map to SQLCODE for backward compatibility */
+ if (strcmp(sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME)==0)
+ sqlcode = ECPG_WARNING_UNKNOWN_PORTAL;
+ else if (strcmp(sqlstate, ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION)==0)
+ sqlcode = ECPG_WARNING_IN_TRANSACTION;
+ else if (strcmp(sqlstate, ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION)==0)
+ sqlcode = ECPG_WARNING_NO_TRANSACTION;
+ else if (strcmp(sqlstate, ECPG_SQLSTATE_DUPLICATE_CURSOR)==0)
+ sqlcode = ECPG_WARNING_PORTAL_EXISTS;
+ else
+ sqlcode = 0;
- /* these and other unmentioned should set sqlca->sqlwarn[2] */
- /* WARNING: The ':' operator is deprecated. Use exp(x) instead. */
- /* WARNING: Rel *: Uninitialized page 0 - fixing */
- /* WARNING: PortalHeapMemoryFree: * not in alloc set! */
- /* WARNING: Too old parent tuple found - can't continue vc_repair_frag */
- /* WARNING: identifier "*" will be truncated to "*" */
- /* WARNING: InvalidateSharedInvalid: cache state reset */
- /* WARNING: RegisterSharedInvalid: SI buffer overflow */
+ strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
+ sqlca->sqlcode = sqlcode;
sqlca->sqlwarn[2] = 'W';
sqlca->sqlwarn[0] = 'W';
+
+ strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof(sqlca->sqlerrm.sqlerrmc));
+ sqlca->sqlerrm.sqlerrmc[sizeof(sqlca->sqlerrm.sqlerrmc) - 1] = 0;
+ sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
+
+ ECPGlog("raising sqlcode %d\n", sqlcode);
}
+
/* this contains some quick hacks, needs to be cleaned up, but it works */
bool
ECPGconnect(int lineno, int c, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit)
if (strncmp(dbname, "unix:", 5) != 0)
{
ECPGlog("connect: socketname %s given for TCP connection in line %d\n", host, lineno);
- ECPGraise(lineno, ECPG_CONNECT, realname ? realname : "<DEFAULT>", ECPG_COMPAT_PGSQL);
+ ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : "<DEFAULT>");
if (host)
ECPGfree(host);
if (port)
if (strcmp(dbname + offset, "localhost") != 0 && strcmp(dbname + offset, "127.0.0.1") != 0)
{
ECPGlog("connect: non-localhost access via sockets in line %d\n", lineno);
- ECPGraise(lineno, ECPG_CONNECT, realname ? realname : "<DEFAULT>", ECPG_COMPAT_PGSQL);
+ ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : "<DEFAULT>");
if (host)
ECPGfree(host);
if (port)
user ? "for user " : "", user ? user : "",
lineno, errmsg);
- ECPGraise(lineno, ECPG_CONNECT, db, ECPG_COMPAT_PGSQL);
+ ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
if (host)
ECPGfree(host);
if (port)
this->committed = true;
this->autocommit = autocommit;
- PQsetNoticeProcessor(this->connection, &ECPGnoticeProcessor, (void *) this);
+ PQsetNoticeReceiver(this->connection, &ECPGnoticeReceiver, (void *) this);
return true;
}