From 4f7df90db0f9c3ed61d12a1d7a9efe0de37f4fe3 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Fri, 1 Aug 2003 13:53:36 +0000 Subject: [PATCH] Make ecpg SQLSTATE-aware. Map existing SQLCODE assignments to SQLSTATEs, rather than parsing the message. Add some documentation about embedded SQL. --- doc/src/sgml/ecpg.sgml | 1151 ++++++++++++++++++---- src/backend/commands/portalcmds.c | 5 +- src/interfaces/ecpg/compatlib/Makefile | 4 +- src/interfaces/ecpg/ecpglib/connect.c | 174 +--- src/interfaces/ecpg/ecpglib/data.c | 34 +- src/interfaces/ecpg/ecpglib/descriptor.c | 22 +- src/interfaces/ecpg/ecpglib/error.c | 55 +- src/interfaces/ecpg/ecpglib/execute.c | 56 +- src/interfaces/ecpg/ecpglib/extern.h | 26 + src/interfaces/ecpg/ecpglib/memory.c | 8 +- src/interfaces/ecpg/ecpglib/misc.c | 13 +- src/interfaces/ecpg/ecpglib/prepare.c | 4 +- src/interfaces/ecpg/include/ecpglib.h | 6 +- src/interfaces/ecpg/include/sqlca.h | 2 +- src/interfaces/ecpg/test/Makefile | 4 +- 15 files changed, 1129 insertions(+), 435 deletions(-) diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml index ee386339286..7c4de83b30b 100644 --- a/doc/src/sgml/ecpg.sgml +++ b/doc/src/sgml/ecpg.sgml @@ -1,5 +1,5 @@ <!-- -$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"> @@ -37,15 +37,16 @@ $Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.46 2003/08/01 03:10:04 momjia 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> @@ -100,13 +101,13 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable> <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> @@ -116,6 +117,16 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable> </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> @@ -146,15 +157,47 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable> </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> @@ -194,6 +237,14 @@ EXEC SQL DISCONNECT <optional><replaceable>connection</replaceable></optional>; </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> @@ -231,14 +282,14 @@ EXEC SQL COMMIT; </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 @@ -264,8 +315,8 @@ EXEC SQL COMMIT; 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> @@ -281,65 +332,512 @@ EXEC SQL COMMIT; </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> @@ -347,35 +845,198 @@ struct varchar_var { int len; char arr[180]; } var; <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; @@ -386,317 +1047,411 @@ struct sqlca 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"> @@ -749,13 +1504,6 @@ EXEC SQL INCLUDE <replaceable>filename</replaceable>; 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. @@ -811,6 +1559,13 @@ ECPG = ecpg 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"> diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 978f05ef928..cf4a0638717 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.18 2003/07/28 00:09:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.19 2003/08/01 13:53:36 petere Exp $ * *------------------------------------------------------------------------- */ @@ -169,8 +169,7 @@ PerformPortalFetch(FetchStmt *stmt, /* FIXME: shouldn't this be an ERROR? */ ereport(WARNING, (errcode(ERRCODE_UNDEFINED_CURSOR), - errmsg("portal \"%s\" does not exist", stmt->portalname), - errfunction("PerformPortalFetch"))); /* for ecpg */ + errmsg("portal \"%s\" does not exist", stmt->portalname))); if (completionTag) strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0"); return; diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile index 5bd527d98a8..83d9193dfd4 100644 --- a/src/interfaces/ecpg/compatlib/Makefile +++ b/src/interfaces/ecpg/compatlib/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/compatlib/Makefile,v 1.5 2003/06/24 14:45:46 momjian Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/compatlib/Makefile,v 1.6 2003/08/01 13:53:36 petere Exp $ # #------------------------------------------------------------------------- @@ -16,7 +16,7 @@ NAME= ecpg_compat SO_MAJOR_VERSION= 1 SO_MINOR_VERSION= 0.0 -override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(top_srcdir)/src/include/utils $(CPPFLAGS) +override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) -I$(top_srcdir)/src/include/utils $(CPPFLAGS) SHLIB_LINK = -L../pgtypeslib -lpgtypes -L../ecpglib -lecpg OBJS= informix.o diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index 44b088481c7..1030b041ab1 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -1,4 +1,4 @@ -/* $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" @@ -116,7 +116,7 @@ ECPGsetcommit(int lineno, const char *mode, const char *connection_name) { 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); @@ -130,7 +130,7 @@ ECPGsetcommit(int lineno, const char *mode, const char *connection_name) { 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); @@ -154,151 +154,47 @@ ECPGsetconn(int lineno, const char *connection_name) 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) @@ -406,7 +302,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p 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) @@ -429,7 +325,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p 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) @@ -497,7 +393,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p 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) @@ -528,7 +424,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p this->committed = true; this->autocommit = autocommit; - PQsetNoticeProcessor(this->connection, &ECPGnoticeProcessor, (void *) this); + PQsetNoticeReceiver(this->connection, &ECPGnoticeReceiver, (void *) this); return true; } diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c index 361dfa73a4a..14e459d1b60 100644 --- a/src/interfaces/ecpg/ecpglib/data.c +++ b/src/interfaces/ecpg/ecpglib/data.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.14 2003/08/01 08:21:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.15 2003/08/01 13:53:36 petere Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -34,7 +34,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, { if (*pval != '{') { - ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, NULL, compat); + ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL); return (false); } @@ -90,13 +90,13 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, } else { - ECPGraise(lineno, ECPG_MISSING_INDICATOR, NULL, compat); + ECPGraise(lineno, ECPG_MISSING_INDICATOR, ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER, NULL); return (false); } } break; default: - ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(ind_type), compat); + ECPGraise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ECPGtype_name(ind_type)); return (false); break; } @@ -126,7 +126,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_INT_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -159,7 +159,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_UINT_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -192,7 +192,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_INT_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -209,7 +209,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_UINT_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -235,7 +235,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_FLOAT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -266,7 +266,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, else if (offset == sizeof(int)) *((int *) (var + offset * act_tuple)) = false; else - ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size", compat); + ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size"); break; } else if (pval[0] == 't' && pval[1] == '\0') @@ -276,7 +276,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, else if (offset == sizeof(int)) *((int *) (var + offset * act_tuple)) = true; else - ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size", compat); + ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size"); break; } else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field)) @@ -286,7 +286,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, } } - ECPGraise(lineno, ECPG_CONVERT_BOOL, pval, compat); + ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); break; @@ -396,7 +396,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_NUMERIC_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_NUMERIC_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -423,7 +423,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_INTERVAL_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_INTERVAL_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } } @@ -446,7 +446,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_DATE_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_DATE_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } @@ -468,7 +468,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, if ((isarray && *scan_length != ',' && *scan_length != '}') || (!isarray && *scan_length != '\0' && *scan_length != ' ')) /* Garbage left */ { - ECPGraise(lineno, ECPG_TIMESTAMP_FORMAT, pval, compat); + ECPGraise(lineno, ECPG_TIMESTAMP_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); return (false); } @@ -477,7 +477,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, break; default: - ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(type), compat); + ECPGraise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ECPGtype_name(type)); return (false); break; } diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index bdb308810da..0beae2d93ab 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -1,6 +1,6 @@ /* dynamic SQL support routines * - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.4 2003/08/01 08:21:04 meskes Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.5 2003/08/01 13:53:36 petere Exp $ */ #define POSTGRES_ECPG_INTERNAL @@ -103,7 +103,7 @@ get_int_item(int lineno, void *var, enum ECPGttype vartype, int value) *(double *) var = (double) value; break; default: - ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL); return (false); } @@ -135,7 +135,7 @@ get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int va } break; default: - ECPGraise(lineno, ECPG_VAR_NOT_CHAR, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_VAR_NOT_CHAR, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL); return (false); } @@ -162,13 +162,13 @@ ECPGget_desc(int lineno, char *desc_name, int index,...) ntuples = PQntuples(ECPGresult); if (ntuples < 1) { - ECPGraise(lineno, ECPG_NOT_FOUND, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); return (false); } if (index < 1 || index > PQnfields(ECPGresult)) { - ECPGraise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX, NULL); return (false); } @@ -300,7 +300,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...) { ECPGlog("ECPGget_desc line %d: Incorrect number of matches: %d don't fit into array of %d\n", lineno, ntuples, arrsize); - ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL); return false; } /* allocate storage if needed */ @@ -324,7 +324,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...) default: snprintf(type_str, sizeof(type_str), "%d", type); - ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, type_str, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str); return (false); } @@ -361,7 +361,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...) { ECPGlog("ECPGget_desc line %d: Incorrect number of matches (indicator): %d don't fit into array of %d\n", lineno, ntuples, data_var.ind_arrsize); - ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL); return false; } /* allocate storage if needed */ @@ -404,7 +404,7 @@ ECPGdeallocate_desc(int line, const char *name) return true; } } - ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, name, ECPG_COMPAT_PGSQL); + ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name); return false; } @@ -430,7 +430,7 @@ ECPGallocate_desc(int line, const char *name) { ECPGfree(new->name); ECPGfree(new); - ECPGraise(line, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); return false; } strcpy(new->name, name); @@ -449,7 +449,7 @@ ECPGdescriptor_lvalue(int line, const char *descriptor) return &i->result; } - ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, (char *) descriptor, ECPG_COMPAT_PGSQL); + ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, (char *) descriptor); return NULL; } diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c index 0b4b563d85a..cfeb4e65b0b 100644 --- a/src/interfaces/ecpg/ecpglib/error.c +++ b/src/interfaces/ecpg/ecpglib/error.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.4 2003/08/01 08:21:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.5 2003/08/01 13:53:36 petere Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -11,11 +11,13 @@ #include "extern.h" #include "sqlca.h" + void -ECPGraise(int line, int code, const char *str, int compat) +ECPGraise(int line, int code, const char * sqlstate, const char *str) { struct sqlca_t *sqlca = ECPGget_sqlca(); sqlca->sqlcode = code; + strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate)); switch (code) { @@ -129,23 +131,6 @@ ECPGraise(int line, int code, const char *str, int compat) "Variable is not a character type in line %d.", line); break; - case ECPG_PGSQL: - { - int slen = strlen(str); - - /* strip trailing newline */ - if (slen > 0 && str[slen - 1] == '\n') - slen--; - snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), - "'%.*s' in line %d.", slen, str, line); - if (strncmp(str, "ERROR: Cannot insert a duplicate key", strlen("ERROR: Cannot insert a duplicate key")) == 0) - sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_DUPLICATE_KEY : ECPG_DUPLICATE_KEY; - else if (strncmp(str, "ERROR: More than one tuple returned by a subselect", strlen("ERROR: More than one tuple returned by a subselect")) == 0) - sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_SUBSELECT_NOT_ONE; - - break; - } - case ECPG_TRANS: snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Error in transaction processing in line %d.", line); @@ -169,6 +154,38 @@ ECPGraise(int line, int code, const char *str, int compat) ECPGfree_auto_mem(); } +void +ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat) +{ + struct sqlca_t *sqlca = ECPGget_sqlca(); + + /* copy error message */ + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), + "'%s' in line %d.", + result ? PQresultErrorField(result, 'M') : PQerrorMessage(conn), + line); + sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc); + + /* copy SQLSTATE */ + strncpy(sqlca->sqlstate, + result ? PQresultErrorField(result, 'C') : ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, + sizeof(sqlca->sqlstate)); + + /* assign SQLCODE for backward compatibility */ + if (strncmp(sqlca->sqlstate, "23505", sizeof(sqlca->sqlstate))==0) + sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_DUPLICATE_KEY : ECPG_DUPLICATE_KEY; + if (strncmp(sqlca->sqlstate, "21000", sizeof(sqlca->sqlstate))==0) + sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_SUBSELECT_NOT_ONE; + else + sqlca->sqlcode = ECPG_PGSQL; + + ECPGlog("raising sqlstate %.*s in line %d, '%s'.\n", + sqlca->sqlstate, sizeof(sqlca->sqlstate), line, sqlca->sqlerrm.sqlerrmc); + + /* free all memory we have allocated for the user */ + ECPGfree_auto_mem(); +} + /* print out an error message */ void sqlprint(void) diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c index 8e9545f0ab4..0d4247897d5 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.21 2003/08/01 08:21:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.22 2003/08/01 13:53:36 petere Exp $ */ /* * The aim is to get a simpler inteface to the database routines. @@ -124,7 +124,7 @@ create_statement(int lineno, int compat, int force_indicator, struct connection /* if variable is NULL, the statement hasn't been prepared */ if (var->pointer == NULL) { - ECPGraise(lineno, ECPG_INVALID_STMT, NULL, compat); + ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, NULL); ECPGfree(var); return false; } @@ -351,7 +351,7 @@ ECPGstore_result(const PGresult *results, int act_field, { ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n", stmt->lineno, ntuples, var->arrsize); - ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL); return false; } } @@ -362,7 +362,7 @@ ECPGstore_result(const PGresult *results, int act_field, */ if (var->arrsize == 0) { - ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(stmt->lineno, ECPG_NO_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL); return false; } } @@ -481,12 +481,13 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var, * arrays are not possible unless the attribute is an array too FIXME: * we do not know if the attribute is an array here */ - -/* if (var->arrsize > 1 && ...) +#if 0 + if (var->arrsize > 1 && ...) { - ECPGraise(stmt->lineno, ECPG_ARRAY_INSERT, NULL, compat); + ECPGraise(stmt->lineno, ECPG_ARRAY_INSERT, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL); return false; - }*/ + } +#endif /* * Some special treatment is needed for records since we want their @@ -757,7 +758,7 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var, for (element = 0; element < var->arrsize; element++) sprintf(mallocedval + strlen(mallocedval), "%c,", (((int *) var->value)[element]) ? 't' : 'f'); else - ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size", ECPG_COMPAT_PGSQL); + ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size"); strcpy(mallocedval + strlen(mallocedval) - 1, "]"); } @@ -768,7 +769,7 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var, else if (var->offset == sizeof(int)) sprintf(mallocedval, "'%c'", (*((int *) var->value)) ? 't' : 'f'); else - ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size", ECPG_COMPAT_PGSQL); + ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size"); } *tobeinserted_p = mallocedval; @@ -1021,7 +1022,7 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var, default: /* Not implemented yet */ - ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, (char *) ECPGtype_name(var->type), ECPG_COMPAT_PGSQL); + ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (char *) ECPGtype_name(var->type)); return false; break; } @@ -1034,7 +1035,7 @@ ECPGexecute(struct statement * stmt) { bool status = false; char *copiedquery; - char *errmsg, *cmdstat; + char *cmdstat; PGresult *results; PGnotify *notify; struct variable *var; @@ -1073,7 +1074,7 @@ ECPGexecute(struct statement * stmt) * We have an argument but we dont have the matched up string * in the string */ - ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL); return false; } else @@ -1111,7 +1112,7 @@ ECPGexecute(struct statement * stmt) /* Check if there are unmatched things left. */ if (next_insert(copiedquery) != NULL) { - ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL); return false; } @@ -1121,7 +1122,7 @@ ECPGexecute(struct statement * stmt) { if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) { - ECPGraise(stmt->lineno, ECPG_TRANS, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); return false; } PQclear(results); @@ -1134,9 +1135,8 @@ ECPGexecute(struct statement * stmt) if (results == NULL) { - errmsg = PQerrorMessage(stmt->connection->connection); - ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, errmsg); - ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg, stmt->compat); + ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, PQerrorMessage(stmt->connection->connection)); + ECPGraise_backend(stmt->lineno, NULL, stmt->connection->connection, stmt->compat); } else @@ -1148,8 +1148,6 @@ ECPGexecute(struct statement * stmt) bool clear_result = TRUE; struct sqlca_t *sqlca = ECPGget_sqlca(); - errmsg = PQresultErrorMessage(results); - var = stmt->outlist; switch (PQresultStatus(results)) { @@ -1167,7 +1165,7 @@ ECPGexecute(struct statement * stmt) if (ntuples) ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", stmt->lineno, ntuples); - ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); status = false; break; } @@ -1198,21 +1196,21 @@ ECPGexecute(struct statement * stmt) } else if (!INFORMIX_MODE(stmt->compat)) { - ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); return (false); } } if (status && var != NULL) { - ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); status = false; } break; case PGRES_EMPTY_QUERY: /* do nothing */ - ECPGraise(stmt->lineno, ECPG_EMPTY, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); break; case PGRES_COMMAND_OK: status = true; @@ -1225,13 +1223,13 @@ ECPGexecute(struct statement * stmt) ( !strncmp(cmdstat, "UPDATE", 6) || !strncmp(cmdstat, "INSERT", 6) || !strncmp(cmdstat, "DELETE", 6))) - ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL, stmt->compat); + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); break; case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: case PGRES_BAD_RESPONSE: - ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, errmsg); - ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg, stmt->compat); + ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, PQresultErrorMessage(results)); + ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat); status = false; break; case PGRES_COPY_OUT: @@ -1245,7 +1243,7 @@ ECPGexecute(struct statement * stmt) default: ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", stmt->lineno); - ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg, stmt->compat); + ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat); status = false; break; } @@ -1300,7 +1298,7 @@ ECPGdo(int lineno, int compat, int force_indicator, const char *connection_name, if (con == NULL || con->connection == NULL) { free_statement(stmt); - ECPGraise(lineno, ECPG_NOT_CONN, (con) ? con->name : "<empty>", stmt->compat); + ECPGraise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : "<empty>"); setlocale(LC_NUMERIC, oldlocale); ECPGfree(oldlocale); return false; diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h index 9f16fc24efa..d98f158bacc 100644 --- a/src/interfaces/ecpg/ecpglib/extern.h +++ b/src/interfaces/ecpg/ecpglib/extern.h @@ -98,4 +98,30 @@ PGresult ** bool ECPGstore_result(const PGresult *results, int act_field, const struct statement * stmt, struct variable * var); +/* SQLSTATE values generated or processed by ecpglib (intentionally + * not exported -- users should refer to the codes directly) */ + +#define ECPG_SQLSTATE_NO_DATA "02000" +#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS "07001" +#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS "07002" +#define ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION "07006" +#define ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX "07009" +#define ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION "08001" +#define ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST "08003" +#define ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN "08007" +#define ECPG_SQLSTATE_CARDINALITY_VIOLATION "21000" +#define ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER "22002" +#define ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION "25001" +#define ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION "25P01" +#define ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME "26000" +#define ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME "33000" +#define ECPG_SQLSTATE_INVALID_CURSOR_NAME "34000" +#define ECPG_SQLSTATE_SYNTAX_ERROR "42601" +#define ECPG_SQLSTATE_DATATYPE_MISMATCH "42804" +#define ECPG_SQLSTATE_DUPLICATE_CURSOR "42P03" + +/* implementation-defined internal errors of ecpg */ +#define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR "YE000" +#define ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY "YE001" + #endif /* _ECPG_LIB_EXTERN_H */ diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c index 0a77da4f095..524192d7ca8 100644 --- a/src/interfaces/ecpg/ecpglib/memory.c +++ b/src/interfaces/ecpg/ecpglib/memory.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.3 2003/08/01 08:21:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.4 2003/08/01 13:53:36 petere Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -21,7 +21,7 @@ ECPGalloc(long size, int lineno) if (!new) { - ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); return NULL; } @@ -36,7 +36,7 @@ ECPGrealloc(void *ptr, long size, int lineno) if (!new) { - ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); return NULL; } @@ -50,7 +50,7 @@ ECPGstrdup(const char *string, int lineno) if (!new) { - ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); return NULL; } diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c index 4334844d61d..f42b8f721e7 100644 --- a/src/interfaces/ecpg/ecpglib/misc.c +++ b/src/interfaces/ecpg/ecpglib/misc.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.11 2003/08/01 08:21:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.12 2003/08/01 13:53:36 petere Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -47,7 +47,7 @@ static struct sqlca_t sqlca_init = 0, 0, 0, 0, 0, 0, 0, 0 }, { - 0, 0, 0, 0, 0, 0, 0, 0 + '0', '0', '0', '0', '0' } }; @@ -78,7 +78,7 @@ static struct sqlca_t sqlca = 0, 0, 0, 0, 0, 0, 0, 0 }, { - 0, 0, 0, 0, 0, 0, 0, 0 + '0', '0', '0', '0', '0' } }; #endif @@ -103,7 +103,8 @@ ECPGinit(const struct connection * con, const char *connection_name, const int l ECPGinit_sqlca(sqlca); if (con == NULL) { - ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL", ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST, + connection_name ? connection_name : "NULL"); return (false); } @@ -150,7 +151,7 @@ ECPGstatus(int lineno, const char *connection_name) /* are we connected? */ if (con->connection == NULL) { - ECPGraise(lineno, ECPG_NOT_CONN, con->name, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, con->name); return false; } @@ -179,7 +180,7 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction) { if ((res = PQexec(con->connection, transaction)) == NULL) { - ECPGraise(lineno, ECPG_TRANS, NULL, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL); return FALSE; } PQclear(res); diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 76c58d1238c..5569d36da07 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.7 2003/08/01 08:21:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.8 2003/08/01 13:53:36 petere Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -120,7 +120,7 @@ ECPGdeallocate(int lineno, int c, char *name) } if (!ret) - ECPGraise(lineno, ECPG_INVALID_STMT, name, ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name); return ret; } diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index e99c50bc866..3f510fbfa60 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -6,7 +6,8 @@ #ifndef _ECPGLIB_H #define _ECPGLIB_H -#include <stdio.h> +#include "postgres_fe.h" +#include "libpq-fe.h" #ifndef __BEOS__ #ifndef __cplusplus @@ -71,7 +72,8 @@ bool ECPGdo_descriptor(int line, const char *connection, const char *descriptor, const char *query); bool ECPGdeallocate_desc(int line, const char *name); bool ECPGallocate_desc(int line, const char *name); -void ECPGraise(int line, int code, const char *str, int); +void ECPGraise(int line, int code, const char *sqlstate, const char *str); +void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat); bool ECPGget_desc_header(int, char *, int *); bool ECPGget_desc(int, char *, int,...); diff --git a/src/interfaces/ecpg/include/sqlca.h b/src/interfaces/ecpg/include/sqlca.h index 8af23921d78..37370b6f605 100644 --- a/src/interfaces/ecpg/include/sqlca.h +++ b/src/interfaces/ecpg/include/sqlca.h @@ -50,7 +50,7 @@ struct sqlca_t /* 6: empty */ /* 7: empty */ - char sqlext[8]; + char sqlstate[5]; }; struct sqlca_t *ECPGget_sqlca(void); diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index 7e7f1fee07d..357779863d0 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -1,10 +1,10 @@ -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.38 2003/07/01 12:40:52 meskes Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.39 2003/08/01 13:53:36 petere Exp $ subdir = src/interfaces/ecpg/test top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -override CPPFLAGS := -I$(srcdir)/../include $(CPPFLAGS) +override CPPFLAGS := -I$(srcdir)/../include -I$(libpq_srcdir) $(CPPFLAGS) ECPG = ../preproc/ecpg -I$(srcdir)/../include -- GitLab