From 4f7df90db0f9c3ed61d12a1d7a9efe0de37f4fe3 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut 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 | 1181 ++++++++++++++++++---- src/backend/commands/portalcmds.c | 5 +- src/interfaces/ecpg/compatlib/Makefile | 4 +- src/interfaces/ecpg/ecpglib/connect.c | 180 +--- 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, 1147 insertions(+), 453 deletions(-) diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml index ee38633928..7c4de83b30 100644 --- a/doc/src/sgml/ecpg.sgml +++ b/doc/src/sgml/ecpg.sgml @@ -1,5 +1,5 @@ @@ -37,15 +37,16 @@ $Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.46 2003/08/01 03:10:04 momjia Embedded SQL has advantages over other methods for handling SQL commands from C code. First, it takes care of the tedious passing of information to and from - variables in your C program. Secondly, embedded - SQL in C is defined in the - SQL standard and supported by many other - SQL databases. The PostgreSQL - implementation is designed to match this standard as much as - possible, and it is usually possible to port embedded - SQL programs written for other - SQL databases to PostgreSQL - with relative ease. + variables in your C program. Second, the SQL + code in the program is checked at build time for syntactical + correctness. Third, embedded SQL in C is + specified in the SQL standard and supported by + many other SQL database systems. The + PostgreSQL implementation is designed to match this + standard as much as possible, and it is usually possible to port + embedded SQL programs written for other SQL + databases to PostgreSQL with relative + ease. @@ -100,13 +101,13 @@ EXEC SQL CONNECT TO target AS - character variable + an SQL string literal containing one of the above forms - character string + a reference to a character variable containing one of the above forms (see examples) @@ -116,6 +117,16 @@ EXEC SQL CONNECT TO target AS + + 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 DEFAULT 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. @@ -146,15 +157,47 @@ EXEC SQL CONNECT TO target AS - The username and - password may be an SQL name, a - character variable, or a character string. + + As above, the parameters username and + password may be an SQL identifier, an + SQL string literal, or a reference to a character variable. The connection-name 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). + + + + Here are some examples of CONNECT statements: + +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; + + 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. + + + + 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. @@ -194,6 +237,14 @@ EXEC SQL DISCONNECT connection; + + If no connection name is specified, the current connection is + closed. + + + + It is good style that an application always explicitly disconnect + from every connection it opened. @@ -231,14 +282,14 @@ EXEC SQL COMMIT; - Singleton Select: + Single-row Select: EXEC SQL SELECT foo INTO :FooBar FROM table1 WHERE ascii = 'doodad'; - Select using Cursors: + Select using cursors: EXEC SQL DECLARE foo_bar CURSOR FOR SELECT number, ascii FROM foo @@ -264,8 +315,8 @@ EXEC SQL COMMIT; The tokens of the form :something are host variables, 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 . @@ -281,65 +332,512 @@ EXEC SQL COMMIT; - - Passing Data + + Choosing a Connection - 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 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. - This section starts with + The first option is to explicitly choose a connection for each SQL + statement, for example + +EXEC SQL AT connection-name SELECT ...; + + This option is particularly suitable if the application needs to + use several connections in mixed order. + + + + The second option is to execute a statement to switch the current + connection. That statement is: + +SET CONNECTION connection-name; + + This option is particularly convenient if many statements are to be + executed on the same connection. + + + + + Using Host Variables + + + In 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 + host variables. + + + + Overview + + + 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: + +INSERT INTO sometable VALUES (:v1, 'foo', :v2); + + This statements refers to two C variables named + v1 and v2 and also uses a + regular SQL string literal, to illustrate that you are not + restricted to use one kind of data or the other. + + + + 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 + host variables. + + + + + Declare Sections + + + 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. + + + + This section starts with EXEC SQL BEGIN DECLARE SECTION; - and ends with + and ends with EXEC SQL END DECLARE SECTION; - Between those lines, there must be normal C variable declarations, such as + Between those lines, there must be normal C variable declarations, + such as int x; char foo[16], bar[16]; - + You can have as many declare sections in a program as you like. + - - 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 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 definition of a structure or union also must be listed inside a - DECLARE section. Otherwise the preprocessor cannot - handle these types since it does not know the definition. - + + The definition of a structure or union also must be listed inside + a DECLARE section. Otherwise the preprocessor cannot + handle these types since it does not know the definition. + - - The special types VARCHAR and VARCHAR2 - are converted into a named struct for every variable. A - declaration like + + The special types VARCHAR and VARCHAR2 + are converted into a named struct for every variable. A + declaration like VARCHAR var[180]; - is converted into + is converted into struct varchar_var { int len; char arr[180]; } var; - This structure is suitable for interfacing with SQL datums of type - varchar. + This structure is suitable for interfacing with SQL datums of type + varchar. + + + + + <command>SELECT INTO</command> and <command>FETCH INTO</command> + + + 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 SELECT and + FETCH. These commands have a special + INTO clause that specifies which host variables + the retrieved values are to be stored in. + + + + Here is an example: + +/* + * 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; + + So the INTO clause appears between the select + list and the FROM clause. The number of + elements in the select list and the list after + INTO (also called the target list) must be + equal. + + + + Here is an example using the command FETCH: + +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 (...); + + Here the INTO clause appears after all the + normal clauses. + + + + 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. + + + + + Indicators + + + 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 + indicator 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: + +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; + + The indicator variable val_ind will be zero if + the value was not null, and it will be negative if the value was + null. + + + + 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. + + + + + + Dynamic SQL + + + 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. - To use a properly declared C variable in an SQL statement, write - :varname where an expression is - expected. See the previous section for some examples. + The simplest way to execute an arbitrary SQL statement is to use + the command EXECUTE IMMEDIATE. For example: + +EXEC SQL BEGIN DECLARE SECTION; +const char *stmt = "CREATE TABLE test1 (...);"; +EXEC SQL END DECLARE SECTION; + +EXECUTE IMMEDIATE :stmt; + + You may not execute statements that retrieve data (e.g., + SELECT) this way. + + + + 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: + +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'; + + If the statement you are executing returns values, then add an + INTO clause: + +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; + + An EXECUTE command may have an + INTO clause, a USING clause, + both, or neither. + + + + When you don't need the prepared statement anymore, you should + deallocate it: + +EXEC SQL DEALLOCATE PREPARE name; + + + + + + Using SQL Descriptor Areas + + + An SQL descriptor area is a more sophisticated method for + processing the result of a SELECT or + FETCH 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. + + + + 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. + + + + Before you can use an SQL descriptor area, you need to allocate one: + +EXEC SQL ALLOCATE DESCRIPTOR identifier; + + The identifier serves as the variable name of the + descriptor area. The scope of the allocated descriptor is WHAT?. + When you don't need the descriptor anymore, you should deallocate + it: + +EXEC SQL DEALLOCATE DESCRIPTOR identifier; + + + + + To use a descriptor area, specify it as the storage target in an + INTO clause, instead of listing host variables: + +EXEC SQL FETCH NEXT FROM mycursor INTO DESCRIPTOR mydesc; + + + + + 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: + +EXEC SQL GET DESCRIPTOR name :hostvar = field; + + Currently, there is only one header field defined: + COUNT, 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: + +EXEC SQL GET DESCRIPTOR name VALUE num :hostvar = field; + + num can be a literal integer or a host + variable containing an integer. Possible fields are: + + + + CARDINALITY (integer) + + + number of rows in the result set + + + + + + DATA + + + actual data item (therefore, the data type of this field + depends on the query) + + + + + + DATETIME_INTERVAL_CODE (integer) + + + ? + + + + + + DATETIME_INTERVAL_PRECISION (integer) + + + not implemented + + + + + + INDICATOR (integer) + + + the indicator (indicating a null value or a value truncation) + + + + + + KEY_MEMBER (integer) + + + not implemented + + + + + + LENGTH (integer) + + + length of the datum in characters + + + + + + NAME (string) + + + name of the column + + + + + + NULLABLE (integer) + + + not implemented + + + + + + OCTET_LENGTH (integer) + + + length of the character representation of the datum in bytes + + + + + + PRECISION (integer) + + + precision (for type numeric) + + + + + + RETURNED_LENGTH (integer) + + + length of the datum in characters + + + + + + RETURNED_OCTET_LENGTH (integer) + + + length of the character representation of the datum in bytes + + + + + + SCALE (integer) + + + scale (for type numeric) + + + + + + TYPE (integer) + + + numeric code of the data type of the column + + + + @@ -347,35 +845,198 @@ struct varchar_var { int len; char arr[180]; } var; Error Handling - 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: - -EXEC SQL WHENEVER sqlerror sqlprint; - - or - -EXEC SQL WHENEVER not found sqlprint; - - This error handling remains enabled throughout the entire program. + This section describes how you can handle exceptional conditions + and warnings in an embedded SQL program. There are several + nonexclusive facilities for this. - - - This is not an exhaustive example of usage - for the EXEC SQL WHENEVER statement. Further - examples of usage may be found in SQL manuals (e.g., - The LAN TIMES Guide to SQL by Groff and Weinberg). - - + + Setting Callbacks - - For a more powerful error handling, the embedded SQL interface - provides a struct and a variable with the name - sqlca as follows: + + One simple method to catch errors and warnings is to set a + specific action to be executed whenever a particular condition + occurs. In general: -struct sqlca +EXEC SQL WHENEVER condition action; + + + + + condition can be one of the following: + + + + SQLERROR + + + The specified action is called whenever an error occurs during + the execution of an SQL statement. + + + + + + SQLWARNING + + + The specified action is called whenever a warning occurs + during the execution of an SQL statement. + + + + + + NOT FOUND + + + 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.) + + + + + + + + action can be one of the following: + + + + CONTINUE + + + This effectively means that the condition is ignored. This is + the default. + + + + + + GOTO label + GO TO label + + + Jump to the specified label (using a C goto + statement). + + + + + + SQLPRINT + + + Print a message to standard error. This is useful for simple + programs or during prototyping. The details of the message + cannot be configured. + + + + + + STOP + + + Call exit(1), which will terminate the + program. + + + + + + BREAK + + + Execute the C statement break. This should + only be used in loops or switch statements. + + + + + + CALL name (args) + DO name (args) + + + Call the specified C functions with the specified arguments. + + + + + + The SQL standard only provides for the actions + CONTINUE and GOTO (and + GO TO). + + + + 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. + +EXEC SQL WHENEVER SQLWARNING SQLPRINT; +EXEC SQL WHENEVER SQLERROR STOP; + + + + + The statement EXEC SQL WHENEVER 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 + EXEC SQL WHENEVER 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. + +/* + * WRONG + */ +int main(int argc, char *argv[]) +{ + ... + if (verbose) { + EXEC SQL WHENEVER SQLWARNING SQLPRINT; + } + ... + EXEC SQL SELECT ...; + ... +} + + + +/* + * WRONG + */ +int main(int argc, char *argv[]) +{ + ... + set_error_handler(); + ... + EXEC SQL SELECT ...; + ... +} + +static void set_error_handler(void) +{ + EXEC SQL WHENEVER SQLERROR STOP; +} + + + + + + sqlca + + + For a more powerful error handling, the embedded SQL interface + provides a global variable with the name sqlca + that has the following structure: + +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; - (Many of the empty fields may be used in a future release.) - + (In a multithreaded program, every thread automatically gets its + own copy of sqlca. This works similar to the + handling of the standard C global variable + errno.) + - - If no error occurred in the last SQL statement, - sqlca.sqlcode will be 0 - (ECPG_NO_ERROR). If sqlca.sqlcode 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. - + + sqlca covers both warnings and errors. If + multiple warnings or errors occur during the execution of a + statement, then sqlca will only contain + information about the last one. + - - sqlca.sqlerrm.sqlerrmc will contain a string - that describes the error. The string ends with the line number in - the source file. - + + If no error occurred in the last SQL statement, + sqlca.sqlcode will be 0 and + sqlca.sqlstate will be + "00000". If a warning or error occured, then + sqlca.sqlcode will be negative and + sqlca.sqlstate will be different from + "00000". A positive + sqlca.sqlcode indicates a harmless condition, + such as that the last query returned zero rows. + sqlcode and sqlstate are two + different error code schemes; details appear below. + - - These are the errors that can occur: + + If the last SQL statement was successful, then + sqlca.sqlerrd[1] contains the OID of the + processed row, if applicable, and + sqlca.sqlerrd[2] contains the number of + processed or returned rows, if applicable to the command. + - - - -12: Out of memory in line %d. - - - Should not normally occur. This indicates your virtual memory - is exhausted. + + In case of an error or warning, + sqlca.sqlerrm.sqlerrmc will contain a string + that describes the error. The field + sqlca.sqlerrm.sqlerrml contains the length of + the error message that is stored in + sqlca.sqlerrm.sqlerrmc (the result of + strlen(), not really interesting for a C + programmer). + + + + In case of a warning, sqlca.sqlwarn[2] is set + to W. (In all other cases, it is set to + something different from W.) If + sqlca.sqlwarn[1] is set to + W, then a value was truncated when it was + stored in a host variable. sqlca.sqlwarn[0] is + set to W if any of the other elements are set + to indicate a warning. + + + + The fields sqlcaid, + sqlcabc, + sqlerrp, and the remaining elements of + sqlerrd and + sqlwarn currently contain no useful + information. + + + + The structure sqlca 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. + + + + + <literal>SQLSTATE</literal> vs <literal>SQLCODE</literal> + + + The fields sqlca.sqlstate and + sqlca.sqlcode are two different schemes that + provide error codes. Both are specified in the SQL standard, but + SQLCODE 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 + SQLSTATE. + + + + SQLSTATE is a five-character array. The five + characters contain digits or upper-case letters that represent + codes of various error and warning conditions. + SQLSTATE 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 + 00000. Further information about the codes can + be found XXX. The SQLSTATE codes are for the + most part defined in the SQL standard. The PostgreSQL server + natively supports SQLSTATE error codes; + therefore a high degree of consistency can be achieved by using + this error code scheme throughout all applications. + + + + SQLCODE, 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 + SQLCODE 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 + SQLSTATE scheme, the corresponding + SQLSTATE 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 + SQLSTATE listing in XXX in each case. + + + + These are the assigned SQLCODE values: + + + + -12 (ECPG_OUT_OF_MEMORY) + + + Indicates that your virtual memory is exhausted. (SQLSTATE + YE001) - -200 (ECPG_UNSUPPORTED): Unsupported type %s on line %d. + -200 (ECPG_UNSUPPORTED) - 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) - -201 (ECPG_TOO_MANY_ARGUMENTS): Too many arguments line %d. + -201 (ECPG_TOO_MANY_ARGUMENTS) - 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 INTO - :var1, :var2 list. + This means that the command specified more host variables than + the command expected. (SQLSTATE 07001 or 07002) - -202 (ECPG_TOO_FEW_ARGUMENTS): Too few arguments line %d. + -202 (ECPG_TOO_FEW_ARGUMENTS) - This means that the server has returned fewer arguments than we - have host variables. Perhaps you have too many host variables - in the INTO :var1,:var2 list. - - - - - -203 (ECPG_TOO_MANY_MATCHES): Too many matches line %d. - - - This means the query has returned multiple rows but the - variables specified are not arrays. The - SELECT command was not unique. + This means that the command specified fewer host variables than + the command expected. (SQLSTATE 07001 or 07002) - -204 (ECPG_INT_FORMAT): Not correctly formatted int type: %s line %d. + -203 (ECPG_TOO_MANY_MATCHES) - This means the host variable is of type int and - the field in the PostgreSQL database - is of another type and contains a value that cannot be - interpreted as an int. The library uses - strtol() for this conversion. + 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) - -205 (ECPG_UINT_FORMAT): Not correctly formatted unsigned type: %s line %d. + -204 (ECPG_INT_FORMAT) - This means the host variable is of type unsigned - int and the field in the - PostgreSQL database is of another - type and contains a value that cannot be interpreted as an - unsigned int. The library uses - strtoul() for this conversion. + The host variable is of type int and the datum in + the database is of a different type and contains a value that + cannot be interpreted as an int. The library uses + strtol() for this conversion. (SQLSTATE + 42804) - -206 (ECPG_FLOAT_FORMAT): Not correctly formatted floating-point type: %s line %d. + -205 (ECPG_UINT_FORMAT) - This means the host variable is of type float and - the field in the PostgreSQL database - is of another type and contains a value that cannot be - interpreted as a float. The library uses - strtod() for this conversion. + The host variable is of type unsigned int and the + datum in the database is of a different type and contains a + value that cannot be interpreted as an unsigned + int. The library uses strtoul() + for this conversion. (SQLSTATE 42804) - -207 (ECPG_CONVERT_BOOL): Unable to convert %s to bool on line %d. + -206 (ECPG_FLOAT_FORMAT) + + + The host variable is of type float and the datum + in the database is of another type and contains a value that + cannot be interpreted as a float. The library + uses strtod() for this conversion. + (SQLSTATE 42804) + + + + + + -207 (ECPG_CONVERT_BOOL) This means the host variable is of type bool and - the field in the PostgreSQL database - is neither 't' nor 'f'. + the datum in the database is neither 't' nor + 'f'. (SQLSTATE 42804) - -208 (ECPG_EMPTY): Empty query line %d. + -208 (ECPG_EMPTY) - 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) - -209 (ECPG_MISSING_INDICATOR): NULL value without indicator in line %d. + -209 (ECPG_MISSING_INDICATOR) A null value was returned and no null indicator variable was - supplied. + supplied. (SQLSTATE 22002) - -210 (ECPG_NO_ARRAY): Variable is not an array in line %d. + -210 (ECPG_NO_ARRAY) An ordinary variable was used in a place that requires an - array. + array. (SQLSTATE 42804) - -211 (ECPG_DATA_NOT_ARRAY): Data read from backend is not an array in line %d. + -211 (ECPG_DATA_NOT_ARRAY) The database returned an ordinary variable in a place that - requires array value. + requires array value. (SQLSTATE 42804) - -220 (ECPG_NO_CONN): No such connection %s in line %d. + -220 (ECPG_NO_CONN) The program tried to access a connection that does not exist. + (SQLSTATE 08003) - -221 (ECPG_NOT_CONN): Not connected in line %d. + -221 (ECPG_NOT_CONN) The program tried to access a connection that does exist but is - not open. + not open. (This is an internal error.) (SQLSTATE YE002) - -230 (ECPG_INVALID_STMT): Invalid statement name %s in line %d. + -230 (ECPG_INVALID_STMT) The statement you are trying to use has not been prepared. + (SQLSTATE 26000) - -240 (ECPG_UNKNOWN_DESCRIPTOR): Descriptor %s not found in line %d. + -240 (ECPG_UNKNOWN_DESCRIPTOR) - 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) - -241 (ECPG_INVALID_DESCRIPTOR_INDEX): Descriptor index out of range in line %d. + -241 (ECPG_INVALID_DESCRIPTOR_INDEX) - The descriptor index specified was out of range. + The descriptor index specified was out of range. (SQLSTATE + 07009) - -242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM): Unknown descriptor item %s in line %d. + -242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM) - 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) - -243 (ECPG_VAR_NOT_NUMERIC): Variable is not a numeric type in line %d. + -243 (ECPG_VAR_NOT_NUMERIC) - 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) - -244 (ECPG_VAR_NOT_CHAR): Variable is not a character type in line %d. + -244 (ECPG_VAR_NOT_CHAR) - 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) - -400 (ECPG_PGSQL): '%s' in line %d. + -400 (ECPG_PGSQL) - Some PostgreSQL error. The message - contains the error message from the + Some error caused by the PostgreSQL + server. The message contains the error message from the PostgreSQL server. - -401 (ECPG_TRANS): Error in transaction processing line %d. + -401 (ECPG_TRANS) - The PostgreSQL server signaled that we cannot - start, commit, or rollback the transaction. + The PostgreSQL server signaled that + we cannot start, commit, or rollback the transaction. + (SQLSTATE 08007) - -402 (ECPG_CONNECT): Could not connect to database %s in line %d. + -402 (ECPG_CONNECT) - The connection attempt to the database did not work. + The connection attempt to the database did not succeed. + (SQLSTATE 08001) - 100 (ECPG_NOT_FOUND): Data not found line %d. + 100 (ECPG_NOT_FOUND) - This is a normal 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) + @@ -749,13 +1504,6 @@ EXEC SQL INCLUDE filename; in the arguments specified for output. - - ecpg is thread-safe if it is compiled using - the --enable-thread-safety configure - command-line option. (You might need to use other threading - command-line options to compile your client code.) - - The preprocessor program is called ecpg and is included in a normal PostgreSQL installation. @@ -811,6 +1559,13 @@ ECPG = ecpg The complete syntax of the ecpg command is detailed in . + + + ecpg is thread-safe if it is compiled using + the diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 978f05ef92..cf4a063871 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 5bd527d98a..83d9193dfd 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 44b088481c..1030b041ab 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,150 +154,46 @@ ECPGsetconn(int lineno, const char *connection_name) return true; } + static void -ECPGnoticeProcessor_raise(int code, 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(); - sqlca->sqlcode = code; + + int sqlcode; + + /* these are not warnings */ + if (strncmp(sqlstate, "00", 2)==0) + 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; + + 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); - /* 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); + ECPGlog("raising sqlcode %d\n", sqlcode); } -/* - * I know this is a mess, but we can't redesign the backend - */ - -static void -ECPGnoticeProcessor(void *arg, const char *message) -{ - 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; - } - - /* 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); - 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; - - /* 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 */ - sqlca->sqlwarn[2] = 'W'; - sqlca->sqlwarn[0] = 'W'; -} /* this contains some quick hacks, needs to be cleaned up, but it works */ bool @@ -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 : "", ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : ""); 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 : "", ECPG_COMPAT_PGSQL); + ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : ""); 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 361dfa73a4..14e459d1b6 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 bdb308810d..0beae2d93a 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 0b4b563d85..cfeb4e65b0 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 8e9545f0ab..0d4247897d 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 : "", stmt->compat); + ECPGraise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ""); 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 9f16fc24ef..d98f158bac 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 0a77da4f09..524192d7ca 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 4334844d61..f42b8f721e 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 76c58d1238..5569d36da0 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 e99c50bc86..3f510fbfa6 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 +#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 8af23921d7..37370b6f60 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 7e7f1fee07..357779863d 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