<acronym>C</acronym>. It also works with <acronym>C++</acronym>, but
- it does not recognize all <acronym>C++</acronym> constructs yet.
+ it does not recognize all <acronym>C++</acronym> constructs yet.
</para>
<para>
<para>
An embedded SQL program consists of code written in an ordinary
programming language, in this case C, mixed with SQL commands in
- specially marked sections. To build the program, the source code
+ specially marked sections. To build the program, the source code (<filename>*.pgc</filename>)
is first passed through the embedded SQL preprocessor, which converts it
- to an ordinary C program, and afterwards it can be processed by a C
- compiler.
+ to an ordinary C program (<filename>*.c</filename>), and afterwards it can be processed by a C
+ compiler. (For details about the compiling and linking see <xref linkend="ecpg-process">).
+ Converted ECPG applications call functions in the libpq library
+ through the embedded SQL library (ecpglib), and communicate with
+ the PostgreSQL server using the normal frontend-backend protocol.
</para>
<para>
</sect1>
<sect1 id="ecpg-connect">
- <title>Connecting to the Database Server</title>
+ <title>Managing Database Connections</title>
+
+ <para>
+ This section describes how to open, close, and switch database
+ connections.
+ </para>
+
+ <sect2 id="ecpg-connecting">
+ <title>Connecting to the database server</title>
<para>
One connects to a database using the following statement:
example above to encapsulate the connection target string
somewhere.
</para>
- </sect1>
+ </sect2>
+
+ <sect2 id="ecpg-set-connection">
+ <title>Choosing a connection</title>
+
+ <para>
+ SQL statements in embedded SQL programs are by default 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>
+ If your application uses multiple threads of execution, they cannot share a
+ connection concurrently. You must either explicitly control access to the connection
+ (using mutexes) or use a connection for each thread. If each thread uses its own connection,
+ you will need to use the AT clause to specify which connection the thread will use.
+ </para>
+
+ <para>
+ The second option is to execute a statement to switch the current
+ connection. That statement is:
+<programlisting>
+EXEC SQL SET CONNECTION <replaceable>connection-name</replaceable>;
+</programlisting>
+ This option is particularly convenient if many statements are to be
+ executed on the same connection. It is not thread-aware.
+ </para>
+
+ <para>
+ Here is an example program managing multiple database connections:
+<programlisting><![CDATA[
+#include <stdio.h>
+
+EXEC SQL BEGIN DECLARE SECTION;
+ char dbname[1024];
+EXEC SQL END DECLARE SECTION;
+
+int
+main()
+{
+ EXEC SQL CONNECT TO testdb1 AS con1 USER testuser;
+ EXEC SQL CONNECT TO testdb2 AS con2 USER testuser;
+ EXEC SQL CONNECT TO testdb3 AS con3 USER testuser;
- <sect1 id="ecpg-disconnect">
- <title>Closing a Connection</title>
+ /* This query would be executed in the last opened database "testdb3". */
+ EXEC SQL SELECT current_database() INTO :dbname;
+ printf("current=%s (should be testdb3)\n", dbname);
+
+ /* Using "AT" to run a query in "testdb2" */
+ EXEC SQL AT con2 SELECT current_database() INTO :dbname;
+ printf("current=%s (should be testdb2)\n", dbname);
+
+ /* Switch the current connection to "testdb1". */
+ EXEC SQL SET CONNECTION con1;
+
+ EXEC SQL SELECT current_database() INTO :dbname;
+ printf("current=%s (should be testdb1)\n", dbname);
+
+ EXEC SQL DISCONNECT ALL;
+ return 0;
+}
+]]></programlisting>
+
+ This example would produce this output:
+<screen>
+current=testdb3 (should be testdb3)
+current=testdb2 (should be testdb2)
+current=testdb1 (should be testdb1)
+</screen>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-disconnect">
+ <title>Closing a connection</title>
<para>
To close a connection, use the following statement:
It is good style that an application always explicitly disconnect
from every connection it opened.
</para>
+ </sect2>
+
</sect1>
<sect1 id="ecpg-commands">
Below are some examples of how to do that.
</para>
+ <sect2 id="ecpg-executing">
+ <title>Executing SQL statements</title>
+
<para>
Creating a table:
<programlisting>
</programlisting>
</para>
+ <para>
+ Updates:
+<programlisting>
+EXEC SQL UPDATE foo
+ SET ascii = 'foobar'
+ WHERE number = 9999;
+EXEC SQL COMMIT;
+</programlisting>
+ </para>
+
+ <para>
+ <literal>SELECT</literal> statements that return a single result
+ row can also be executed using
+ <literal>EXEC SQL</literal> directly. To handle result sets with
+ multiple rows, an application has to use a cursor;
+ see <xref linkend="ecpg-cursors"> below. (As a special case, an
+ application can fetch multiple rows at once into an array host
+ variable; see <xref linkend="ecpg-variables-arrays">.)
+ </para>
+
<para>
Single-row select:
<programlisting>
</programlisting>
</para>
+ <para>
+ Also, a configuration parameter can be retreived with the
+ <literal>SHOW</literal> command:
+<programlisting>
+EXEC SQL SHOW search_path INTO :var;
+</programlisting>
+ </para>
+
+ <para>
+ 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 <xref
+ linkend="ecpg-variables">.
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-cursors">
+ <title>Using cursors</title>
+
+ <para>
+ To retrieve a result set holding multiple rows, an application has
+ to declare a cursor and fetch each row from the cursor. The steps
+ to use a cursor are the following: declare a cursor, open it, fetch
+ a row from the cursor, repeat, and finally close it.
+ </para>
+
<para>
Select using cursors:
<programlisting>
</para>
<para>
- Updates:
-<programlisting>
-EXEC SQL UPDATE foo
- SET ascii = 'foobar'
- WHERE number = 9999;
-EXEC SQL COMMIT;
-</programlisting>
+ For more details about declaration of the cursor,
+ see <xref linkend="ecpg-sql-declare">, and
+ see <xref linkend="sql-fetch"> for <literal>FETCH</literal> command
+ details.
</para>
- <para>
- 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 <xref
- linkend="ecpg-variables">.
- </para>
+ <note>
+ <para>
+ The ECPG <command>DECLARE</command> command does not actually
+ cause a statement to be sent to the PostgreSQL backend. The
+ cursor is opened in the backend (using the
+ backend's <command>DECLARE</command> command) at the point when
+ the <command>OPEN</command> command is executed.
+ </para>
+ </note>
+ </sect2>
+
+ <sect2 id="ecpg-transactions">
+ <title>Managing transactions</title>
<para>
In the default mode, statements are committed only when
<command>EXEC SQL COMMIT</command> is issued. The embedded SQL
interface also supports autocommit of transactions (similar to
- <application>libpq</> behavior) via the <option>-t</option> command-line
- option to <command>ecpg</command> (see below) or via the <literal>EXEC SQL
- SET AUTOCOMMIT TO ON</literal> statement. In autocommit mode, each
- command is automatically committed unless it is inside an explicit
- transaction block. This mode can be explicitly turned off using
- <literal>EXEC SQL SET AUTOCOMMIT TO OFF</literal>.
+ <application>libpq</> behavior) via the <option>-t</option>
+ command-line option to <command>ecpg</command> (see <xref
+ linkend="app-ecpg">) or via the <literal>EXEC SQL SET AUTOCOMMIT TO
+ ON</literal> statement. In autocommit mode, each command is
+ automatically committed unless it is inside an explicit transaction
+ block. This mode can be explicitly turned off using <literal>EXEC
+ SQL SET AUTOCOMMIT TO OFF</literal>.
</para>
- </sect1>
- <sect1 id="ecpg-set-connection">
- <title>Choosing a Connection</title>
+ <para>
+ The following transaction management commands are available:
- <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>
+ <variablelist>
+ <varlistentry>
+ <term><literal>EXEC SQL COMMIT</literal></term>
+ <listitem>
+ <para>
+ Commit an in-progress transaction.
+ </para>
+ </listitem>
+ </varlistentry>
- <para>
- The first option is to explicitly choose a connection for each SQL
- statement, for example:
+ <varlistentry>
+ <term><literal>EXEC SQL ROLLBACK</literal></term>
+ <listitem>
+ <para>
+ Roll back an in-progress transaction.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>EXEC SQL SET AUTOCOMMIT TO ON</literal></term>
+ <listitem>
+ <para>
+ Enable autocommit mode.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET AUTOCOMMIT TO OFF</literal></term>
+ <listitem>
+ <para>
+ Disable autocommit mode. This is the default.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-prepared">
+ <title>Prepared statements</title>
+
+ <para>
+ When the values to be passed to an SQL statement are not known at
+ compile time, or the same statement is going to be used many
+ times, then prepared statements can be useful.
+ </para>
+
+ <para>
+ The statement is prepared using the
+ command <literal>PREPARE</literal>. For the values that are not
+ known yet, use the
+ placeholder <quote><literal>?</literal></quote>:
<programlisting>
-EXEC SQL AT <replaceable>connection-name</replaceable> SELECT ...;
+EXEC SQL PREPARE stmt1 FROM "SELECT oid, datname FROM pg_database WHERE oid = ?";
</programlisting>
- This option is particularly suitable if the application needs to
- use several connections in mixed order.
- </para>
+ </para>
- <para>
- If your application uses multiple threads of execution, they cannot share a
- connection concurrently. You must either explicitly control access to the connection
- (using mutexes) or use a connection for each thread. If each thread uses its own connection,
- you will need to use the AT clause to specify which connection the thread will use.
- </para>
+ <para>
+ If a statement returns a single row, the application can
+ call <literal>EXECUTE</literal> after
+ <literal>PREPARE</literal> to execute the statement, supplying the
+ actual values for the placeholders with a <literal>USING</literal>
+ clause:
+<programlisting>
+EXEC SQL EXECUTE stmt1 INTO :dboid, :dbname USING 1;
+</programlisting>
+ </para>
- <para>
- The second option is to execute a statement to switch the current
- connection. That statement is:
+ <para>
+ If a statement return multiple rows, the application can use a
+ cursor declared based on the prepared statement. To bind input
+ parameters, the cursor must be opened with
+ a <literal>USING</literal> clause:
<programlisting>
-EXEC SQL SET CONNECTION <replaceable>connection-name</replaceable>;
+EXEC SQL PREPARE stmt1 FROM "SELECT oid,datname FROM pg_database WHERE oid > ?";
+EXEC SQL DECLARE foo_bar CURSOR FOR stmt1;
+
+/* when end of result set reached, break out of while loop */
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+EXEC SQL OPEN foo_bar USING 100;
+...
+while (1)
+{
+ EXEC SQL FETCH NEXT FROM foo_bar INTO :dboid, :dbname;
+ ...
+}
+EXEC SQL CLOSE foo_bar;
</programlisting>
- This option is particularly convenient if many statements are to be
- executed on the same connection. It is not thread-aware.
- </para>
+ </para>
+
+ <para>
+ When you don't need the prepared statement anymore, you should
+ deallocate it:
+<programlisting>
+EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;
+</programlisting>
+ </para>
+
+ <para>
+ For more details about <literal>PREPARE</literal>,
+ see <xref linkend="ecpg-sql-prepare">. Also
+ see <xref linkend="ecpg-dynamic"> for more details about using
+ placeholders and input parameters.
+ </para>
+ </sect2>
</sect1>
<sect1 id="ecpg-variables">
variables</firstterm>.
</para>
- <sect2>
+ <para>
+ Another way to exchange values between PostgreSQL backends and ECPG
+ applications is the use of SQL descriptors, described
+ in <xref linkend="ecpg-descriptors">.
+ </para>
+
+ <sect2 id="ecpg-variables-overview">
<title>Overview</title>
<para>
</para>
</sect2>
- <sect2>
+ <sect2 id="ecpg-declare-sections">
<title>Declare Sections</title>
<para>
</para>
</sect2>
- <sect2>
- <title>Different types of host variables</title>
+ <sect2 id="ecpg-retrieving">
+ <title>Retrieving query results</title>
+
<para>
- As a host variable you can also use arrays, typedefs, structs and
- pointers. Moreover there are special types of host variables that exist
- only in ECPG.
+ 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.
+ <command>SELECT</command> is used for a query that returns only
+ single row, and <command>FETCH</command> is used for a query that
+ returns multiple rows, using a cursor.
</para>
<para>
- A few examples on host variables:
- <variablelist>
- <varlistentry>
- <term>Arrays</term>
- <listitem>
- <para>
- One of the most common uses of an array declaration is probably the
- allocation of a char array as in:
+ Here is an example:
<programlisting>
+/*
+ * assume this table:
+ * CREATE TABLE test1 (a int, b varchar(50));
+ */
+
EXEC SQL BEGIN DECLARE SECTION;
- char str[50];
+int v1;
+VARCHAR v2;
EXEC SQL END DECLARE SECTION;
+
+ ...
+
+EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;
</programlisting>
- Note that you have to take care of the length for yourself. If you use
- this host variable as the target variable of a query which returns a
- string with more than 49 characters, a buffer overflow occurs.
- </para>
- </listitem>
- </varlistentry>
+ 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>
- <varlistentry>
- <term>Typedefs</term>
- <listitem>
- <para>
- Use the <literal>typedef</literal> keyword to map new types to already
- existing types.
+ <para>
+ Here is an example using the command <command>FETCH</command>:
<programlisting>
EXEC SQL BEGIN DECLARE SECTION;
- typedef char mychartype[40];
- typedef long serial_t;
-EXEC SQL END DECLARE SECTION;
-</programlisting>
- Note that you could also use:
-<programlisting>
-EXEC SQL TYPE serial_t IS long;
-</programlisting>
- This declaration does not need to be part of a declare section.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Pointers</term>
- <listitem>
- <para>
- You can declare pointers to the most common types. Note however that
- you cannot use pointers as target variables of queries without
- auto-allocation. See <xref linkend="ecpg-descriptors"> for more
- information on auto-allocation.
- </para>
-<programlisting>
-EXEC SQL BEGIN DECLARE SECTION;
- int *intp;
- char **charp;
-EXEC SQL END DECLARE SECTION;
-</programlisting>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Special types of variables</term>
- <listitem>
- <para>
- ECPG contains some special types that help you to interact easily with
- data from the SQL server. For example it has implemented support for
- the <type>varchar</>, <type>numeric</>, <type>date</>, <type>timestamp</>, and <type>interval</> types.
- <xref linkend="ecpg-pgtypes"> contains basic functions to deal with
- those types, such that you do not need to send a query to the SQL
- server just for adding an interval to a timestamp for example.
- </para>
- <para>
- The special type <type>VARCHAR</type>
- is 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>
- </listitem>
- </varlistentry>
- </variablelist>
- </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;
+int v1;
+VARCHAR v2;
EXEC SQL END DECLARE SECTION;
...
...
-do {
+do
+{
...
EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
...
</para>
</sect2>
- <sect2>
- <title>Indicators</title>
+ <sect2 id="ecpg-variables-type-mapping">
+ <title>Type mapping</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 datum 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:
-
- ...
+ When ECPG applications exchange values between the PostgreSQL
+ server and the C application, such as when retrieving query
+ results from the server or executing SQL statements with input
+ parameters, the values need to be converted between PostgreSQL
+ data types and host language variable types (C language data
+ types, concretely). One of the main points of ECPG is that it
+ takes care of this automatically in most cases.
+ </para>
-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>
+ In this respect, there are two kinds of data types: Some simple
+ PostgreSQL data types, such as <type>integer</type>
+ and <type>text</type>, can be read and written by the application
+ directly. Other PostgreSQL data types, such
+ as <type>timestamp</type> and <type>numeric</type> can only be
+ accessed through special library functions; see
+ <xref linkend="ecpg-special-types">.
</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.
+ <xref linkend="ecpg-datatype-hostvars-table"> shows which PostgreSQL
+ data types correspond to which C data types. When you wish to
+ send or receive a value of a given PostgreSQL data type, you
+ should declare a C variable of the corresponding C data type in
+ the declare section.
</para>
- </sect2>
- </sect1>
- <sect1 id="ecpg-dynamic">
- <title>Dynamic SQL</title>
+ <table id="ecpg-datatype-hostvars-table">
+ <title>Mapping between PostgreSQL data types and C variable types</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>PostgreSQL data type</entry>
+ <entry>Host variable type</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><type>smallint</type></entry>
+ <entry><type>short</type></entry>
+ </row>
+
+ <row>
+ <entry><type>integer</type></entry>
+ <entry><type>int</type></entry>
+ </row>
+
+ <row>
+ <entry><type>bigint</type></entry>
+ <entry><type>long long int</type></entry>
+ </row>
+
+ <row>
+ <entry><type>decimal</type></entry>
+ <entry><type>decimal</type><footnote id="ecpg-datatype-table-fn"><para>This type can only be accessed through special library functions; see <xref linkend="ecpg-special-types">.</para></footnote></entry>
+ </row>
+
+ <row>
+ <entry><type>numeric</type></entry>
+ <entry><type>numeric</type><footnoteref linkend="ecpg-datatype-table-fn"></entry>
+ </row>
+
+ <row>
+ <entry><type>real</type></entry>
+ <entry><type>float</type></entry>
+ </row>
+
+ <row>
+ <entry><type>double precision</type></entry>
+ <entry><type>double</type></entry>
+ </row>
+
+ <row>
+ <entry><type>serial</type></entry>
+ <entry><type>int</type></entry>
+ </row>
+
+ <row>
+ <entry><type>bigserial</type></entry>
+ <entry><type>long long int</type></entry>
+ </row>
+
+ <row>
+ <entry><type>oid</type></entry>
+ <entry><type>unsigned int</type></entry>
+ </row>
+
+ <row>
+ <entry><type>character(<replaceable>n</>)</type>, <type>varchar(<replaceable>n</>)</type>, <type>text</type></entry>
+ <entry><type>char[<replaceable>n</>+1]</type>, <type>VARCHAR[<replaceable>n</>+1]</type><footnote><para>declared in <filename>ecpglib.h</filename></para></footnote></entry>
+ </row>
+
+ <row>
+ <entry><type>name</type></entry>
+ <entry><type>char[NAMEDATALEN]</type></entry>
+ </row>
+
+ <row>
+ <entry><type>timestamp</type></entry>
+ <entry><type>timestamp</type><footnoteref linkend="ecpg-datatype-table-fn"></entry>
+ </row>
+
+ <row>
+ <entry><type>interval</type></entry>
+ <entry><type>interval</type><footnoteref linkend="ecpg-datatype-table-fn"></entry>
+ </row>
+
+ <row>
+ <entry><type>date</type></entry>
+ <entry><type>date</type><footnoteref linkend="ecpg-datatype-table-fn"></entry>
+ </row>
+
+ <row>
+ <entry><type>boolean</type></entry>
+ <entry><type>bool</type><footnote><para>declared in <filename>ecpglib.h</filename> if not native</para></footnote></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <sect3 id="ecpg-char">
+ <title>Handling character strings</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>
+ To handle SQL character string data types, such
+ as <type>varchar</type> and <type>text</type>, there are two
+ possible ways to declare the host variables.
+ </para>
- <para>
- The simplest way to execute an arbitrary SQL statement is to use
- the command <command>EXECUTE IMMEDIATE</command>. For example:
+ <para>
+ One way is using <type>char[]</type>, an array
+ of <type>char</type>, which is the most common way to handle
+ character data in C.
<programlisting>
EXEC SQL BEGIN DECLARE SECTION;
-const char *stmt = "CREATE TABLE test1 (...);";
+ char str[50];
EXEC SQL END DECLARE SECTION;
+</programlisting>
+ Note that you have to take care of the length yourself. If you
+ use this host variable as the target variable of a query which
+ returns a string with more than 49 characters, a buffer overflow
+ occurs.
+ </para>
-EXEC SQL EXECUTE IMMEDIATE :stmt;
+ <para>
+ The other way is using the <type>VARCHAR</type> type, which is a
+ special type provided by ECPG. The definition on an array of
+ type <type>VARCHAR</type> is converted into a
+ named <type>struct</> for every variable. A declaration like:
+<programlisting>
+VARCHAR var[180];
</programlisting>
- You cannot execute statements that retrieve data (e.g.,
- <command>SELECT</command>) this way.
- </para>
+ is converted into:
+<programlisting>
+struct varchar_var { int len; char arr[180]; } var;
+</programlisting>
+ The member <structfield>arr</structfield> hosts the string
+ including a terminating zero byte. Thus, to store a string in
+ a <type>VARCHAR</type> host variable, the host variable has to be
+ declared with the length including the zero byte terminator. The
+ member <structfield>len</structfield> holds the length of the
+ string stored in the <structfield>arr</structfield> without the
+ terminating zero byte. When a host variable is used as input for
+ a query, if <literal>strlen(arr)</literal>
+ and <structfield>len</structfield> are different, the shorter one
+ is used.
+ </para>
- <para>
- 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:
+ <para>
+ Two or more <type>VARCHAR</type> host variables cannot be defined
+ in single line statement. The following code will confuse
+ the <command>ecpg</command> preprocessor:
<programlisting>
-EXEC SQL BEGIN DECLARE SECTION;
-const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
-EXEC SQL END DECLARE SECTION;
+VARCHAR v1[128], v2[128]; /* WRONG */
+</programlisting>
+ Two variables should be defined in separate statements like this:
+<programlisting>
+VARCHAR v1[128];
+VARCHAR v2[128];
+</programlisting>
+ </para>
-EXEC SQL PREPARE mystmt FROM :stmt;
- ...
-EXEC SQL EXECUTE mystmt USING 42, 'foobar';
+ <para>
+ <type>VARCHAR</type> can be written in upper or lower case, but
+ not in mixed case.
+ </para>
+
+ <para>
+ <type>char</type> and <type>VARCHAR</type> host variables can
+ also hold values of other SQL types, which will be stored in
+ their string forms.
+ </para>
+ </sect3>
+
+ <sect3 id="ecpg-special-types">
+ <title>Accessing special data types</title>
+
+ <para>
+ ECPG contains some special types that help you to interact easily
+ with some special data types from the PostgreSQL server. In
+ particular, it has implemented support for the
+ <type>numeric</>, <type>decimal</type>, <type>date</>, <type>timestamp</>,
+ and <type>interval</> types. These data types cannot usefully be
+ mapped to primitive host variable types (such
+ as <type>int</>, <type>long long int</type>,
+ or <type>char[]</type>), because they have a complex internal
+ structure. Applications deal with these types by declaring host
+ variables in special types and accessing them using functions in
+ the pgtypes library. The pgtypes library, described in detail
+ in <xref linkend="ecpg-pgtypes"> contains basic functions to deal
+ with those types, such that you do not need to send a query to
+ the SQL server just for adding an interval to a timestamp for
+ example.
+ </para>
+
+ <para>
+ The follow subsections describe these special data types. For
+ more details about pgtypes library functions,
+ see <xref linkend="ecpg-pgtypes">.
+ </para>
+
+ <sect4>
+ <title id="ecpg-type-timestamp-date">timestamp, date</title>
+
+ <para>
+ Here is a pattern for handling <type>timestamp</type> variables
+ in the ECPG host application.
+ </para>
+
+ <para>
+ First, the program has to include the header file for the
+ <type>timestamp</type> type:
+<programlisting>
+#include <pgtypes_timestamp.h>
</programlisting>
- If the statement you are executing returns values, then add an
- <literal>INTO</literal> clause:
-<programlisting><![CDATA[
+ </para>
+
+ <para>
+ Next, declare a host variable as type <type>timestamp</type> in
+ the declare section:
+<programlisting>
EXEC SQL BEGIN DECLARE SECTION;
-const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
-int v1, v2;
-VARCHAR v3;
+timestamp ts;
EXEC SQL END DECLARE SECTION;
-
-EXEC SQL PREPARE mystmt FROM :stmt;
- ...
-EXEC SQL EXECUTE mystmt INTO v1, v2, v3 USING 37;
-]]>
</programlisting>
- An <command>EXECUTE</command> command can have an
- <literal>INTO</literal> clause, a <literal>USING</literal> clause,
- both, or neither.
- </para>
+ </para>
- <para>
- When you don't need the prepared statement anymore, you should
- deallocate it:
+ <para>
+ And after reading a value into the host variable, process it
+ using pgtypes library functions. In following example, the
+ <type>timestamp</type> value is converted into text (ASCII) form
+ with the <function>PGTYPEStimestamp_to_asc()</function>
+ function:
<programlisting>
-EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;
+EXEC SQL SELECT now()::timestamp INTO :ts;
+
+printf("ts = %s\n", PGTYPEStimestamp_to_asc(ts));
</programlisting>
- </para>
- </sect1>
+ This example will show some result like following:
+<screen>
+ts = 2010-06-27 18:03:56.949343
+</screen>
+ </para>
+ <para>
+ In addition, the DATE type can be handled in the same way. The
+ program has to include pg_types_date.h, declare a host variable
+ as the date type and convert a DATE value into a text form using
+ PGTYPESdate_to_asc() function. For more details about the
+ pgtypes library functions, see <xref linkend="ecpg-pgtypes">.
+ </para>
+ </sect4>
- <sect1 id="ecpg-pgtypes">
- <title>pgtypes library</title>
+ <sect4 id="ecpg-type-interval">
+ <title>interval</title>
- <para>
- The pgtypes library maps <productname>PostgreSQL</productname> database
- types to C equivalents that can be used in C programs. It also offers
- functions to do basic calculations with those types within C, i.e., without
- the help of the <productname>PostgreSQL</productname> server. See the
- following example:
-<programlisting><![CDATA[
+ <para>
+ The handling of the <type>interval</type> type is also similar
+ to the <type>timestamp</type> and <type>date</type> types. It
+ is required, however, to allocate memory for
+ an <type>interval</type> type value explicitly. In other words,
+ the memory space for the variable has to be allocated in the
+ heap memory, not in the stack memory.
+ </para>
+
+ <para>
+ Here is an example program:
+<programlisting>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pgtypes_interval.h>
+
+int
+main(void)
+{
EXEC SQL BEGIN DECLARE SECTION;
- date date1;
- timestamp ts1, tsout;
- interval iv1;
- char *out;
+ interval *in;
EXEC SQL END DECLARE SECTION;
-PGTYPESdate_today(&date1);
-EXEC SQL SELECT started, duration INTO :ts1, :iv1 FROM datetbl WHERE d=:date1;
-PGTYPEStimestamp_add_interval(&ts1, &iv1, &tsout);
-out = PGTYPEStimestamp_to_asc(&tsout);
-printf("Started + duration: %s\n", out);
-free(out);
-]]>
+ EXEC SQL CONNECT TO testdb;
+
+ in = PGTYPESinterval_new();
+ EXEC SQL SELECT '1 min'::interval INTO :in;
+ printf("interval = %s\n", PGTYPESinterval_to_asc(in));
+ PGTYPESinterval_free(in);
+
+ EXEC SQL COMMIT;
+ EXEC SQL DISCONNECT ALL;
+ return 0;
+}
</programlisting>
- </para>
+ </para>
+ </sect4>
- <sect2>
- <title>The numeric type</title>
- <para>
- The numeric type offers to do calculations with arbitrary precision. See
- <xref linkend="datatype-numeric"> for the equivalent type in the
- <productname>PostgreSQL</> server. Because of the arbitrary precision this
- variable needs to be able to expand and shrink dynamically. That's why you
- can only create numeric variables on the heap, by means of the
- <function>PGTYPESnumeric_new</> and <function>PGTYPESnumeric_free</>
- functions. The decimal type, which is similar but limited in precision,
- can be created on the stack as well as on the heap.
- </para>
- <para>
- The following functions can be used to work with the numeric type:
- <variablelist>
- <varlistentry>
- <term><function>PGTYPESnumeric_new</function></term>
- <listitem>
- <para>
- Request a pointer to a newly allocated numeric variable.
-<synopsis>
-numeric *PGTYPESnumeric_new(void);
-</synopsis>
- </para>
- </listitem>
- </varlistentry>
+ <sect4 id="ecpg-type-numeric-decimal">
+ <title>numeric, decimal</title>
- <varlistentry>
- <term><function>PGTYPESnumeric_free</function></term>
- <listitem>
- <para>
- Free a numeric type, release all of its memory.
-<synopsis>
-void PGTYPESnumeric_free(numeric *var);
-</synopsis>
- </para>
- </listitem>
- </varlistentry>
+ <para>
+ The handling of the <type>numeric</type>
+ and <type>decimal</type> types is similar to the
+ <type>interval</type> type: It requires defining a pointer,
+ allocating some memory space on the heap, and accessing the
+ variable using the pgtypes library functions. For more details
+ about the pgtypes library functions,
+ see <xref linkend="ecpg-pgtypes">.
+ </para>
- <varlistentry>
- <term><function>PGTYPESnumeric_from_asc</function></term>
- <listitem>
- <para>
- Parse a numeric type from its string notation.
-<synopsis>
-numeric *PGTYPESnumeric_from_asc(char *str, char **endptr);
-</synopsis>
- Valid formats are for example:
- <literal>-2</literal>,
- <literal>.794</literal>,
- <literal>+3.44</literal>,
- <literal>592.49E07</literal> or
- <literal>-32.84e-4</literal>.
- If the value could be parsed successfully, a valid pointer is returned,
- else the NULL pointer. At the moment ECPG always parses the complete
- string and so it currently does not support to store the address of the
- first invalid character in <literal>*endptr</literal>. You can safely
- set <literal>endptr</literal> to NULL.
- </para>
- </listitem>
- </varlistentry>
+ <para>
+ No functions are provided specifically for
+ the <type>decimal</type> type. An application has to convert it
+ to a <type>numeric</type> variable using a pgtypes library
+ function to do further processing.
+ </para>
- <varlistentry>
- <term><function>PGTYPESnumeric_to_asc</function></term>
- <listitem>
- <para>
- Returns a pointer to a string allocated by <function>malloc</function> that contains the string
- representation of the numeric type <literal>num</literal>.
-<synopsis>
-char *PGTYPESnumeric_to_asc(numeric *num, int dscale);
-</synopsis>
- The numeric value will be printed with <literal>dscale</literal> decimal
- digits, with rounding applied if necessary.
- </para>
- </listitem>
- </varlistentry>
+ <para>
+ Here is an example program handling <type>numeric</type>
+ and <type>decimal</type> type variables.
+<programlisting>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pgtypes_numeric.h>
- <varlistentry>
- <term><function>PGTYPESnumeric_add</function></term>
- <listitem>
- <para>
- Add two numeric variables into a third one.
-<synopsis>
-int PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result);
-</synopsis>
- The function adds the variables <literal>var1</literal> and
- <literal>var2</literal> into the result variable
- <literal>result</literal>.
- The function returns 0 on success and -1 in case of error.
- </para>
- </listitem>
- </varlistentry>
+EXEC SQL WHENEVER SQLERROR STOP;
- <varlistentry>
- <term><function>PGTYPESnumeric_sub</function></term>
- <listitem>
- <para>
- Subtract two numeric variables and return the result in a third one.
-<synopsis>
-int PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result);
-</synopsis>
- The function subtracts the variable <literal>var2</literal> from
- the variable <literal>var1</literal>. The result of the operation is
- stored in the variable <literal>result</literal>.
- The function returns 0 on success and -1 in case of error.
- </para>
- </listitem>
- </varlistentry>
+int
+main(void)
+{
+EXEC SQL BEGIN DECLARE SECTION;
+ numeric *num;
+ numeric *num2;
+ decimal *dec;
+EXEC SQL END DECLARE SECTION;
- <varlistentry>
- <term><function>PGTYPESnumeric_mul</function></term>
- <listitem>
- <para>
- Multiply two numeric variables and return the result in a third one.
-<synopsis>
-int PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result);
-</synopsis>
- The function multiplies the variables <literal>var1</literal> and
- <literal>var2</literal>. The result of the operation is stored in the
- variable <literal>result</literal>.
- The function returns 0 on success and -1 in case of error.
- </para>
- </listitem>
- </varlistentry>
+ EXEC SQL CONNECT TO testdb;
- <varlistentry>
- <term><function>PGTYPESnumeric_div</function></term>
- <listitem>
- <para>
- Divide two numeric variables and return the result in a third one.
-<synopsis>
-int PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result);
-</synopsis>
- The function divides the variables <literal>var1</literal> by
- <literal>var2</literal>. The result of the operation is stored in the
- variable <literal>result</literal>.
- The function returns 0 on success and -1 in case of error.
- </para>
- </listitem>
- </varlistentry>
+ num = PGTYPESnumeric_new();
+ dec = PGTYPESdecimal_new();
- <varlistentry>
- <term><function>PGTYPESnumeric_cmp</function></term>
- <listitem>
- <para>
- Compare two numeric variables.
-<synopsis>
-int PGTYPESnumeric_cmp(numeric *var1, numeric *var2)
-</synopsis>
- This function compares two numeric variables. In case of error,
- <literal>INT_MAX</literal> is returned. On success, the function
- returns one of three possible results:
- <itemizedlist>
- <listitem>
- <para>
- 1, if <literal>var1</> is bigger than <literal>var2</>
- </para>
- </listitem>
- <listitem>
- <para>
- -1, if <literal>var1</> is smaller than <literal>var2</>
- </para>
- </listitem>
- <listitem>
- <para>
- 0, if <literal>var1</> and <literal>var2</> are equal
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </listitem>
- </varlistentry>
+ EXEC SQL SELECT 12.345::numeric(4,2), 23.456::decimal(4,2) INTO :num, :dec;
- <varlistentry>
- <term><function>PGTYPESnumeric_from_int</function></term>
- <listitem>
- <para>
- Convert an int variable to a numeric variable.
-<synopsis>
-int PGTYPESnumeric_from_int(signed int int_val, numeric *var);
-</synopsis>
- This function accepts a variable of type signed int and stores it
- in the numeric variable <literal>var</>. Upon success, 0 is returned and
- -1 in case of a failure.
- </para>
- </listitem>
- </varlistentry>
+ printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 0));
+ printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 1));
+ printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 2));
- <varlistentry>
- <term><function>PGTYPESnumeric_from_long</function></term>
- <listitem>
- <para>
- Convert a long int variable to a numeric variable.
-<synopsis>
-int PGTYPESnumeric_from_long(signed long int long_val, numeric *var);
-</synopsis>
- This function accepts a variable of type signed long int and stores it
- in the numeric variable <literal>var</>. Upon success, 0 is returned and
- -1 in case of a failure.
- </para>
- </listitem>
- </varlistentry>
+ /* Convert decimal to numeric to show a decimal value. */
+ num2 = PGTYPESnumeric_new();
+ PGTYPESnumeric_from_decimal(dec, num2);
- <varlistentry>
- <term><function>PGTYPESnumeric_copy</function></term>
- <listitem>
- <para>
- Copy over one numeric variable into another one.
-<synopsis>
-int PGTYPESnumeric_copy(numeric *src, numeric *dst);
-</synopsis>
- This function copies over the value of the variable that
- <literal>src</literal> points to into the variable that <literal>dst</>
- points to. It returns 0 on success and -1 if an error occurs.
- </para>
- </listitem>
- </varlistentry>
+ printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 0));
+ printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 1));
+ printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 2));
- <varlistentry>
- <term><function>PGTYPESnumeric_from_double</function></term>
- <listitem>
- <para>
- Convert a variable of type double to a numeric.
-<synopsis>
-int PGTYPESnumeric_from_double(double d, numeric *dst);
-</synopsis>
- This function accepts a variable of type double and stores the result
- in the variable that <literal>dst</> points to. It returns 0 on success
- and -1 if an error occurs.
- </para>
- </listitem>
- </varlistentry>
+ PGTYPESnumeric_free(num2);
+ PGTYPESdecimal_free(dec);
+ PGTYPESnumeric_free(num);
- <varlistentry>
- <term><function>PGTYPESnumeric_to_double</function></term>
- <listitem>
- <para>
- Convert a variable of type numeric to double.
-<synopsis>
-int PGTYPESnumeric_to_double(numeric *nv, double *dp)
-</synopsis>
- The function converts the numeric value from the variable that
- <literal>nv</> points to into the double variable that <literal>dp</> points
- to. It returns 0 on success and -1 if an error occurs, including
- overflow. On overflow, the global variable <literal>errno</> will be set
- to <literal>PGTYPES_NUM_OVERFLOW</> additionally.
- </para>
- </listitem>
- </varlistentry>
+ EXEC SQL COMMIT;
+ EXEC SQL DISCONNECT ALL;
+ return 0;
+}
+</programlisting>
+ </para>
+ </sect4>
+ </sect3>
- <varlistentry>
- <term><function>PGTYPESnumeric_to_int</function></term>
- <listitem>
- <para>
- Convert a variable of type numeric to int.
-<synopsis>
-int PGTYPESnumeric_to_int(numeric *nv, int *ip);
-</synopsis>
- The function converts the numeric value from the variable that
- <literal>nv</> points to into the integer variable that <literal>ip</>
- points to. It returns 0 on success and -1 if an error occurs, including
- overflow. On overflow, the global variable <literal>errno</> will be set
- to <literal>PGTYPES_NUM_OVERFLOW</> additionally.
- </para>
- </listitem>
- </varlistentry>
+ <sect3 id="ecpg-variables-nonprimitive-c">
+ <title>Host variables with nonprimitive types</title>
- <varlistentry>
- <term><function>PGTYPESnumeric_to_long</function></term>
- <listitem>
- <para>
- Convert a variable of type numeric to long.
-<synopsis>
-int PGTYPESnumeric_to_long(numeric *nv, long *lp);
-</synopsis>
- The function converts the numeric value from the variable that
- <literal>nv</> points to into the long integer variable that
- <literal>lp</> points to. It returns 0 on success and -1 if an error
- occurs, including overflow. On overflow, the global variable
- <literal>errno</> will be set to <literal>PGTYPES_NUM_OVERFLOW</>
- additionally.
- </para>
- </listitem>
- </varlistentry>
+ <para>
+ As a host variable you can also use arrays, typedefs, structs, and
+ pointers.
+ </para>
- <varlistentry>
- <term><function>PGTYPESnumeric_to_decimal</function></term>
- <listitem>
- <para>
- Convert a variable of type numeric to decimal.
-<synopsis>
-int PGTYPESnumeric_to_decimal(numeric *src, decimal *dst);
-</synopsis>
- The function converts the numeric value from the variable that
- <literal>src</> points to into the decimal variable that
- <literal>dst</> points to. It returns 0 on success and -1 if an error
- occurs, including overflow. On overflow, the global variable
- <literal>errno</> will be set to <literal>PGTYPES_NUM_OVERFLOW</>
- additionally.
- </para>
- </listitem>
- </varlistentry>
+ <sect4 id="ecpg-variables-arrays">
+ <title>Arrays</title>
- <varlistentry>
- <term><function>PGTYPESnumeric_from_decimal</function></term>
- <listitem>
- <para>
- Convert a variable of type decimal to numeric.
-<synopsis>
-int PGTYPESnumeric_from_decimal(decimal *src, numeric *dst);
-</synopsis>
- The function converts the decimal value from the variable that
- <literal>src</> points to into the numeric variable that
- <literal>dst</> points to. It returns 0 on success and -1 if an error
- occurs. Since the decimal type is implemented as a limited version of
- the numeric type, overflow cannot occur with this conversion.
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </para>
- </sect2>
+ <para>
+ There are two use cases for arrays as host variables. The first
+ is a way to store some text string in <type>char[]</type>
+ or <type>VARCHAR[]</type>, as
+ explained <xref linkend="ecpg-char">. The second use case is to
+ retreive multiple rows from a query result without using a
+ cursor. Without an array, to process a query result consisting
+ of multiple rows, it is required to use a cursor and
+ the <command>FETCH</command> command. But with array host
+ variables, multiple rows can be received at once. The length of
+ the array has to be defined to be able to accomodate all rows,
+ otherwise a buffer overflow will likely occur.
+ </para>
- <sect2>
- <title>The date type</title>
- <para>
- The date type in C enables your programs to deal with data of the SQL type
- date. See <xref linkend="datatype-datetime"> for the equivalent type in the
- <productname>PostgreSQL</> server.
- </para>
- <para>
- The following functions can be used to work with the date type:
- <variablelist>
- <varlistentry id="PGTYPESdatefromtimestamp">
- <term><function>PGTYPESdate_from_timestamp</function></term>
- <listitem>
- <para>
- Extract the date part from a timestamp.
-<synopsis>
-date PGTYPESdate_from_timestamp(timestamp dt);
-</synopsis>
- The function receives a timestamp as its only argument and returns the
- extracted date part from this timestamp.
- </para>
- </listitem>
- </varlistentry>
+ <para>
+ Following example scans the <literal>pg_database</literal>
+ system table and shows all OIDs and names of the available
+ databases:
+<programlisting>
+int
+main(void)
+{
+EXEC SQL BEGIN DECLARE SECTION;
+ int dbid[8];
+ char dbname[8][16];
+ int i;
+EXEC SQL END DECLARE SECTION;
- <varlistentry id="PGTYPESdatefromasc">
- <term><function>PGTYPESdate_from_asc</function></term>
- <listitem>
- <para>
- Parse a date from its textual representation.
-<synopsis>
-date PGTYPESdate_from_asc(char *str, char **endptr);
-</synopsis>
- The function receives a C char* string <literal>str</> and a pointer to
- a C char* string <literal>endptr</>. At the moment ECPG always parses
- the complete string and so it currently does not support to store the
- address of the first invalid character in <literal>*endptr</literal>.
- You can safely set <literal>endptr</literal> to NULL.
- </para>
- <para>
- Note that the function always assumes MDY-formatted dates and there is
- currently no variable to change that within ECPG.
- </para>
- <para>
- <xref linkend="ecpg-pgtypesdate-from-asc-table"> shows the allowed input formats.
- </para>
- <table id="ecpg-pgtypesdate-from-asc-table">
- <title>Valid input formats for <function>PGTYPESdate_from_asc</function></title>
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Input</entry>
- <entry>Result</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><literal>January 8, 1999</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>1999-01-08</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>1/8/1999</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>1/18/1999</literal></entry>
- <entry><literal>January 18, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>01/02/03</literal></entry>
- <entry><literal>February 1, 2003</literal></entry>
- </row>
- <row>
- <entry><literal>1999-Jan-08</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>Jan-08-1999</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>08-Jan-1999</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>99-Jan-08</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>08-Jan-99</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>08-Jan-06</literal></entry>
- <entry><literal>January 8, 2006</literal></entry>
- </row>
- <row>
- <entry><literal>Jan-08-99</literal></entry>
- <entry><literal>January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>19990108</literal></entry>
- <entry><literal>ISO 8601; January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>990108</literal></entry>
- <entry><literal>ISO 8601; January 8, 1999</literal></entry>
- </row>
- <row>
- <entry><literal>1999.008</literal></entry>
- <entry><literal>year and day of year</literal></entry>
- </row>
- <row>
- <entry><literal>J2451187</literal></entry>
- <entry><literal>Julian day</literal></entry>
- </row>
- <row>
- <entry><literal>January 8, 99 BC</literal></entry>
- <entry><literal>year 99 before the Common Era</literal></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </listitem>
- </varlistentry>
+ memset(dbname, 0, sizeof(char)* 16 * 8);
+ memset(dbid, 0, sizeof(int) * 8);
- <varlistentry id="PGTYPESdatetoasc">
- <term><function>PGTYPESdate_to_asc</function></term>
- <listitem>
- <para>
- Return the textual representation of a date variable.
-<synopsis>
-char *PGTYPESdate_to_asc(date dDate);
-</synopsis>
- The function receives the date <literal>dDate</> as its only parameter.
- It will output the date in the form <literal>1999-01-18</>, i.e., in the
- <literal>YYYY-MM-DD</> format.
- </para>
- </listitem>
- </varlistentry>
+ EXEC SQL CONNECT TO testdb;
- <varlistentry id="PGTYPESdatejulmdy">
- <term><function>PGTYPESdate_julmdy</function></term>
- <listitem>
- <para>
- Extract the values for the day, the month and the year from a variable
- of type date.
-<synopsis>
-void PGTYPESdate_julmdy(date d, int *mdy);
-</synopsis>
- <!-- almost same description as for rjulmdy() -->
- The function receives the date <literal>d</> and a pointer to an array
- of 3 integer values <literal>mdy</>. The variable name indicates
- the sequential order: <literal>mdy[0]</> will be set to contain the
- number of the month, <literal>mdy[1]</> will be set to the value of the
- day and <literal>mdy[2]</> will contain the year.
- </para>
- </listitem>
- </varlistentry>
+ /* Retrieve multiple rows into arrays at once. */
+ EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database;
- <varlistentry id="PGTYPESdatemdyjul">
- <term><function>PGTYPESdate_mdyjul</function></term>
- <listitem>
- <para>
- Create a date value from an array of 3 integers that specify the
- day, the month and the year of the date.
-<synopsis>
-void PGTYPESdate_mdyjul(int *mdy, date *jdate);
-</synopsis>
- The function receives the array of the 3 integers (<literal>mdy</>) as
- its first argument and as its second argument a pointer to a variable
- of type date that should hold the result of the operation.
- </para>
- </listitem>
- </varlistentry>
+ for (i = 0; i < 8; i++)
+ printf("oid=%d, dbname=%s\n", dbid[i], dbname[i]);
- <varlistentry id="PGTYPESdatedayofweek">
- <term><function>PGTYPESdate_dayofweek</function></term>
- <listitem>
- <para>
- Return a number representing the day of the week for a date value.
-<synopsis>
-int PGTYPESdate_dayofweek(date d);
-</synopsis>
- The function receives the date variable <literal>d</> as its only
- argument and returns an integer that indicates the day of the week for
- this date.
- <itemizedlist>
- <listitem>
- <para>
- 0 - Sunday
- </para>
- </listitem>
- <listitem>
- <para>
- 1 - Monday
- </para>
- </listitem>
- <listitem>
- <para>
- 2 - Tuesday
- </para>
- </listitem>
- <listitem>
- <para>
- 3 - Wednesday
- </para>
- </listitem>
- <listitem>
- <para>
- 4 - Thursday
- </para>
- </listitem>
- <listitem>
- <para>
- 5 - Friday
- </para>
- </listitem>
- <listitem>
- <para>
- 6 - Saturday
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </listitem>
- </varlistentry>
+ EXEC SQL COMMIT;
+ EXEC SQL DISCONNECT ALL;
+ return 0;
+}
+</programlisting>
- <varlistentry id="PGTYPESdatetoday">
- <term><function>PGTYPESdate_today</function></term>
- <listitem>
- <para>
- Get the current date.
-<synopsis>
-void PGTYPESdate_today(date *d);
-</synopsis>
- The function receives a pointer to a date variable (<literal>d</>)
- that it sets to the current date.
- </para>
- </listitem>
- </varlistentry>
+ This example shows following result. (The exact values depend on
+ local circumstances.)
+<screen>
+oid=1, dbname=template1
+oid=11510, dbname=template0
+oid=11511, dbname=postgres
+oid=313780, dbname=testdb
+oid=0, dbname=
+oid=0, dbname=
+oid=0, dbname=
+</screen>
+ </para>
+ </sect4>
- <varlistentry id="PGTYPESdatefmtasc">
- <term><function>PGTYPESdate_fmt_asc</function></term>
- <listitem>
- <para>
- Convert a variable of type date to its textual representation using a
- format mask.
-<synopsis>
-int PGTYPESdate_fmt_asc(date dDate, char *fmtstring, char *outbuf);
-</synopsis>
- The function receives the date to convert (<literal>dDate</>), the
- format mask (<literal>fmtstring</>) and the string that will hold the
- textual representation of the date (<literal>outbuf</>).
- </para>
- <para>
- On success, 0 is returned and a negative value if an error occurred.
- </para>
- <para>
- The following literals are the field specifiers you can use:
- <itemizedlist>
- <listitem>
- <para>
- <literal>dd</literal> - The number of the day of the month.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>mm</literal> - The number of the month of the year.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>yy</literal> - The number of the year as a two digit number.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>yyyy</literal> - The number of the year as a four digit number.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>ddd</literal> - The name of the day (abbreviated).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>mmm</literal> - The name of the month (abbreviated).
- </para>
- </listitem>
- </itemizedlist>
- All other characters are copied 1:1 to the output string.
- </para>
- <para>
- <xref linkend="ecpg-pgtypesdate-fmt-asc-example-table"> indicates a few possible formats. This will give
- you an idea of how to use this function. All output lines are based on
- the same date: November 23, 1959.
- </para>
- <table id="ecpg-pgtypesdate-fmt-asc-example-table">
- <title>Valid input formats for <function>PGTYPESdate_fmt_asc</function></title>
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Format</entry>
- <entry>Result</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><literal>mmddyy</literal></entry>
- <entry><literal>112359</literal></entry>
- </row>
- <row>
- <entry><literal>ddmmyy</literal></entry>
- <entry><literal>231159</literal></entry>
- </row>
- <row>
- <entry><literal>yymmdd</literal></entry>
- <entry><literal>591123</literal></entry>
- </row>
- <row>
- <entry><literal>yy/mm/dd</literal></entry>
- <entry><literal>59/11/23</literal></entry>
- </row>
- <row>
- <entry><literal>yy mm dd</literal></entry>
- <entry><literal>59 11 23</literal></entry>
- </row>
- <row>
- <entry><literal>yy.mm.dd</literal></entry>
- <entry><literal>59.11.23</literal></entry>
- </row>
- <row>
- <entry><literal>.mm.yyyy.dd.</literal></entry>
- <entry><literal>.11.1959.23.</literal></entry>
- </row>
- <row>
- <entry><literal>mmm. dd, yyyy</literal></entry>
- <entry><literal>Nov. 23, 1959</literal></entry>
- </row>
- <row>
- <entry><literal>mmm dd yyyy</literal></entry>
- <entry><literal>Nov 23 1959</literal></entry>
- </row>
- <row>
- <entry><literal>yyyy dd mm</literal></entry>
- <entry><literal>1959 23 11</literal></entry>
- </row>
- <row>
- <entry><literal>ddd, mmm. dd, yyyy</literal></entry>
- <entry><literal>Mon, Nov. 23, 1959</literal></entry>
- </row>
- <row>
- <entry><literal>(ddd) mmm. dd, yyyy</literal></entry>
- <entry><literal>(Mon) Nov. 23, 1959</literal></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </listitem>
- </varlistentry>
+ <sect4 id="ecpg-variables-struct">
+ <title>Structures</title>
- <varlistentry id="PGTYPESdatedefmtasc">
- <term><function>PGTYPESdate_defmt_asc</function></term>
- <listitem>
- <para>
- Use a format mask to convert a C <type>char*</type> string to a value of type
- date.
-<synopsis>
-int PGTYPESdate_defmt_asc(date *d, char *fmt, char *str);
-</synopsis>
- <!-- same description as rdefmtdate -->
- The function receives a pointer to the date value that should hold the
- result of the operation (<literal>d</>), the format mask to use for
- parsing the date (<literal>fmt</>) and the C char* string containing
- the textual representation of the date (<literal>str</>). The textual
- representation is expected to match the format mask. However you do not
- need to have a 1:1 mapping of the string to the format mask. The
- function only analyzes the sequential order and looks for the literals
- <literal>yy</literal> or <literal>yyyy</literal> that indicate the
- position of the year, <literal>mm</literal> to indicate the position of
- the month and <literal>dd</literal> to indicate the position of the
- day.
- </para>
- <para>
- <xref linkend="ecpg-rdefmtdate-example-table"> indicates a few possible formats. This will give
- you an idea of how to use this function.
- </para>
- <table id="ecpg-rdefmtdate-example-table">
- <title>Valid input formats for <function>rdefmtdate</function></title>
- <tgroup cols="3">
- <thead>
- <row>
- <entry>Format</entry>
- <entry>String</entry>
- <entry>Result</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><literal>ddmmyy</literal></entry>
- <entry><literal>21-2-54</literal></entry>
- <entry><literal>1954-02-21</literal></entry>
- </row>
- <row>
- <entry><literal>ddmmyy</literal></entry>
- <entry><literal>2-12-54</literal></entry>
- <entry><literal>1954-12-02</literal></entry>
- </row>
- <row>
- <entry><literal>ddmmyy</literal></entry>
- <entry><literal>20111954</literal></entry>
- <entry><literal>1954-11-20</literal></entry>
- </row>
- <row>
- <entry><literal>ddmmyy</literal></entry>
- <entry><literal>130464</literal></entry>
- <entry><literal>1964-04-13</literal></entry>
- </row>
- <row>
- <entry><literal>mmm.dd.yyyy</literal></entry>
- <entry><literal>MAR-12-1967</literal></entry>
- <entry><literal>1967-03-12</literal></entry>
- </row>
- <row>
- <entry><literal>yy/mm/dd</literal></entry>
- <entry><literal>1954, February 3rd</literal></entry>
- <entry><literal>1954-02-03</literal></entry>
- </row>
- <row>
- <entry><literal>mmm.dd.yyyy</literal></entry>
- <entry><literal>041269</literal></entry>
- <entry><literal>1969-04-12</literal></entry>
- </row>
- <row>
- <entry><literal>yy/mm/dd</literal></entry>
- <entry><literal>In the year 2525, in the month of July, mankind will be alive on the 28th day</literal></entry>
- <entry><literal>2525-07-28</literal></entry>
- </row>
- <row>
- <entry><literal>dd-mm-yy</literal></entry>
- <entry><literal>I said on the 28th of July in the year 2525</literal></entry>
- <entry><literal>2525-07-28</literal></entry>
- </row>
- <row>
- <entry><literal>mmm.dd.yyyy</literal></entry>
- <entry><literal>9/14/58</literal></entry>
- <entry><literal>1958-09-14</literal></entry>
- </row>
- <row>
- <entry><literal>yy/mm/dd</literal></entry>
- <entry><literal>47/03/29</literal></entry>
- <entry><literal>1947-03-29</literal></entry>
- </row>
- <row>
- <entry><literal>mmm.dd.yyyy</literal></entry>
- <entry><literal>oct 28 1975</literal></entry>
- <entry><literal>1975-10-28</literal></entry>
- </row>
- <row>
- <entry><literal>mmddyy</literal></entry>
- <entry><literal>Nov 14th, 1985</literal></entry>
- <entry><literal>1985-11-14</literal></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </listitem>
- </varlistentry>
- </variablelist>
- </para>
- </sect2>
-
- <sect2>
- <title>The timestamp type</title>
- <para>
- The timestamp type in C enables your programs to deal with data of the SQL
- type timestamp. See <xref linkend="datatype-datetime"> for the equivalent
- type in the <productname>PostgreSQL</> server.
- </para>
- <para>
- The following functions can be used to work with the timestamp type:
- <variablelist>
- <varlistentry id="PGTYPEStimestampfromasc">
- <term><function>PGTYPEStimestamp_from_asc</function></term>
- <listitem>
- <para>
- Parse a timestamp from its textual representation into a timestamp
- variable.
-<synopsis>
-timestamp PGTYPEStimestamp_from_asc(char *str, char **endptr);
-</synopsis>
- The function receives the string to parse (<literal>str</>) and a
- pointer to a C char* (<literal>endptr</>).
- At the moment ECPG always parses
- the complete string and so it currently does not support to store the
- address of the first invalid character in <literal>*endptr</literal>.
- You can safely set <literal>endptr</literal> to NULL.
- </para>
- <para>
- The function returns the parsed timestamp on success. On error,
- <literal>PGTYPESInvalidTimestamp</literal> is returned and <varname>errno</> is
- set to <literal>PGTYPES_TS_BAD_TIMESTAMP</>. See <xref linkend="PGTYPESInvalidTimestamp"> for important notes on this value.
-
- </para>
- <para>
- In general, the input string can contain any combination of an allowed
- date specification, a whitespace character and an allowed time
- specification. Note that timezones are not supported by ECPG. It can
- parse them but does not apply any calculation as the
- <productname>PostgreSQL</> server does for example. Timezone
- specifiers are silently discarded.
- </para>
- <para>
- <xref linkend="ecpg-pgtypestimestamp-from-asc-example-table"> contains a few examples for input strings.
- </para>
- <table id="ecpg-pgtypestimestamp-from-asc-example-table">
- <title>Valid input formats for <function>PGTYPEStimestamp_from_asc</function></title>
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Input</entry>
- <entry>Result</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><literal>1999-01-08 04:05:06</literal></entry>
- <entry><literal>1999-01-08 04:05:06</literal></entry>
- </row>
- <row>
- <entry><literal>January 8 04:05:06 1999 PST</literal></entry>
- <entry><literal>1999-01-08 04:05:06</literal></entry>
- </row>
- <row>
- <entry><literal>1999-Jan-08 04:05:06.789-8</literal></entry>
- <entry><literal>1999-01-08 04:05:06.789 (time zone specifier ignored)</literal></entry>
- </row>
- <row>
- <entry><literal>J2451187 04:05-08:00</literal></entry>
- <entry><literal>1999-01-08 04:05:00 (time zone specifier ignored)</literal></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </listitem>
- </varlistentry>
+ <para>
+ A structure whose member names match the column names of a query
+ result, can be used to retrieve multiple columns at once. The
+ structure enables handling multiple column values in a single
+ host variable.
+ </para>
- <varlistentry id="PGTYPEStimestamptoasc">
- <term><function>PGTYPEStimestamp_to_asc</function></term>
- <listitem>
- <para>
- Converts a date to a C char* string.
-<synopsis>
-char *PGTYPEStimestamp_to_asc(timestamp tstamp);
-</synopsis>
- The function receives the timestamp <literal>tstamp</> as
- its only argument and returns an allocated string that contains the
- textual representation of the timestamp.
- </para>
- </listitem>
- </varlistentry>
+ <para>
+ The following example retrieves OIDs, names, and sizes of the
+ avilable databases from the <literal>pg_database</literal>
+ system table and using
+ the <function>pg_database_size()</function> function. In this
+ example, a structure variable <varname>dbinfo_t</varname> with
+ members whose names match each column in
+ the <literal>SELECT</literal> result is used to retrieve one
+ result row without putting multiple host variables in
+ the <literal>FETCH</literal> statement.
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+ typedef struct
+ {
+ int oid;
+ char datname[65];
+ long long int size;
+ } dbinfo_t;
- <varlistentry id="PGTYPEStimestampcurrent">
- <term><function>PGTYPEStimestamp_current</function></term>
- <listitem>
- <para>
- Retrieve the current timestamp.
-<synopsis>
-void PGTYPEStimestamp_current(timestamp *ts);
-</synopsis>
- The function retrieves the current timestamp and saves it into the
- timestamp variable that <literal>ts</> points to.
- </para>
- </listitem>
- </varlistentry>
+ dbinfo_t dbval;
+EXEC SQL END DECLARE SECTION;
- <varlistentry id="PGTYPEStimestampfmtasc">
- <term><function>PGTYPEStimestamp_fmt_asc</function></term>
- <listitem>
- <para>
- Convert a timestamp variable to a C char* using a format mask.
-<synopsis>
-int PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr);
-</synopsis>
- The function receives a pointer to the timestamp to convert as its
- first argument (<literal>ts</>), a pointer to the output buffer
- (<literal>output</>), the maximal length that has been allocated for
- the output buffer (<literal>str_len</literal>) and the format mask to
- use for the conversion (<literal>fmtstr</literal>).
- </para>
- <para>
- Upon success, the function returns 0 and a negative value if an
- error occurred.
- </para>
- <para>
- You can use the following format specifiers for the format mask. The
- format specifiers are the same ones that are used in the
- <function>strftime</> function in <productname>libc</productname>. Any
- non-format specifier will be copied into the output buffer.
- <!-- This is from the FreeBSD man page:
- http://www.freebsd.org/cgi/man.cgi?query=strftime&apropos=0&sektion=3&manpath=FreeBSD+7.0-current&format=html
- -->
- <itemizedlist>
- <listitem>
- <para>
- <literal>%A</literal> - is replaced by national representation of
- the full weekday name.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%a</literal> - is replaced by national representation of
- the abbreviated weekday name.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%B</literal> - is replaced by national representation of
- the full month name.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%b</literal> - is replaced by national representation of
- the abbreviated month name.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%C</literal> - is replaced by (year / 100) as decimal
- number; single digits are preceded by a zero.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%c</literal> - is replaced by national representation of
- time and date.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%D</literal> - is equivalent to
- <literal>%m/%d/%y</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%d</literal> - is replaced by the day of the month as a
- decimal number (01-31).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%E*</literal> <literal>%O*</literal> - POSIX locale
- extensions. The sequences
- <literal>%Ec</literal>
- <literal>%EC</literal>
- <literal>%Ex</literal>
- <literal>%EX</literal>
- <literal>%Ey</literal>
- <literal>%EY</literal>
- <literal>%Od</literal>
- <literal>%Oe</literal>
- <literal>%OH</literal>
- <literal>%OI</literal>
- <literal>%Om</literal>
- <literal>%OM</literal>
- <literal>%OS</literal>
- <literal>%Ou</literal>
- <literal>%OU</literal>
- <literal>%OV</literal>
- <literal>%Ow</literal>
- <literal>%OW</literal>
- <literal>%Oy</literal>
- are supposed to provide alternative representations.
- </para>
- <para>
- Additionally <literal>%OB</literal> implemented to represent
- alternative months names (used standalone, without day mentioned).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%e</literal> - is replaced by the day of month as a decimal
- number (1-31); single digits are preceded by a blank.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%F</literal> - is equivalent to <literal>%Y-%m-%d</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%G</literal> - is replaced by a year as a decimal number
- with century. This year is the one that contains the greater part of
- the week (Monday as the first day of the week).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%g</literal> - is replaced by the same year as in
- <literal>%G</literal>, but as a decimal number without century
- (00-99).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%H</literal> - is replaced by the hour (24-hour clock) as a
- decimal number (00-23).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%h</literal> - the same as <literal>%b</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%I</literal> - is replaced by the hour (12-hour clock) as a
- decimal number (01-12).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%j</literal> - is replaced by the day of the year as a
- decimal number (001-366).
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>%k</literal> - is replaced by the hour (24-hour clock) as a
- decimal number (0-23); single digits are preceded by a blank.
- </para>
- </listitem>
- <listitem>
- <para>
+ memset(&dbval, 0, sizeof(dbinfo_t));
+
+ EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database;
+ EXEC SQL OPEN cur1;
+
+ /* when end of result set reached, break out of while loop */
+ EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+ while (1)
+ {
+ /* Fetch multiple columns into one structure. */
+ EXEC SQL FETCH FROM cur1 INTO :dbval;
+
+ /* Print members of the structure. */
+ printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, dbval.size);
+ }
+
+ EXEC SQL CLOSE cur1;
+</programlisting>
+ </para>
+
+ <para>
+ This example shows following result. (The exact values depend on
+ local circumstances.)
+<screen>
+oid=1, datname=template1, size=4324580
+oid=11510, datname=template0, size=4243460
+oid=11511, datname=postgres, size=4324580
+oid=313780, datname=testdb, size=8183012
+</screen>
+ </para>
+
+ <para>
+ Structure host variables <quote>absorb</quote> as many columns
+ as the structure as fields. Additional columns can be assigned
+ to other host variables. For example, the above program could
+ also be restructured like this, with the <varname>size</varname>
+ variable outside the structure:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+ typedef struct
+ {
+ int oid;
+ char datname[65];
+ } dbinfo_t;
+
+ dbinfo_t dbval;
+ long long int size;
+EXEC SQL END DECLARE SECTION;
+
+ memset(&dbval, 0, sizeof(dbinfo_t));
+
+ EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database;
+ EXEC SQL OPEN cur1;
+
+ /* when end of result set reached, break out of while loop */
+ EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+ while (1)
+ {
+ /* Fetch multiple columns into one structure. */
+ EXEC SQL FETCH FROM cur1 INTO :dbval, :size;
+
+ /* Print members of the structure. */
+ printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size);
+ }
+
+ EXEC SQL CLOSE cur1;
+</programlisting>
+ </para>
+ </sect4>
+
+ <sect4>
+ <title>Typedefs</title>
+
+ <para>
+ Use the <literal>typedef</literal> keyword to map new types to already
+ existing types.
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+ typedef char mychartype[40];
+ typedef long serial_t;
+EXEC SQL END DECLARE SECTION;
+</programlisting>
+ Note that you could also use:
+<programlisting>
+EXEC SQL TYPE serial_t IS long;
+</programlisting>
+ This declaration does not need to be part of a declare section.
+ </para>
+ </sect4>
+
+ <sect4>
+ <title>Pointers</title>
+
+ <para>
+ You can declare pointers to the most common types. Note however
+ that you cannot use pointers as target variables of queries
+ without auto-allocation. See <xref linkend="ecpg-descriptors">
+ for more information on auto-allocation.
+ </para>
+
+ <para>
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+ int *intp;
+ char **charp;
+EXEC SQL END DECLARE SECTION;
+</programlisting>
+ </para>
+ </sect4>
+ </sect3>
+ </sect2>
+
+ <sect2 id="ecpg-variables-nonprimitive-sql">
+ <title>Handling nonprimitive SQL data types</title>
+
+ <para>
+ This section contains information on how to handle nonscalar and
+ user-defined SQL-level data types in ECPG applications. Note that
+ this is distinct from the handling of host variables of
+ nonprimitive types, described in the previous section.
+ </para>
+
+ <sect3>
+ <title>Arrays</title>
+
+ <para>
+ SQL-level arrays are not directly supported in ECPG. It is not
+ possible to simply map an SQL array into a C array host variable.
+ This will result in undefined behavior. Some workarounds exist,
+ however.
+ </para>
+
+ <para>
+ If a query accesses <emphasis>elements</emphasis> of an array
+ separately, then this avoids the use of arrays in ECPG. Then, a
+ host variable with a type that can be mapped to the element type
+ should be used. For example, if a column type is array of
+ <type>integer</type>, a host variable of type <type>int</type>
+ can be used. Also if the element type is <type>varchar</type>
+ or <type>text</type>, a host variable of type <type>char[]</type>
+ or <type>VARCHAR[]</type> can be used.
+ </para>
+
+ <para>
+ Here is an example. Assume the following table:
+<programlisting>
+CREATE TABLE t3 (
+ ii integer[]
+);
+
+testdb=> SELECT * FROM t3;
+ ii
+-------------
+ {1,2,3,4,5}
+(1 row)
+</programlisting>
+
+ The following example program retrieves the 4th element of the
+ array and stores it into a host variable of
+ type <type>int</type>:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int ii;
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[4] FROM t3;
+EXEC SQL OPEN cur1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ EXEC SQL FETCH FROM cur1 INTO :ii ;
+ printf("ii=%d\n", ii);
+}
+
+EXEC SQL CLOSE cur1;
+</programlisting>
+
+ This example shows the following result:
+<screen>
+ii=4
+</screen>
+ </para>
+
+ <para>
+ To map multiple array elements to the multiple elements in an
+ array type host variables each element of array column and each
+ element of the host variable array have to be managed separately,
+ for example:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int ii_a[8];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[1], ii[2], ii[3], ii[4] FROM t3;
+EXEC SQL OPEN cur1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ EXEC SQL FETCH FROM cur1 INTO :ii_a[0], :ii_a[1], :ii_a[2], :ii_a[3];
+ ...
+}
+</programlisting>
+ </para>
+
+ <para>
+ Note again that
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int ii_a[8];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii FROM t3;
+EXEC SQL OPEN cur1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ /* WRONG */
+ EXEC SQL FETCH FROM cur1 INTO :ii_a;
+ ...
+}
+</programlisting>
+ would not work correctly in this case, because you cannot map an
+ array type column to an array host variable directly.
+ </para>
+
+ <para>
+ Another workaround is to store arrays in their external string
+ representation in host variables of type <type>char[]</type>
+ or <type>VARCHAR[]</type>. For more details about this
+ representation, see <xref linkend="arrays-input">. Note that
+ this means that the array cannot be accessed naturally as an
+ array in the host program (without further processing that parses
+ the text representation).
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Composite types</title>
+
+ <para>
+ Composite types are not directly supported in ECPG, but an easy workaround is possible.
+ The
+ available workarounds are similar to the ones described for
+ arrays above: Either access each attribute separately or use the
+ external string representation.
+ </para>
+
+ <para>
+ For the following examples, assume the following type and table:
+<programlisting>
+CREATE TYPE comp_t AS (intval integer, textval varchar(32));
+CREATE TABLE t4 (compval comp_t);
+INSERT INTO t4 VALUES ( (256, 'PostgreSQL') );
+</programlisting>
+
+ The most obvious solution is to access each attribute separately.
+ The following program retrieves data from the example table by
+ selecting each attribute of the type <type>comp_t</type>
+ separately:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int intval;
+varchar textval[33];
+EXEC SQL END DECLARE SECTION;
+
+/* Put each element of the composite type column in the SELECT list. */
+EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4;
+EXEC SQL OPEN cur1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ /* Fetch each element of the composite type column into host variables. */
+ EXEC SQL FETCH FROM cur1 INTO :intval, :textval;
+
+ printf("intval=%d, textval=%s\n", intval, textval.arr);
+}
+
+EXEC SQL CLOSE cur1;
+</programlisting>
+ </para>
+
+ <para>
+ To enhance this example, the host variables to store values in
+ the <command>FETCH</command> command can be gathered into one
+ structure. For more details about the host variable in the
+ structure form, see <xref linkend="ecpg-variables-struct">.
+ To switch to the structure, the example can be modified as below.
+ The two host variables, <varname>intval</varname>
+ and <varname>textval</varname>, become members of
+ the <structname>comp_t</structname> structure, and the structure
+ is specified on the <command>FETCH</command> command.
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+typedef struct
+{
+ int intval;
+ varchar textval[33];
+} comp_t;
+
+comp_t compval;
+EXEC SQL END DECLARE SECTION;
+
+/* Put each element of the composite type column in the SELECT list. */
+EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4;
+EXEC SQL OPEN cur1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ /* Put all values in the SELECT list into one structure. */
+ EXEC SQL FETCH FROM cur1 INTO :compval;
+
+ printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr);
+}
+
+EXEC SQL CLOSE cur1;
+</programlisting>
+
+ Although a structure is used in the <command>FETCH</command>
+ command, the attribute names in the <command>SELECT</command>
+ clause are specified one by one. This can be enhanced by using
+ a <literal>*</literal> to ask for all attributes of the composite
+ type value.
+<programlisting>
+...
+EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).* FROM t4;
+EXEC SQL OPEN cur1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ /* Put all values in the SELECT list into one structure. */
+ EXEC SQL FETCH FROM cur1 INTO :compval;
+
+ printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr);
+}
+...
+</programlisting>
+ This way, composite types can be mapped into structures almost
+ seamlessly, even though ECPG does not understand the composite
+ type itself.
+ </para>
+
+ <para>
+ Finally, it is also possible to store composite type values in
+ their external string representation in host variables of
+ type <type>char[]</type> or <type>VARCHAR[]</type>. But that
+ way, it is not easily possible to access the fields of the value
+ from the host program.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>User-defined base types</title>
+
+ <para>
+ New user-defined base types are not directly supported by ECPG.
+ You can use the external string representation and host variables
+ of type <type>char[]</type> or <type>VARCHAR[]</type>, and this
+ solution is indeed appropriate and sufficient for many types.
+ </para>
+
+ <para>
+ Here is an example using the data type <type>complex</type> from
+ the example in <xref linkend="xtypes">. The external string
+ representation of that type is <literal>(%lf,%lf)</literal>,
+ which is defined in the
+ functions <function>complex_in()</function>
+ and <function>complex_out()</function> functions
+ in <xref linkend="xtypes">. The following example inserts the
+ complex type values <literal>(1,1)</literal>
+ and <literal>(3,3)</literal> into the
+ columns <literal>a</literal> and <literal>b</literal>, and select
+ them from the table after that.
+
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+ varchar a[64];
+ varchar b[64];
+EXEC SQL END DECLARE SECTION;
+
+ EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)');
+
+ EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex;
+ EXEC SQL OPEN cur1;
+
+ EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+ while (1)
+ {
+ EXEC SQL FETCH FROM cur1 INTO :a, :b;
+ printf("a=%s, b=%s\n", a.arr, b.arr);
+ }
+
+ EXEC SQL CLOSE cur1;
+</programlisting>
+
+ This example shows following result:
+<screen>
+a=(1,1), b=(3,3)
+</screen>
+ </para>
+
+ <para>
+ Another workaround is avoiding the direct use of the user-defined
+ types in ECPG and instead create a function or cast that converts
+ between the user-defined type and a primitive type that ECPG can
+ handle. Note, however, that type casts, especially implicit
+ ones, should be introduced into the type system very carefully.
+ </para>
+
+ <para>
+ For example,
+<programlisting>
+CREATE FUNCTION create_complex(r double, i double) RETURNS complex
+LANGUAGE SQL
+IMMUTABLE
+AS $$ SELECT $1 * complex '(1,0')' + $2 * complex '(0,1)' $$;
+</programlisting>
+ After this definition, the following
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+double a, b, c, d;
+EXEC SQL END DECLARE SECTION;
+
+a = 1;
+b = 2;
+c = 3;
+d = 4;
+
+EXEC SQL INSERT INTO test_complex VALUES (create_complex(:a, :b), create_complex(:c, :d));
+</programlisting>
+ has the same effect as
+<programlisting>
+EXEC SQL INSERT INTO test_complex VALUES ('(1,2)', '(3,4)');
+</programlisting>
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="ecpg-indicators">
+ <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 datum 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>
+
+ <para>
+ If the argument <literal>-r no_indicator</literal> is passed to
+ the preprocessor <command>ecpg</command>, it works in
+ <quote>no-indicator</quote> mode. In no-indicator mode, if no
+ indicator variable is specified, null values are signaled (on
+ input and output) for character string types as empty string and
+ for integer types as the lowest possible value for type (for
+ example, <symbol>INT_MIN</symbol> for <type>int</type>).
+ </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>
+
+ <sect2 id="ecpg-dynamic-without-result">
+ <title>Executing statements without a result set</title>
+
+ <para>
+ 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;
+
+EXEC SQL EXECUTE IMMEDIATE :stmt;
+</programlisting>
+ <command>EXECUTE IMMEDIATE</command> can be used for SQL
+ statements that do not return a result set (e.g.,
+ DDL, <command>INSERT</command>, <command>UPDATE</command>,
+ <command>DELETE</command>). You cannot execute statements that
+ retrieve data (e.g., <command>SELECT</command>) this way. The
+ next section describes how to do that.
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-dynamic-input">
+ <title>Executing a statement with input parameters</title>
+
+ <para>
+ 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;
+
+EXEC SQL PREPARE mystmt FROM :stmt;
+ ...
+EXEC SQL EXECUTE mystmt USING 42, 'foobar';
+</programlisting>
+ </para>
+
+ <para>
+ When you don't need the prepared statement anymore, you should
+ deallocate it:
+<programlisting>
+EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;
+</programlisting>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-dynamic-with-result">
+ <title>Executing a statement with a result set</title>
+
+ <para>
+ To execute an SQL statement with a single result row,
+ <command>EXECUTE</command> can be used. To save the result, add
+ an <literal>INTO</literal> clause.
+<programlisting><![CDATA[
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
+int v1, v2;
+VARCHAR v3[50];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL PREPARE mystmt FROM :stmt;
+ ...
+EXEC SQL EXECUTE mystmt INTO :v1, :v2, :v3 USING 37;
+]]>
+</programlisting>
+ An <command>EXECUTE</command> command can have an
+ <literal>INTO</literal> clause, a <literal>USING</literal> clause,
+ both, or neither.
+ </para>
+
+ <para>
+ If a query is expected to return more than one result row, a
+ cursor should be used, as in the following example.
+ (See <xref linkend="ecpg-cursors"> for more details about the
+ cursor.)
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+char dbaname[128];
+char datname[128];
+char *stmt = "SELECT u.usename as dbaname, d.datname "
+ " FROM pg_database d, pg_user u "
+ " WHERE d.datdba = u.usesysid";
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL CONNECT TO testdb AS con1 USER testuser;
+
+EXEC SQL PREPARE stmt1 FROM :stmt;
+
+EXEC SQL DECLARE cursor1 CURSOR FOR stmt1;
+EXEC SQL OPEN cursor1;
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+while (1)
+{
+ EXEC SQL FETCH cursor1 INTO :dbaname,:datname;
+ printf("dbaname=%s, datname=%s\n", dbaname, datname);
+}
+
+EXEC SQL CLOSE cursor1;
+
+EXEC SQL COMMIT;
+EXEC SQL DISCONNECT ALL;
+</programlisting>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="ecpg-pgtypes">
+ <title>pgtypes library</title>
+
+ <para>
+ The pgtypes library maps <productname>PostgreSQL</productname> database
+ types to C equivalents that can be used in C programs. It also offers
+ functions to do basic calculations with those types within C, i.e., without
+ the help of the <productname>PostgreSQL</productname> server. See the
+ following example:
+<programlisting><![CDATA[
+EXEC SQL BEGIN DECLARE SECTION;
+ date date1;
+ timestamp ts1, tsout;
+ interval iv1;
+ char *out;
+EXEC SQL END DECLARE SECTION;
+
+PGTYPESdate_today(&date1);
+EXEC SQL SELECT started, duration INTO :ts1, :iv1 FROM datetbl WHERE d=:date1;
+PGTYPEStimestamp_add_interval(&ts1, &iv1, &tsout);
+out = PGTYPEStimestamp_to_asc(&tsout);
+printf("Started + duration: %s\n", out);
+free(out);
+]]>
+</programlisting>
+ </para>
+
+ <sect2 id="ecpg-pgtypes-numeric">
+ <title>The numeric type</title>
+ <para>
+ The numeric type offers to do calculations with arbitrary precision. See
+ <xref linkend="datatype-numeric"> for the equivalent type in the
+ <productname>PostgreSQL</> server. Because of the arbitrary precision this
+ variable needs to be able to expand and shrink dynamically. That's why you
+ can only create numeric variables on the heap, by means of the
+ <function>PGTYPESnumeric_new</> and <function>PGTYPESnumeric_free</>
+ functions. The decimal type, which is similar but limited in precision,
+ can be created on the stack as well as on the heap.
+ </para>
+ <para>
+ The following functions can be used to work with the numeric type:
+ <variablelist>
+ <varlistentry>
+ <term><function>PGTYPESnumeric_new</function></term>
+ <listitem>
+ <para>
+ Request a pointer to a newly allocated numeric variable.
+<synopsis>
+numeric *PGTYPESnumeric_new(void);
+</synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_free</function></term>
+ <listitem>
+ <para>
+ Free a numeric type, release all of its memory.
+<synopsis>
+void PGTYPESnumeric_free(numeric *var);
+</synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_from_asc</function></term>
+ <listitem>
+ <para>
+ Parse a numeric type from its string notation.
+<synopsis>
+numeric *PGTYPESnumeric_from_asc(char *str, char **endptr);
+</synopsis>
+ Valid formats are for example:
+ <literal>-2</literal>,
+ <literal>.794</literal>,
+ <literal>+3.44</literal>,
+ <literal>592.49E07</literal> or
+ <literal>-32.84e-4</literal>.
+ If the value could be parsed successfully, a valid pointer is returned,
+ else the NULL pointer. At the moment ECPG always parses the complete
+ string and so it currently does not support to store the address of the
+ first invalid character in <literal>*endptr</literal>. You can safely
+ set <literal>endptr</literal> to NULL.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_to_asc</function></term>
+ <listitem>
+ <para>
+ Returns a pointer to a string allocated by <function>malloc</function> that contains the string
+ representation of the numeric type <literal>num</literal>.
+<synopsis>
+char *PGTYPESnumeric_to_asc(numeric *num, int dscale);
+</synopsis>
+ The numeric value will be printed with <literal>dscale</literal> decimal
+ digits, with rounding applied if necessary.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_add</function></term>
+ <listitem>
+ <para>
+ Add two numeric variables into a third one.
+<synopsis>
+int PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result);
+</synopsis>
+ The function adds the variables <literal>var1</literal> and
+ <literal>var2</literal> into the result variable
+ <literal>result</literal>.
+ The function returns 0 on success and -1 in case of error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_sub</function></term>
+ <listitem>
+ <para>
+ Subtract two numeric variables and return the result in a third one.
+<synopsis>
+int PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result);
+</synopsis>
+ The function subtracts the variable <literal>var2</literal> from
+ the variable <literal>var1</literal>. The result of the operation is
+ stored in the variable <literal>result</literal>.
+ The function returns 0 on success and -1 in case of error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_mul</function></term>
+ <listitem>
+ <para>
+ Multiply two numeric variables and return the result in a third one.
+<synopsis>
+int PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result);
+</synopsis>
+ The function multiplies the variables <literal>var1</literal> and
+ <literal>var2</literal>. The result of the operation is stored in the
+ variable <literal>result</literal>.
+ The function returns 0 on success and -1 in case of error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_div</function></term>
+ <listitem>
+ <para>
+ Divide two numeric variables and return the result in a third one.
+<synopsis>
+int PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result);
+</synopsis>
+ The function divides the variables <literal>var1</literal> by
+ <literal>var2</literal>. The result of the operation is stored in the
+ variable <literal>result</literal>.
+ The function returns 0 on success and -1 in case of error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_cmp</function></term>
+ <listitem>
+ <para>
+ Compare two numeric variables.
+<synopsis>
+int PGTYPESnumeric_cmp(numeric *var1, numeric *var2)
+</synopsis>
+ This function compares two numeric variables. In case of error,
+ <literal>INT_MAX</literal> is returned. On success, the function
+ returns one of three possible results:
+ <itemizedlist>
+ <listitem>
+ <para>
+ 1, if <literal>var1</> is bigger than <literal>var2</>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ -1, if <literal>var1</> is smaller than <literal>var2</>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 0, if <literal>var1</> and <literal>var2</> are equal
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_from_int</function></term>
+ <listitem>
+ <para>
+ Convert an int variable to a numeric variable.
+<synopsis>
+int PGTYPESnumeric_from_int(signed int int_val, numeric *var);
+</synopsis>
+ This function accepts a variable of type signed int and stores it
+ in the numeric variable <literal>var</>. Upon success, 0 is returned and
+ -1 in case of a failure.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_from_long</function></term>
+ <listitem>
+ <para>
+ Convert a long int variable to a numeric variable.
+<synopsis>
+int PGTYPESnumeric_from_long(signed long int long_val, numeric *var);
+</synopsis>
+ This function accepts a variable of type signed long int and stores it
+ in the numeric variable <literal>var</>. Upon success, 0 is returned and
+ -1 in case of a failure.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_copy</function></term>
+ <listitem>
+ <para>
+ Copy over one numeric variable into another one.
+<synopsis>
+int PGTYPESnumeric_copy(numeric *src, numeric *dst);
+</synopsis>
+ This function copies over the value of the variable that
+ <literal>src</literal> points to into the variable that <literal>dst</>
+ points to. It returns 0 on success and -1 if an error occurs.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_from_double</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type double to a numeric.
+<synopsis>
+int PGTYPESnumeric_from_double(double d, numeric *dst);
+</synopsis>
+ This function accepts a variable of type double and stores the result
+ in the variable that <literal>dst</> points to. It returns 0 on success
+ and -1 if an error occurs.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_to_double</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type numeric to double.
+<synopsis>
+int PGTYPESnumeric_to_double(numeric *nv, double *dp)
+</synopsis>
+ The function converts the numeric value from the variable that
+ <literal>nv</> points to into the double variable that <literal>dp</> points
+ to. It returns 0 on success and -1 if an error occurs, including
+ overflow. On overflow, the global variable <literal>errno</> will be set
+ to <literal>PGTYPES_NUM_OVERFLOW</> additionally.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_to_int</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type numeric to int.
+<synopsis>
+int PGTYPESnumeric_to_int(numeric *nv, int *ip);
+</synopsis>
+ The function converts the numeric value from the variable that
+ <literal>nv</> points to into the integer variable that <literal>ip</>
+ points to. It returns 0 on success and -1 if an error occurs, including
+ overflow. On overflow, the global variable <literal>errno</> will be set
+ to <literal>PGTYPES_NUM_OVERFLOW</> additionally.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_to_long</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type numeric to long.
+<synopsis>
+int PGTYPESnumeric_to_long(numeric *nv, long *lp);
+</synopsis>
+ The function converts the numeric value from the variable that
+ <literal>nv</> points to into the long integer variable that
+ <literal>lp</> points to. It returns 0 on success and -1 if an error
+ occurs, including overflow. On overflow, the global variable
+ <literal>errno</> will be set to <literal>PGTYPES_NUM_OVERFLOW</>
+ additionally.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_to_decimal</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type numeric to decimal.
+<synopsis>
+int PGTYPESnumeric_to_decimal(numeric *src, decimal *dst);
+</synopsis>
+ The function converts the numeric value from the variable that
+ <literal>src</> points to into the decimal variable that
+ <literal>dst</> points to. It returns 0 on success and -1 if an error
+ occurs, including overflow. On overflow, the global variable
+ <literal>errno</> will be set to <literal>PGTYPES_NUM_OVERFLOW</>
+ additionally.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESnumeric_from_decimal</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type decimal to numeric.
+<synopsis>
+int PGTYPESnumeric_from_decimal(decimal *src, numeric *dst);
+</synopsis>
+ The function converts the decimal value from the variable that
+ <literal>src</> points to into the numeric variable that
+ <literal>dst</> points to. It returns 0 on success and -1 if an error
+ occurs. Since the decimal type is implemented as a limited version of
+ the numeric type, overflow cannot occur with this conversion.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-pgtypes-date">
+ <title>The date type</title>
+ <para>
+ The date type in C enables your programs to deal with data of the SQL type
+ date. See <xref linkend="datatype-datetime"> for the equivalent type in the
+ <productname>PostgreSQL</> server.
+ </para>
+ <para>
+ The following functions can be used to work with the date type:
+ <variablelist>
+ <varlistentry id="PGTYPESdatefromtimestamp">
+ <term><function>PGTYPESdate_from_timestamp</function></term>
+ <listitem>
+ <para>
+ Extract the date part from a timestamp.
+<synopsis>
+date PGTYPESdate_from_timestamp(timestamp dt);
+</synopsis>
+ The function receives a timestamp as its only argument and returns the
+ extracted date part from this timestamp.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatefromasc">
+ <term><function>PGTYPESdate_from_asc</function></term>
+ <listitem>
+ <para>
+ Parse a date from its textual representation.
+<synopsis>
+date PGTYPESdate_from_asc(char *str, char **endptr);
+</synopsis>
+ The function receives a C char* string <literal>str</> and a pointer to
+ a C char* string <literal>endptr</>. At the moment ECPG always parses
+ the complete string and so it currently does not support to store the
+ address of the first invalid character in <literal>*endptr</literal>.
+ You can safely set <literal>endptr</literal> to NULL.
+ </para>
+ <para>
+ Note that the function always assumes MDY-formatted dates and there is
+ currently no variable to change that within ECPG.
+ </para>
+ <para>
+ <xref linkend="ecpg-pgtypesdate-from-asc-table"> shows the allowed input formats.
+ </para>
+ <table id="ecpg-pgtypesdate-from-asc-table">
+ <title>Valid input formats for <function>PGTYPESdate_from_asc</function></title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Input</entry>
+ <entry>Result</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>January 8, 1999</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>1999-01-08</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>1/8/1999</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>1/18/1999</literal></entry>
+ <entry><literal>January 18, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>01/02/03</literal></entry>
+ <entry><literal>February 1, 2003</literal></entry>
+ </row>
+ <row>
+ <entry><literal>1999-Jan-08</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>Jan-08-1999</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>08-Jan-1999</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>99-Jan-08</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>08-Jan-99</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>08-Jan-06</literal></entry>
+ <entry><literal>January 8, 2006</literal></entry>
+ </row>
+ <row>
+ <entry><literal>Jan-08-99</literal></entry>
+ <entry><literal>January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>19990108</literal></entry>
+ <entry><literal>ISO 8601; January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>990108</literal></entry>
+ <entry><literal>ISO 8601; January 8, 1999</literal></entry>
+ </row>
+ <row>
+ <entry><literal>1999.008</literal></entry>
+ <entry><literal>year and day of year</literal></entry>
+ </row>
+ <row>
+ <entry><literal>J2451187</literal></entry>
+ <entry><literal>Julian day</literal></entry>
+ </row>
+ <row>
+ <entry><literal>January 8, 99 BC</literal></entry>
+ <entry><literal>year 99 before the Common Era</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatetoasc">
+ <term><function>PGTYPESdate_to_asc</function></term>
+ <listitem>
+ <para>
+ Return the textual representation of a date variable.
+<synopsis>
+char *PGTYPESdate_to_asc(date dDate);
+</synopsis>
+ The function receives the date <literal>dDate</> as its only parameter.
+ It will output the date in the form <literal>1999-01-18</>, i.e., in the
+ <literal>YYYY-MM-DD</> format.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatejulmdy">
+ <term><function>PGTYPESdate_julmdy</function></term>
+ <listitem>
+ <para>
+ Extract the values for the day, the month and the year from a variable
+ of type date.
+<synopsis>
+void PGTYPESdate_julmdy(date d, int *mdy);
+</synopsis>
+ <!-- almost same description as for rjulmdy() -->
+ The function receives the date <literal>d</> and a pointer to an array
+ of 3 integer values <literal>mdy</>. The variable name indicates
+ the sequential order: <literal>mdy[0]</> will be set to contain the
+ number of the month, <literal>mdy[1]</> will be set to the value of the
+ day and <literal>mdy[2]</> will contain the year.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatemdyjul">
+ <term><function>PGTYPESdate_mdyjul</function></term>
+ <listitem>
+ <para>
+ Create a date value from an array of 3 integers that specify the
+ day, the month and the year of the date.
+<synopsis>
+void PGTYPESdate_mdyjul(int *mdy, date *jdate);
+</synopsis>
+ The function receives the array of the 3 integers (<literal>mdy</>) as
+ its first argument and as its second argument a pointer to a variable
+ of type date that should hold the result of the operation.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatedayofweek">
+ <term><function>PGTYPESdate_dayofweek</function></term>
+ <listitem>
+ <para>
+ Return a number representing the day of the week for a date value.
+<synopsis>
+int PGTYPESdate_dayofweek(date d);
+</synopsis>
+ The function receives the date variable <literal>d</> as its only
+ argument and returns an integer that indicates the day of the week for
+ this date.
+ <itemizedlist>
+ <listitem>
+ <para>
+ 0 - Sunday
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 1 - Monday
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 2 - Tuesday
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 3 - Wednesday
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 4 - Thursday
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 5 - Friday
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ 6 - Saturday
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatetoday">
+ <term><function>PGTYPESdate_today</function></term>
+ <listitem>
+ <para>
+ Get the current date.
+<synopsis>
+void PGTYPESdate_today(date *d);
+</synopsis>
+ The function receives a pointer to a date variable (<literal>d</>)
+ that it sets to the current date.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatefmtasc">
+ <term><function>PGTYPESdate_fmt_asc</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type date to its textual representation using a
+ format mask.
+<synopsis>
+int PGTYPESdate_fmt_asc(date dDate, char *fmtstring, char *outbuf);
+</synopsis>
+ The function receives the date to convert (<literal>dDate</>), the
+ format mask (<literal>fmtstring</>) and the string that will hold the
+ textual representation of the date (<literal>outbuf</>).
+ </para>
+ <para>
+ On success, 0 is returned and a negative value if an error occurred.
+ </para>
+ <para>
+ The following literals are the field specifiers you can use:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>dd</literal> - The number of the day of the month.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>mm</literal> - The number of the month of the year.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>yy</literal> - The number of the year as a two digit number.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>yyyy</literal> - The number of the year as a four digit number.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ddd</literal> - The name of the day (abbreviated).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>mmm</literal> - The name of the month (abbreviated).
+ </para>
+ </listitem>
+ </itemizedlist>
+ All other characters are copied 1:1 to the output string.
+ </para>
+ <para>
+ <xref linkend="ecpg-pgtypesdate-fmt-asc-example-table"> indicates a few possible formats. This will give
+ you an idea of how to use this function. All output lines are based on
+ the same date: November 23, 1959.
+ </para>
+ <table id="ecpg-pgtypesdate-fmt-asc-example-table">
+ <title>Valid input formats for <function>PGTYPESdate_fmt_asc</function></title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Format</entry>
+ <entry>Result</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>mmddyy</literal></entry>
+ <entry><literal>112359</literal></entry>
+ </row>
+ <row>
+ <entry><literal>ddmmyy</literal></entry>
+ <entry><literal>231159</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yymmdd</literal></entry>
+ <entry><literal>591123</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yy/mm/dd</literal></entry>
+ <entry><literal>59/11/23</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yy mm dd</literal></entry>
+ <entry><literal>59 11 23</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yy.mm.dd</literal></entry>
+ <entry><literal>59.11.23</literal></entry>
+ </row>
+ <row>
+ <entry><literal>.mm.yyyy.dd.</literal></entry>
+ <entry><literal>.11.1959.23.</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmm. dd, yyyy</literal></entry>
+ <entry><literal>Nov. 23, 1959</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmm dd yyyy</literal></entry>
+ <entry><literal>Nov 23 1959</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yyyy dd mm</literal></entry>
+ <entry><literal>1959 23 11</literal></entry>
+ </row>
+ <row>
+ <entry><literal>ddd, mmm. dd, yyyy</literal></entry>
+ <entry><literal>Mon, Nov. 23, 1959</literal></entry>
+ </row>
+ <row>
+ <entry><literal>(ddd) mmm. dd, yyyy</literal></entry>
+ <entry><literal>(Mon) Nov. 23, 1959</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESdatedefmtasc">
+ <term><function>PGTYPESdate_defmt_asc</function></term>
+ <listitem>
+ <para>
+ Use a format mask to convert a C <type>char*</type> string to a value of type
+ date.
+<synopsis>
+int PGTYPESdate_defmt_asc(date *d, char *fmt, char *str);
+</synopsis>
+ <!-- same description as rdefmtdate -->
+ The function receives a pointer to the date value that should hold the
+ result of the operation (<literal>d</>), the format mask to use for
+ parsing the date (<literal>fmt</>) and the C char* string containing
+ the textual representation of the date (<literal>str</>). The textual
+ representation is expected to match the format mask. However you do not
+ need to have a 1:1 mapping of the string to the format mask. The
+ function only analyzes the sequential order and looks for the literals
+ <literal>yy</literal> or <literal>yyyy</literal> that indicate the
+ position of the year, <literal>mm</literal> to indicate the position of
+ the month and <literal>dd</literal> to indicate the position of the
+ day.
+ </para>
+ <para>
+ <xref linkend="ecpg-rdefmtdate-example-table"> indicates a few possible formats. This will give
+ you an idea of how to use this function.
+ </para>
+ <table id="ecpg-rdefmtdate-example-table">
+ <title>Valid input formats for <function>rdefmtdate</function></title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Format</entry>
+ <entry>String</entry>
+ <entry>Result</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>ddmmyy</literal></entry>
+ <entry><literal>21-2-54</literal></entry>
+ <entry><literal>1954-02-21</literal></entry>
+ </row>
+ <row>
+ <entry><literal>ddmmyy</literal></entry>
+ <entry><literal>2-12-54</literal></entry>
+ <entry><literal>1954-12-02</literal></entry>
+ </row>
+ <row>
+ <entry><literal>ddmmyy</literal></entry>
+ <entry><literal>20111954</literal></entry>
+ <entry><literal>1954-11-20</literal></entry>
+ </row>
+ <row>
+ <entry><literal>ddmmyy</literal></entry>
+ <entry><literal>130464</literal></entry>
+ <entry><literal>1964-04-13</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmm.dd.yyyy</literal></entry>
+ <entry><literal>MAR-12-1967</literal></entry>
+ <entry><literal>1967-03-12</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yy/mm/dd</literal></entry>
+ <entry><literal>1954, February 3rd</literal></entry>
+ <entry><literal>1954-02-03</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmm.dd.yyyy</literal></entry>
+ <entry><literal>041269</literal></entry>
+ <entry><literal>1969-04-12</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yy/mm/dd</literal></entry>
+ <entry><literal>In the year 2525, in the month of July, mankind will be alive on the 28th day</literal></entry>
+ <entry><literal>2525-07-28</literal></entry>
+ </row>
+ <row>
+ <entry><literal>dd-mm-yy</literal></entry>
+ <entry><literal>I said on the 28th of July in the year 2525</literal></entry>
+ <entry><literal>2525-07-28</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmm.dd.yyyy</literal></entry>
+ <entry><literal>9/14/58</literal></entry>
+ <entry><literal>1958-09-14</literal></entry>
+ </row>
+ <row>
+ <entry><literal>yy/mm/dd</literal></entry>
+ <entry><literal>47/03/29</literal></entry>
+ <entry><literal>1947-03-29</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmm.dd.yyyy</literal></entry>
+ <entry><literal>oct 28 1975</literal></entry>
+ <entry><literal>1975-10-28</literal></entry>
+ </row>
+ <row>
+ <entry><literal>mmddyy</literal></entry>
+ <entry><literal>Nov 14th, 1985</literal></entry>
+ <entry><literal>1985-11-14</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-pgtypes-timestamp">
+ <title>The timestamp type</title>
+ <para>
+ The timestamp type in C enables your programs to deal with data of the SQL
+ type timestamp. See <xref linkend="datatype-datetime"> for the equivalent
+ type in the <productname>PostgreSQL</> server.
+ </para>
+ <para>
+ The following functions can be used to work with the timestamp type:
+ <variablelist>
+ <varlistentry id="PGTYPEStimestampfromasc">
+ <term><function>PGTYPEStimestamp_from_asc</function></term>
+ <listitem>
+ <para>
+ Parse a timestamp from its textual representation into a timestamp
+ variable.
+<synopsis>
+timestamp PGTYPEStimestamp_from_asc(char *str, char **endptr);
+</synopsis>
+ The function receives the string to parse (<literal>str</>) and a
+ pointer to a C char* (<literal>endptr</>).
+ At the moment ECPG always parses
+ the complete string and so it currently does not support to store the
+ address of the first invalid character in <literal>*endptr</literal>.
+ You can safely set <literal>endptr</literal> to NULL.
+ </para>
+ <para>
+ The function returns the parsed timestamp on success. On error,
+ <literal>PGTYPESInvalidTimestamp</literal> is returned and <varname>errno</> is
+ set to <literal>PGTYPES_TS_BAD_TIMESTAMP</>. See <xref linkend="PGTYPESInvalidTimestamp"> for important notes on this value.
+
+ </para>
+ <para>
+ In general, the input string can contain any combination of an allowed
+ date specification, a whitespace character and an allowed time
+ specification. Note that timezones are not supported by ECPG. It can
+ parse them but does not apply any calculation as the
+ <productname>PostgreSQL</> server does for example. Timezone
+ specifiers are silently discarded.
+ </para>
+ <para>
+ <xref linkend="ecpg-pgtypestimestamp-from-asc-example-table"> contains a few examples for input strings.
+ </para>
+ <table id="ecpg-pgtypestimestamp-from-asc-example-table">
+ <title>Valid input formats for <function>PGTYPEStimestamp_from_asc</function></title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Input</entry>
+ <entry>Result</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>1999-01-08 04:05:06</literal></entry>
+ <entry><literal>1999-01-08 04:05:06</literal></entry>
+ </row>
+ <row>
+ <entry><literal>January 8 04:05:06 1999 PST</literal></entry>
+ <entry><literal>1999-01-08 04:05:06</literal></entry>
+ </row>
+ <row>
+ <entry><literal>1999-Jan-08 04:05:06.789-8</literal></entry>
+ <entry><literal>1999-01-08 04:05:06.789 (time zone specifier ignored)</literal></entry>
+ </row>
+ <row>
+ <entry><literal>J2451187 04:05-08:00</literal></entry>
+ <entry><literal>1999-01-08 04:05:00 (time zone specifier ignored)</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPEStimestamptoasc">
+ <term><function>PGTYPEStimestamp_to_asc</function></term>
+ <listitem>
+ <para>
+ Converts a date to a C char* string.
+<synopsis>
+char *PGTYPEStimestamp_to_asc(timestamp tstamp);
+</synopsis>
+ The function receives the timestamp <literal>tstamp</> as
+ its only argument and returns an allocated string that contains the
+ textual representation of the timestamp.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPEStimestampcurrent">
+ <term><function>PGTYPEStimestamp_current</function></term>
+ <listitem>
+ <para>
+ Retrieve the current timestamp.
+<synopsis>
+void PGTYPEStimestamp_current(timestamp *ts);
+</synopsis>
+ The function retrieves the current timestamp and saves it into the
+ timestamp variable that <literal>ts</> points to.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPEStimestampfmtasc">
+ <term><function>PGTYPEStimestamp_fmt_asc</function></term>
+ <listitem>
+ <para>
+ Convert a timestamp variable to a C char* using a format mask.
+<synopsis>
+int PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr);
+</synopsis>
+ The function receives a pointer to the timestamp to convert as its
+ first argument (<literal>ts</>), a pointer to the output buffer
+ (<literal>output</>), the maximal length that has been allocated for
+ the output buffer (<literal>str_len</literal>) and the format mask to
+ use for the conversion (<literal>fmtstr</literal>).
+ </para>
+ <para>
+ Upon success, the function returns 0 and a negative value if an
+ error occurred.
+ </para>
+ <para>
+ You can use the following format specifiers for the format mask. The
+ format specifiers are the same ones that are used in the
+ <function>strftime</> function in <productname>libc</productname>. Any
+ non-format specifier will be copied into the output buffer.
+ <!-- This is from the FreeBSD man page:
+ http://www.freebsd.org/cgi/man.cgi?query=strftime&apropos=0&sektion=3&manpath=FreeBSD+7.0-current&format=html
+ -->
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>%A</literal> - is replaced by national representation of
+ the full weekday name.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%a</literal> - is replaced by national representation of
+ the abbreviated weekday name.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%B</literal> - is replaced by national representation of
+ the full month name.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%b</literal> - is replaced by national representation of
+ the abbreviated month name.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%C</literal> - is replaced by (year / 100) as decimal
+ number; single digits are preceded by a zero.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%c</literal> - is replaced by national representation of
+ time and date.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%D</literal> - is equivalent to
+ <literal>%m/%d/%y</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%d</literal> - is replaced by the day of the month as a
+ decimal number (01-31).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%E*</literal> <literal>%O*</literal> - POSIX locale
+ extensions. The sequences
+ <literal>%Ec</literal>
+ <literal>%EC</literal>
+ <literal>%Ex</literal>
+ <literal>%EX</literal>
+ <literal>%Ey</literal>
+ <literal>%EY</literal>
+ <literal>%Od</literal>
+ <literal>%Oe</literal>
+ <literal>%OH</literal>
+ <literal>%OI</literal>
+ <literal>%Om</literal>
+ <literal>%OM</literal>
+ <literal>%OS</literal>
+ <literal>%Ou</literal>
+ <literal>%OU</literal>
+ <literal>%OV</literal>
+ <literal>%Ow</literal>
+ <literal>%OW</literal>
+ <literal>%Oy</literal>
+ are supposed to provide alternative representations.
+ </para>
+ <para>
+ Additionally <literal>%OB</literal> implemented to represent
+ alternative months names (used standalone, without day mentioned).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%e</literal> - is replaced by the day of month as a decimal
+ number (1-31); single digits are preceded by a blank.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%F</literal> - is equivalent to <literal>%Y-%m-%d</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%G</literal> - is replaced by a year as a decimal number
+ with century. This year is the one that contains the greater part of
+ the week (Monday as the first day of the week).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%g</literal> - is replaced by the same year as in
+ <literal>%G</literal>, but as a decimal number without century
+ (00-99).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%H</literal> - is replaced by the hour (24-hour clock) as a
+ decimal number (00-23).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%h</literal> - the same as <literal>%b</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%I</literal> - is replaced by the hour (12-hour clock) as a
+ decimal number (01-12).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%j</literal> - is replaced by the day of the year as a
+ decimal number (001-366).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>%k</literal> - is replaced by the hour (24-hour clock) as a
+ decimal number (0-23); single digits are preceded by a blank.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>%l</literal> - is replaced by the hour (12-hour clock) as a
decimal number (1-12); single digits are preceded by a blank.
</para>
</listitem>
</varlistentry>
- <varlistentry id="PGTYPEStimestampsub">
- <term><function>PGTYPEStimestamp_sub</function></term>
+ <varlistentry id="PGTYPEStimestampsub">
+ <term><function>PGTYPEStimestamp_sub</function></term>
+ <listitem>
+ <para>
+ Subtract one timestamp from another one and save the result in a
+ variable of type interval.
+<synopsis>
+int PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv);
+</synopsis>
+ The function will subtract the timestamp variable that <literal>ts2</>
+ points to from the timestamp variable that <literal>ts1</> points to
+ and will store the result in the interval variable that <literal>iv</>
+ points to.
+ </para>
+ <para>
+ Upon success, the function returns 0 and a negative value if an
+ error occurred.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPEStimestampdefmtasc">
+ <term><function>PGTYPEStimestamp_defmt_asc</function></term>
+ <listitem>
+ <para>
+ Parse a timestamp value from its textual representation using a
+ formatting mask.
+<synopsis>
+int PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp *d);
+</synopsis>
+ The function receives the textual representation of a timestamp in the
+ variable <literal>str</> as well as the formatting mask to use in the
+ variable <literal>fmt</>. The result will be stored in the variable
+ that <literal>d</> points to.
+ </para>
+ <para>
+ If the formatting mask <literal>fmt</> is NULL, the function will fall
+ back to the default formatting mask which is <literal>%Y-%m-%d
+ %H:%M:%S</literal>.
+ </para>
+ <para>
+ This is the reverse function to <xref
+ linkend="PGTYPEStimestampfmtasc">. See the documentation there in
+ order to find out about the possible formatting mask entries.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPEStimestampaddinterval">
+ <term><function>PGTYPEStimestamp_add_interval</function></term>
+ <listitem>
+ <para>
+ Add an interval variable to a timestamp variable.
+<synopsis>
+int PGTYPEStimestamp_add_interval(timestamp *tin, interval *span, timestamp *tout);
+</synopsis>
+ The function receives a pointer to a timestamp variable <literal>tin</>
+ and a pointer to an interval variable <literal>span</>. It adds the
+ interval to the timestamp and saves the resulting timestamp in the
+ variable that <literal>tout</> points to.
+ </para>
+ <para>
+ Upon success, the function returns 0 and a negative value if an
+ error occurred.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPEStimestampsubinterval">
+ <term><function>PGTYPEStimestamp_sub_interval</function></term>
+ <listitem>
+ <para>
+ Subtract an interval variable from a timestamp variable.
+<synopsis>
+int PGTYPEStimestamp_sub_interval(timestamp *tin, interval *span, timestamp *tout);
+</synopsis>
+ The function subtracts the interval variable that <literal>span</>
+ points to from the timestamp variable that <literal>tin</> points to
+ and saves the result into the variable that <literal>tout</> points
+ to.
+ </para>
+ <para>
+ Upon success, the function returns 0 and a negative value if an
+ error occurred.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-pgtypes-interval">
+ <title>The interval type</title>
+ <para>
+ The interval type in C enables your programs to deal with data of the SQL
+ type interval. See <xref linkend="datatype-datetime"> for the equivalent
+ type in the <productname>PostgreSQL</> server.
+ </para>
+ <para>
+ The following functions can be used to work with the interval type:
+ <variablelist>
+
+ <varlistentry id="PGTYPESintervalnew">
+ <term><function>PGTYPESinterval_new</function></term>
+ <listitem>
+ <para>
+ Return a pointer to a newly allocated interval variable.
+<synopsis>
+interval *PGTYPESinterval_new(void);
+</synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESintervalfree">
+ <term><function>PGTYPESinterval_free</function></term>
+ <listitem>
+ <para>
+ Release the memory of a previously allocated interval variable.
+<synopsis>
+void PGTYPESinterval_new(interval *intvl);
+</synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESintervalfromasc">
+ <term><function>PGTYPESinterval_from_asc</function></term>
+ <listitem>
+ <para>
+ Parse an interval from its textual representation.
+<synopsis>
+interval *PGTYPESinterval_from_asc(char *str, char **endptr);
+</synopsis>
+ The function parses the input string <literal>str</> and returns a
+ pointer to an allocated interval variable.
+ At the moment ECPG always parses
+ the complete string and so it currently does not support to store the
+ address of the first invalid character in <literal>*endptr</literal>.
+ You can safely set <literal>endptr</literal> to NULL.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESintervaltoasc">
+ <term><function>PGTYPESinterval_to_asc</function></term>
+ <listitem>
+ <para>
+ Convert a variable of type interval to its textual representation.
+<synopsis>
+char *PGTYPESinterval_to_asc(interval *span);
+</synopsis>
+ The function converts the interval variable that <literal>span</>
+ points to into a C char*. The output looks like this example:
+ <literal>@ 1 day 12 hours 59 mins 10 secs</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="PGTYPESintervalcopy">
+ <term><function>PGTYPESinterval_copy</function></term>
+ <listitem>
+ <para>
+ Copy a variable of type interval.
+<synopsis>
+int PGTYPESinterval_copy(interval *intvlsrc, interval *intvldest);
+</synopsis>
+ The function copies the interval variable that <literal>intvlsrc</>
+ points to into the variable that <literal>intvldest</> points to. Note
+ that you need to allocate the memory for the destination variable
+ before.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-pgtypes-decimal">
+ <title>The decimal type</title>
+ <para>
+ The decimal type is similar to the numeric type. However it is limited to
+ a maximum precision of 30 significant digits. In contrast to the numeric
+ type which can be created on the heap only, the decimal type can be
+ created either on the stack or on the heap (by means of the functions
+ <function>PGTYPESdecimal_new</> and
+ <function>PGTYPESdecimal_free</>).
+ There are a lot of other functions that deal with the decimal type in the
+ <productname>Informix</productname> compatibility mode described in <xref
+ linkend="ecpg-informix-compat">.
+ </para>
+ <para>
+ The following functions can be used to work with the decimal type and are
+ not only contained in the <literal>libcompat</> library.
+ <variablelist>
+ <varlistentry>
+ <term><function>PGTYPESdecimal_new</function></term>
+ <listitem>
+ <para>
+ Request a pointer to a newly allocated decimal variable.
+<synopsis>
+decimal *PGTYPESdecimal_new(void);
+</synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><function>PGTYPESdecimal_free</function></term>
+ <listitem>
+ <para>
+ Free a decimal type, release all of its memory.
+<synopsis>
+void PGTYPESdecimal_free(decimal *var);
+</synopsis>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-pgtypes-errno">
+ <title>errno values of pgtypeslib</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><literal>PGTYPES_NUM_BAD_NUMERIC</literal></term>
+ <listitem>
+ <para>
+ An argument should contain a numeric variable (or point to a numeric
+ variable) but in fact its in-memory representation was invalid.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_NUM_OVERFLOW</literal></term>
+ <listitem>
+ <para>
+ An overflow occurred. Since the numeric type can deal with almost
+ arbitrary precision, converting a numeric variable into other types
+ might cause overflow.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_NUM_UNDERFLOW</literal></term>
+ <listitem>
+ <para>
+ An underflow occurred. Since the numeric type can deal with almost
+ arbitrary precision, converting a numeric variable into other types
+ might cause underflow.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_NUM_DIVIDE_ZERO</literal></term>
+ <listitem>
+ <para>
+ A division by zero has been attempted.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_DATE_BAD_DATE</literal></term>
+ <listitem>
+ <para>
+ An invalid date string was passed to
+ the <function>PGTYPESdate_from_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_DATE_ERR_EARGS</literal></term>
+ <listitem>
+ <para>
+ Invalid arguments were passed to the
+ <function>PGTYPESdate_defmt_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_DATE_ERR_ENOSHORTDATE</literal></term>
+ <listitem>
+ <para>
+ An invalid token in the input string was found by the
+ <function>PGTYPESdate_defmt_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_INTVL_BAD_INTERVAL</literal></term>
+ <listitem>
+ <para>
+ An invalid interval string was passed to the
+ <function>PGTYPESinterval_from_asc</function> function, or an
+ invalid interval value was passed to the
+ <function>PGTYPESinterval_to_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_DATE_ERR_ENOTDMY</literal></term>
+ <listitem>
+ <para>
+ There was a mismatch in the day/month/year assignment in the
+ <function>PGTYPESdate_defmt_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_DATE_BAD_DAY</literal></term>
+ <listitem>
+ <para>
+ An invalid day of the month value was found by
+ the <function>PGTYPESdate_defmt_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_DATE_BAD_MONTH</literal></term>
+ <listitem>
+ <para>
+ An invalid month value was found by
+ the <function>PGTYPESdate_defmt_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_TS_BAD_TIMESTAMP</literal></term>
+ <listitem>
+ <para>
+ An invalid timestamp string pass passed to
+ the <function>PGTYPEStimestamp_from_asc</function> function,
+ or an invalid timestamp value was passed to
+ the <function>PGTYPEStimestamp_to_asc</function> function.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGTYPES_TS_ERR_EINFTIME</literal></term>
+ <listitem>
+ <para>
+ An infinite timestamp value was encountered in a context that
+ cannot handle it.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="ecpg-pgtypes-constants">
+ <title>Special constants of pgtypeslib</title>
+ <para>
+ <variablelist>
+ <varlistentry id="PGTYPESInvalidTimestamp">
+ <term><literal>PGTYPESInvalidTimestamp</literal></term>
+ <listitem>
+ <para>
+ A value of type timestamp representing an invalid time stamp. This is
+ returned by the function <function>PGTYPEStimestamp_from_asc</> on
+ parse error.
+ Note that due to the internal representation of the <type>timestamp</type> data type,
+ <literal>PGTYPESInvalidTimestamp</literal> is also a valid timestamp at
+ the same time. It is set to <literal>1899-12-31 23:59:59</>. In order
+ to detect errors, make sure that your application does not only test
+ for <literal>PGTYPESInvalidTimestamp</literal> but also for
+ <literal>errno != 0</> after each call to
+ <function>PGTYPEStimestamp_from_asc</>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="ecpg-descriptors">
+ <title>Using Descriptor Areas</title>
+
+ <para>
+ An SQL descriptor area is a more sophisticated method for processing
+ the result of a <command>SELECT</command>, <command>FETCH</command> or
+ a <command>DESCRIBE</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 might
+ not be known ahead of time. PostgreSQL provides two ways to use
+ Descriptor Areas: the named SQL Descriptor Areas and the C-structure
+ SQLDAs.
+ </para>
+
+ <sect2 id="ecpg-named-descriptors">
+ <title>Named SQL descriptor areas</title>
+
+ <para>
+ A named 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>
+EXEC SQL ALLOCATE DESCRIPTOR <replaceable>identifier</replaceable>;
+</programlisting>
+ The identifier serves as the <quote>variable name</quote> of the
+ descriptor area. <remark>The scope of the allocated descriptor is WHAT?.</remark>
+ When you don't need the descriptor anymore, you should deallocate
+ it:
+<programlisting>
+EXEC SQL DEALLOCATE DESCRIPTOR <replaceable>identifier</replaceable>;
+</programlisting>
+ </para>
+
+ <para>
+ 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 SQL DESCRIPTOR mydesc;
+</programlisting>
+ If the result set is empty, the Descriptor Area will still contain
+ the metadata from the query, i.e. the field names.
+ </para>
+
+ <para>
+ For not yet executed prepared queries, the <command>DESCRIBE</command>
+ statement can be used to get the metadata of the result set:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+char *sql_stmt = "SELECT * FROM table1";
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL PREPARE stmt1 FROM :sql_stmt;
+EXEC SQL DESCRIBE stmt1 INTO SQL DESCRIPTOR mydesc;
+</programlisting>
+ </para>
+
+ <para>
+ Before PostgreSQL 9.0, the <literal>SQL</literal> keyword was optional,
+ so using <literal>DESCRIPTOR</literal> and <literal>SQL DESCRIPTOR</literal>
+ produced named SQL Descriptor Areas. Now it is mandatory, omitting
+ the <literal>SQL</literal> keyword produces SQLDA Descriptor Areas,
+ see <xref linkend="ecpg-sqlda-descriptors">.
+ </para>
+
+ <para>
+ In <command>DESCRIBE</command> and <command>FETCH</command> statements,
+ the <literal>INTO</literal> and <literal>USING</literal> keywords can be
+ used to similarly: they produce the result set and the metadata in a
+ Descriptor Area.
+ </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>
+ When <literal>TYPE</literal> is <literal>9</literal>,
+ <literal>DATETIME_INTERVAL_CODE</literal> will have a value of
+ <literal>1</literal> for <literal>DATE</literal>,
+ <literal>2</literal> for <literal>TIME</literal>,
+ <literal>3</literal> for <literal>TIMESTAMP</literal>,
+ <literal>4</literal> for <literal>TIME WITH TIME ZONE</literal>, or
+ <literal>5</literal> for <literal>TIMESTAMP WITH TIME ZONE</literal>.
+ </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>
+
+ <para>
+ In <command>EXECUTE</command>, <command>DECLARE</command> and <command>OPEN</command>
+ statements, the effect of the <literal>INTO</literal> and <literal>USING</literal>
+ keywords are different. A Descriptor Area can also be manually built to
+ provide the input parameters for a query or a cursor and
+ <literal>USING SQL DESCRIPTOR <replaceable>name</replaceable></literal>
+ is the way to pass the input parameters into a parametrized query. The statement
+ to build a named SQL Descriptor Area is below:
+<programlisting>
+EXEC SQL SET DESCRIPTOR <replaceable>name</replaceable> VALUE <replaceable>num</replaceable> <replaceable>field</replaceable> = :<replaceable>hostvar</replaceable>;
+</programlisting>
+ </para>
+
+ <para>
+ PostgreSQL supports retrieving more that one record in one <command>FETCH</command>
+ statement and storing the data in host variables in this case assumes that the
+ variable is an array. E.g.:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int id[5];
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL FETCH 5 FROM mycursor INTO SQL DESCRIPTOR mydesc;
+
+EXEC SQL GET DESCRIPTOR mydesc VALUE 1 :id = DATA;
+</programlisting>
+
+ </para>
+
+ </sect2>
+
+ <sect2 id="ecpg-sqlda-descriptors">
+ <title>SQLDA descriptor areas</title>
+
+ <para>
+ An SQLDA Descriptor Area is a C language structure which can be also used
+ to get the result set and the metadata of a query. One structure stores one
+ record from the result set.
+<programlisting>
+EXEC SQL include sqlda.h;
+sqlda_t *mysqlda;
+
+EXEC SQL FETCH 3 FROM mycursor INTO DESCRIPTOR mysqlda;
+</programlisting>
+ Note that the <literal>SQL</literal> keyword is omitted. The paragraphs about
+ the use cases of the <literal>INTO</literal> and <literal>USING</literal>
+ keywords in <xref linkend="ecpg-named-descriptors"> also apply here with an addition.
+ In a <command>DESCRIBE</command> statement the <literal>DESCRIPTOR</literal>
+ keyword can be completely omitted if the <literal>INTO</literal> keyword is used:
+<programlisting>
+EXEC SQL DESCRIBE prepared_statement INTO mysqlda;
+</programlisting>
+ </para>
+
+ <procedure>
+ <para>
+ The general flow of a program that uses SQLDA is:
+ </para>
+ <step><simpara>Prepare a query, and declare a cursor for it.</simpara></step>
+ <step><simpara>Declare an SQLDA for the result rows.</simpara></step>
+ <step><simpara>Declare an SQLDA for the input parameters, and initialize them (memory allocation, parameter settings).</simpara></step>
+ <step><simpara>Open a cursor with the input SQLDA.</simpara></step>
+ <step><simpara>Fetch rows from the cursor, and store them into an output SQLDA.</simpara></step>
+ <step><simpara>Read values from the output SQLDA into the host variables (with conversion if necessary).</simpara></step>
+ <step><simpara>Close the cursor.</simpara></step>
+ <step><simpara>Free the memory area allocated for the input SQLDA.</simpara></step>
+ </procedure>
+
+ <sect3>
+ <title>SQLDA data structure</title>
+
+ <para>
+ SQLDA uses three data structure
+ types: <type>sqlda_t</type>, <type>sqlvar_t</type>,
+ and <type>struct sqlname</type>.
+ </para>
+
+ <tip>
+ <para>
+ PostgreSQL's SQLDA has a similar data structure to the one in
+ IBM DB2 Universal Database, so some technical information on
+ DB2's SQLDA could help understanding PostgreSQL's one better.
+ </para>
+ </tip>
+
+ <sect4 id="ecpg-sqlda-sqlda">
+ <title>sqlda_t structure</title>
+
+ <para>
+ The structure type <type>sqlda_t</type> is the type of the
+ actual SQLDA. It holds one record. And two or
+ more <type>sqlda_t</type> structures can be connected in a
+ linked list with the pointer in
+ the <structfield>desc_next</structfield> field, thus
+ representing an ordered collection of rows. So, when two or
+ more rows are fetched, the application can read them by
+ following the <structfield>desc_next</structfield> pointer in
+ each <type>sqlda_t</type> node.
+ </para>
+
+ <para>
+ The definition of <type>sqlda_t</type> is:
+<programlisting>
+struct sqlda_struct
+{
+ char sqldaid[8];
+ long sqldabc;
+ short sqln;
+ short sqld;
+ struct sqlda_struct *desc_next;
+ struct sqlvar_struct sqlvar[1];
+};
+
+typedef struct sqlda_struct sqlda_t;
+</programlisting>
+
+ The meaning of the fields is:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>sqldaid</></term>
+ <listitem>
+ <para>
+ It contains the literal string <literal>"SQLDA "</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqldabc</></term>
+ <listitem>
+ <para>
+ It contains the size of the allocated space in bytes.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqln</></term>
+ <listitem>
+ <para>
+ It contains the number of input parameters for a parametrized query
+ case it's passed into <command>OPEN</command>, <command>DECLARE</command> or
+ <command>EXECUTE</command> statements using the <literal>USING</literal>
+ keyword. In case it's used as output of <command>SELECT</command>,
+ <command>EXECUTE</command> or <command>FETCH</command> statements,
+ its value is the same as <literal>sqld</literal>
+ statement
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqld</></term>
+ <listitem>
+ <para>
+ It contains the number of fields in a result set.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>desc_next</></term>
+ <listitem>
+ <para>
+ If the query returns more than one records, multiple linked SQLDA structures
+ are returned, the first record is stored in the SQLDA returned in the
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>sqlvar</></term>
+ <listitem>
+ <para>
+ This is the array of the columns in the result set.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect4>
+
+ <sect4 id="ecpg-sqlda-sqlvar">
+ <title>sqlvar_t structure</title>
+
+ <para>
+ The structure type <type>sqlvar_t</type> holds a column value
+ and metadata such as type and length. The definition of the type
+ is:
+
+<programlisting>
+struct sqlvar_struct
+{
+ short sqltype;
+ short sqllen;
+ char *sqldata;
+ short *sqlind;
+ struct sqlname sqlname;
+};
+
+typedef struct sqlvar_struct sqlvar_t;
+</programlisting>
+
+ The meaning of the fields is:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>sqltype</></term>
+ <listitem>
+ <para>
+ Contains the type identifier of the field. For values,
+ see <literal>enum ECPGttype</literal> in <literal>ecpgtype.h</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqllen</></term>
+ <listitem>
+ <para>
+ Contains the binary length of the field. e.g. 4 bytes for <type>ECPGt_int</type>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqldata</></term>
+ <listitem>
+ <para>
+ Points to the data. The format of the data is described
+ in <xref linkend="ecpg-variables-type-mapping">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqlind</></term>
+ <listitem>
+ <para>
+ Points to the null indicator. 0 means not null, -1 means
+ null.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>sqlname</></term>
+ <listitem>
+ <para>
+ The the name of the field.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect4>
+
+ <sect4 id="ecpg-sqlda-sqlname">
+ <title>struct sqlname structure</title>
+
+ <para>
+ A <type>struct sqlname</type> structure holds a column name. It
+ is used as a member of the <type>sqlvar_t</type> structure. The
+ definition of the structure is:
+<programlisting>
+#define NAMEDATALEN 64
+
+struct sqlname
+{
+ short length;
+ char data[NAMEDATALEN];
+};
+</programlisting>
+ The meaning of the fields is:
+ <variablelist>
+ <varlistentry>
+ <term><literal>length</></term>
+ <listitem>
+ <para>
+ Contains the length of the field name.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>data</></term>
+ <listitem>
+ <para>
+ Contains the actual field name.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect4>
+ </sect3>
+
+ <sect3 id="ecpg-sqlda-output">
+ <title>Retreiving a result set using an SQLDA</title>
+
+ <procedure>
+ <para>
+ The general steps to retrieve a query result set through an
+ SQLDA are:
+ </para>
+ <step><simpara>Declare an <type>sqlda_t</type> structure to receive the result set.</simpara></step>
+ <step><simpara>Execute <command>FETCH</>/<command>EXECUTE</>/<command>DESCRIBE</> commands to process a query specifying the declared SQLDA.</simpara></step>
+ <step><simpara>Check the number of records in the result set by looking at <structfield>sqln</>, a member of the <type>sqlda_t</type> structure.</simpara></step>
+ <step><simpara>Get the values of each column from <literal>sqlvar[0]</>, <literal>sqlvar[1]</>, etc., members of the <type>sqlda_t</type> structure.</simpara></step>
+ <step><simpara>Go to next row (<type>sqlda_t</type> structure) by following the <structfield>desc_next</> pointer, a member of the <type>sqlda_t</type> structure.</simpara></step>
+ <step><simpara>Repeat above as you need.</simpara></step>
+ </procedure>
+
+ <para>
+ Here is an example retrieving a result set through an SQLDA.
+ </para>
+
+ <para>
+ First, declare a <type>sqlda_t</type> structure to receive the result set.
+<programlisting>
+sqlda_t *sqlda1;
+</programlisting>
+ </para>
+
+ <para>
+ Next, specify the SQLDA in a command. This is
+ a <command>FETCH</> command example.
+<programlisting>
+EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1;
+</programlisting>
+ </para>
+
+ <para>
+ Run a loop following the linked list to retrieve the rows.
+<programlisting>
+sqlda_t *cur_sqlda;
+
+for (cur_sqlda = sqlda1;
+ cur_sqlda != NULL;
+ cur_sqlda = cur_sqlda->desc_next)
+{
+ ...
+}
+</programlisting>
+ </para>
+
+ <para>
+ Inside the loop, run another loop to retrieve each column data
+ (<type>sqlvar_t</type> structure) of the row.
+<programlisting>
+for (i = 0; i < cur_sqlda->sqld; i++)
+{
+ sqlvar_t v = cur_sqlda->sqlvar[i];
+ char *sqldata = v.sqldata;
+ short sqllen = v.sqllen;
+ ...
+}
+</programlisting>
+ </para>
+
+ <para>
+ To get a column value, check the <structfield>sqltype</> value,
+ a member of the <type>sqlvar_t</type> structure. Then, switch
+ to an appropriate way, depending on the column type, to copy
+ data from the <structfield>sqlvar</> field to a host variable.
+<programlisting>
+char var_buf[1024];
+
+switch (v.sqltype)
+{
+ case ECPGt_char:
+ memset(&var_buf, 0, sizeof(var_buf));
+ memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf) - 1 : sqllen));
+ break;
+
+ case ECPGt_int: /* integer */
+ memcpy(&intval, sqldata, sqllen);
+ snprintf(var_buf, sizeof(var_buf), "%d", intval);
+ break;
+
+ ...
+}
+</programlisting>
+ </para>
+ </sect3>
+
+ <sect3 id="ecpg-sqlda-input">
+ <title>Passing query parameters using an SQLDA</title>
+
+ <procedure>
+ <para>
+ The general steps to use an SQLDA to pass input
+ parameters to a prepared query are:
+ </para>
+ <step><simpara>Create a prepared query (prepared statement)</simpara></step>
+ <step><simpara>Declare a sqlda_t structure as an input SQLDA.</simpara></step>
+ <step><simpara>Allocate memory area (as sqlda_t structure) for the input SQLDA.</simpara></step>
+ <step><simpara>Set (copy) input values in the allocated memory.</simpara></step>
+ <step><simpara>Open a cursor with specifying the input SQLDA.</simpara></step>
+ </procedure>
+
+ <para>
+ Here is an example.
+ </para>
+
+ <para>
+ First, create a prepared statement.
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+char query[1024] = "SELECT d.oid, * FROM pg_database d, pg_stat_database s WHERE d.oid = s.datid AND (d.datname = ? OR d.oid = ?)";
+EXEC SQL END DECLARE SECTION;
+
+EXEC SQL PREPARE stmt1 FROM :query;
+</programlisting>
+ </para>
+
+ <para>
+ Next, allocate memory for an SQLDA, and set the number of input
+ parameters in <structfield>sqln</>, a member variable of
+ the <type>sqlda_t</type> structure. When two or more input
+ parameters are required for the prepared query, the application
+ has to allocate additional memory space which is calculated by
+ (nr. of params - 1) * sizeof(sqlvar_t). The example shown here
+ allocates memory space for two input parameters.
+<programlisting>
+sqlda_t *sqlda2;
+
+sqlda2 = (sqlda_t *) malloc(sizeof(sqlda_t) + sizeof(sqlvar_t));
+memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t));
+
+sqlda2->sqln = 2; /* number of input variables */
+</programlisting>
+ </para>
+
+ <para>
+ After memory allocation, store the parameter values into the
+ <literal>sqlvar[]</literal> array. (This is same array used for
+ retrieving column values when the SQLDA is receiving a result
+ set.) In this example, the input parameters
+ are <literal>"postgres"</literal>, having a string type,
+ and <literal>1</literal>, having an integer type.
+<programlisting>
+sqlda2->sqlvar[0].sqltype = ECPGt_char;
+sqlda2->sqlvar[0].sqldata = "postgres";
+sqlda2->sqlvar[0].sqllen = 8;
+
+int intval = 1;
+sqlda2->sqlvar[1].sqltype = ECPGt_int;
+sqlda2->sqlvar[1].sqldata = (char *) &intval;
+sqlda2->sqlvar[1].sqllen = sizeof(intval);
+</programlisting>
+ </para>
+
+ <para>
+ By opening a cursor and specifying the SQLDA that was set up
+ beforehand, the input parameters are passed to the prepared
+ statement.
+<programlisting>
+EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2;
+</programlisting>
+ </para>
+
+ <para>
+ Finally, after using input SQLDAs, the allocated memory space
+ must be freed explicitly, unlike SQLDAs used for receiving query
+ results.
+<programlisting>
+free(sqlda2);
+</programlisting>
+ </para>
+ </sect3>
+
+ <sect3 id="ecpg-sqlda-example">
+ <title>A sample application using SQLDA</title>
+
+ <para>
+ Here is an example program, which describes how to fetch access
+ statistics of the databases, specified by the input parameters,
+ from the system catalogs.
+ </para>
+
+ <para>
+ This application joins two system tables, pg_database and
+ pg_stat_database on the database oid, and also fetches and shows
+ the database statistics which are retreived by two input
+ parameters (a database "postgres", and oid "1").
+ </para>
+
+ <para>
+ First, declare an SQLDA for input and an SQLDA for output.
+<programlisting>
+EXEC SQL include sqlda.h;
+
+sqlda_t *sqlda1; /* an output descriptor */
+sqlda_t *sqlda2; /* an input descriptor */
+</programlisting>
+ </para>
+
+ <para>
+ Next, connect to the database, prepare a statement, and declare a
+ cursor for the prepared statement.
+<programlisting>
+int
+main(void)
+{
+ EXEC SQL BEGIN DECLARE SECTION;
+ char query[1024] = "SELECT d.oid,* FROM pg_database d, pg_stat_database s WHERE d.oid=s.datid AND ( d.datname=? OR d.oid=? )";
+ EXEC SQL END DECLARE SECTION;
+
+ EXEC SQL CONNECT TO testdb AS con1 USER testuser;
+
+ EXEC SQL PREPARE stmt1 FROM :query;
+ EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
+</programlisting>
+ </para>
+
+ <para>
+ Next, put some values in the input SQLDA for the input
+ parameters. Allocate memory for the input SQLDA, and set the
+ number of input parameters to <literal>sqln</literal>. Store
+ type, value, and value length into <literal>sqltype</literal>,
+ <literal>sqldata</literal>, and <literal>sqllen</literal> in the
+ <literal>sqlvar</literal> structure.
+
+<programlisting>
+ /* Create SQLDA structure for input parameters. */
+ sqlda2 = (sqlda_t *) malloc(sizeof(sqlda_t) + sizeof(sqlvar_t));
+ memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t));
+ sqlda2->sqln = 2; /* number of input variables */
+
+ sqlda2->sqlvar[0].sqltype = ECPGt_char;
+ sqlda2->sqlvar[0].sqldata = "postgres";
+ sqlda2->sqlvar[0].sqllen = 8;
+
+ intval = 1;
+ sqlda2->sqlvar[1].sqltype = ECPGt_int;
+ sqlda2->sqlvar[1].sqldata = (char *)&intval;
+ sqlda2->sqlvar[1].sqllen = sizeof(intval);
+</programlisting>
+ </para>
+
+ <para>
+ After setting up the input SQLDA, open a cursor with the input
+ SQLDA.
+
+<programlisting>
+ /* Open a cursor with input parameters. */
+ EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2;
+</programlisting>
+ </para>
+
+ <para>
+ Fetch rows into the output SQLDA from the opened cursor.
+ (Generally, you have to call <command>FETCH</command> repeatedly
+ in the loop, to fetch all rows in the result set.)
+<programlisting>
+ while (1)
+ {
+ sqlda_t *cur_sqlda;
+
+ /* Assign descriptor to the cursor */
+ EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1;
+</programlisting>
+ </para>
+
+ <para>
+ Next, retrieve the fetched records from the SQLDA, by following
+ the linked list of the <type>sqlda_t</type> structure.
+<programlisting>
+ for (cur_sqlda = sqlda1 ;
+ cur_sqlda != NULL ;
+ cur_sqlda = cur_sqlda->desc_next)
+ {
+ ...
+</programlisting>
+ </para>
+
+ <para>
+ Read each columns in the first record. The number of columns is
+ stored in <structfield>sqld</>, the actual data of the first
+ column is stored in <literal>sqlvar[0]</>, both members of
+ the <type>sqlda_t</type> structure.
+
+<programlisting>
+ /* Print every column in a row. */
+ for (i = 0; i < sqlda1->sqld; i++)
+ {
+ sqlvar_t v = sqlda1->sqlvar[i];
+ char *sqldata = v.sqldata;
+ short sqllen = v.sqllen;
+
+ strncpy(name_buf, v.sqlname.data, v.sqlname.length);
+ name_buf[v.sqlname.length] = '\0';
+</programlisting>
+ </para>
+
+ <para>
+ Now, the column data is stored in the variable <varname>v</>.
+ Copy every datum into host variables, looking
+ at <literal>v.sqltype</> for the type of the column.
+<programlisting>
+ switch (v.sqltype) {
+ int intval;
+ double doubleval;
+ unsigned long long int longlongval;
+
+ case ECPGt_char:
+ memset(&var_buf, 0, sizeof(var_buf));
+ memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf)-1 : sqllen));
+ break;
+
+ case ECPGt_int: /* integer */
+ memcpy(&intval, sqldata, sqllen);
+ snprintf(var_buf, sizeof(var_buf), "%d", intval);
+ break;
+
+ ...
+
+ default:
+ ...
+ }
+
+ printf("%s = %s (type: %d)\n", name_buf, var_buf, v.sqltype);
+ }
+</programlisting>
+ </para>
+
+ <para>
+ Close the cursor after processing all of records, and disconnect
+ from the database.
+<programlisting>
+ EXEC SQL CLOSE cur1;
+ EXEC SQL COMMIT;
+
+ EXEC SQL DISCONNECT ALL;
+</programlisting>
+ </para>
+
+ <para>
+ The whole program is shown
+ in <xref linkend="ecpg-sqlda-example-example">.
+ </para>
+
+ <example id="ecpg-sqlda-example-example">
+ <title>Example SQLDA program</title>
+<programlisting>
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+EXEC SQL include sqlda.h;
+
+sqlda_t *sqlda1; /* descriptor for output */
+sqlda_t *sqlda2; /* descriptor for input */
+
+EXEC SQL WHENEVER NOT FOUND DO BREAK;
+EXEC SQL WHENEVER SQLERROR STOP;
+
+int
+main(void)
+{
+ EXEC SQL BEGIN DECLARE SECTION;
+ char query[1024] = "SELECT d.oid,* FROM pg_database d, pg_stat_database s WHERE d.oid=s.datid AND ( d.datname=? OR d.oid=? )";
+
+ int intval;
+ unsigned long long int longlongval;
+ EXEC SQL END DECLARE SECTION;
+
+ EXEC SQL CONNECT TO uptimedb AS con1 USER uptime;
+
+ EXEC SQL PREPARE stmt1 FROM :query;
+ EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
+
+ /* Create a SQLDA structure for an input parameter */
+ sqlda2 = (sqlda_t *)malloc(sizeof(sqlda_t) + sizeof(sqlvar_t));
+ memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t));
+ sqlda2->sqln = 2; /* a number of input variables */
+
+ sqlda2->sqlvar[0].sqltype = ECPGt_char;
+ sqlda2->sqlvar[0].sqldata = "postgres";
+ sqlda2->sqlvar[0].sqllen = 8;
+
+ intval = 1;
+ sqlda2->sqlvar[1].sqltype = ECPGt_int;
+ sqlda2->sqlvar[1].sqldata = (char *) &intval;
+ sqlda2->sqlvar[1].sqllen = sizeof(intval);
+
+ /* Open a cursor with input parameters. */
+ EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2;
+
+ while (1)
+ {
+ sqlda_t *cur_sqlda;
+
+ /* Assign descriptor to the cursor */
+ EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1;
+
+ for (cur_sqlda = sqlda1 ;
+ cur_sqlda != NULL ;
+ cur_sqlda = cur_sqlda->desc_next)
+ {
+ int i;
+ char name_buf[1024];
+ char var_buf[1024];
+
+ /* Print every column in a row. */
+ for (i=0 ; i<cur_sqlda->sqld ; i++)
+ {
+ sqlvar_t v = cur_sqlda->sqlvar[i];
+ char *sqldata = v.sqldata;
+ short sqllen = v.sqllen;
+
+ strncpy(name_buf, v.sqlname.data, v.sqlname.length);
+ name_buf[v.sqlname.length] = '\0';
+
+ switch (v.sqltype)
+ {
+ case ECPGt_char:
+ memset(&var_buf, 0, sizeof(var_buf));
+ memcpy(&var_buf, sqldata, (sizeof(var_buf)<=sqllen ? sizeof(var_buf)-1 : sqllen) );
+ break;
+
+ case ECPGt_int: /* integer */
+ memcpy(&intval, sqldata, sqllen);
+ snprintf(var_buf, sizeof(var_buf), "%d", intval);
+ break;
+
+ case ECPGt_long_long: /* bigint */
+ memcpy(&longlongval, sqldata, sqllen);
+ snprintf(var_buf, sizeof(var_buf), "%lld", longlongval);
+ break;
+
+ default:
+ {
+ int i;
+ memset(var_buf, 0, sizeof(var_buf));
+ for (i = 0; i < sqllen; i++)
+ {
+ char tmpbuf[16];
+ snprintf(tmpbuf, sizeof(tmpbuf), "%02x ", (unsigned char) sqldata[i]);
+ strncat(var_buf, tmpbuf, sizeof(var_buf));
+ }
+ }
+ break;
+ }
+
+ printf("%s = %s (type: %d)\n", name_buf, var_buf, v.sqltype);
+ }
+
+ printf("\n");
+ }
+ }
+
+ EXEC SQL CLOSE cur1;
+ EXEC SQL COMMIT;
+
+ EXEC SQL DISCONNECT ALL;
+
+ return 0;
+}
+</programlisting>
+
+ <para>
+ The output of this example should look something like the
+ following (some numbers will vary).
+ </para>
+
+<screen>
+oid = 1 (type: 1)
+datname = template1 (type: 1)
+datdba = 10 (type: 1)
+encoding = 0 (type: 5)
+datistemplate = t (type: 1)
+datallowconn = t (type: 1)
+datconnlimit = -1 (type: 5)
+datlastsysoid = 11510 (type: 1)
+datfrozenxid = 379 (type: 1)
+dattablespace = 1663 (type: 1)
+datconfig = (type: 1)
+datacl = {=c/uptime,uptime=CTc/uptime} (type: 1)
+datid = 1 (type: 1)
+datname = template1 (type: 1)
+numbackends = 0 (type: 5)
+xact_commit = 113606 (type: 9)
+xact_rollback = 0 (type: 9)
+blks_read = 130 (type: 9)
+blks_hit = 7341714 (type: 9)
+tup_returned = 38262679 (type: 9)
+tup_fetched = 1836281 (type: 9)
+tup_inserted = 0 (type: 9)
+tup_updated = 0 (type: 9)
+tup_deleted = 0 (type: 9)
+
+oid = 11511 (type: 1)
+datname = postgres (type: 1)
+datdba = 10 (type: 1)
+encoding = 0 (type: 5)
+datistemplate = f (type: 1)
+datallowconn = t (type: 1)
+datconnlimit = -1 (type: 5)
+datlastsysoid = 11510 (type: 1)
+datfrozenxid = 379 (type: 1)
+dattablespace = 1663 (type: 1)
+datconfig = (type: 1)
+datacl = (type: 1)
+datid = 11511 (type: 1)
+datname = postgres (type: 1)
+numbackends = 0 (type: 5)
+xact_commit = 221069 (type: 9)
+xact_rollback = 18 (type: 9)
+blks_read = 1176 (type: 9)
+blks_hit = 13943750 (type: 9)
+tup_returned = 77410091 (type: 9)
+tup_fetched = 3253694 (type: 9)
+tup_inserted = 0 (type: 9)
+tup_updated = 0 (type: 9)
+tup_deleted = 0 (type: 9)
+</screen>
+ </example>
+ </sect3>
+ </sect2>
+ </sect1>
+
+ <sect1 id="ecpg-errors">
+ <title>Error Handling</title>
+
+ <para>
+ This section describes how you can handle exceptional conditions
+ and warnings in an embedded SQL program. There are two
+ nonexclusive facilities for this.
+
+ <itemizedlist>
+ <listitem>
+ <simpara>
+ Callbacks can be configured to handle warning and error
+ conditions using the <literal>WHENEVER</literal> command.
+ </simpara>
+ </listitem>
+
+ <listitem>
+ <simpara>
+ Detailed information about the error or warning can be obtained
+ from the <varname>sqlca</varname> variable.
+ </simpara>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <sect2 id="ecpg-whenever">
+ <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 <replaceable>condition</replaceable> <replaceable>action</replaceable>;
+</programlisting>
+ </para>
+
+ <para>
+ <replaceable>condition</replaceable> can be one of the following:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>SQLERROR</literal></term>
<listitem>
<para>
- Subtract one timestamp from another one and save the result in a
- variable of type interval.