📄 ecpg.sgml
字号:
</programlisting> This structure is suitable for interfacing with SQL datums of type <type>varchar</type>. </para> </sect2> <sect2> <title><command>SELECT INTO</command> and <command>FETCH INTO</command></title> <para> Now you should be able to pass data generated by your program into an SQL command. But how do you retrieve the results of a query? For that purpose, embedded SQL provides special variants of the usual commands <command>SELECT</command> and <command>FETCH</command>. These commands have a special <literal>INTO</literal> clause that specifies which host variables the retrieved values are to be stored in. </para> <para> Here is an example:<programlisting>/* * assume this table: * CREATE TABLE test1 (a int, b varchar(50)); */EXEC SQL BEGIN DECLARE SECTION;int v1;VARCHAR v2;EXEC SQL END DECLARE SECTION; ...EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;</programlisting> So the <literal>INTO</literal> clause appears between the select list and the <literal>FROM</literal> clause. The number of elements in the select list and the list after <literal>INTO</literal> (also called the target list) must be equal. </para> <para> Here is an example using the command <command>FETCH</command>:<programlisting>EXEC SQL BEGIN DECLARE SECTION;int v1;VARCHAR v2;EXEC SQL END DECLARE SECTION; ...EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test; ...do { ... EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2; ...} while (...);</programlisting> Here the <literal>INTO</literal> clause appears after all the normal clauses. </para> <para> Both of these methods only allow retrieving one row at a time. If you need to process result sets that potentially contain more than one row, you need to use a cursor, as shown in the second example. </para> </sect2> <sect2> <title>Indicators</title> <para> The examples above do not handle null values. In fact, the retrieval examples will raise an error if they fetch a null value from the database. To be able to pass null values to the database or retrieve null values from the database, you need to append a second host variable specification to each host variable that contains data. This second host variable is called the <firstterm>indicator</firstterm> and contains a flag that tells whether the 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> </sect2> </sect1> <sect1 id="ecpg-dynamic"> <title>Dynamic SQL</title> <para> In many cases, the particular SQL statements that an application has to execute are known at the time the application is written. In some cases, however, the SQL statements are composed at run time or provided by an external source. In these cases you cannot embed the SQL statements directly into the C source code, but there is a facility that allows you to call arbitrary SQL statements that you provide in a string variable. </para> <para> The 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> You may not execute statements that retrieve data (e.g., <command>SELECT</command>) this way. </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:<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> If the statement you are executing returns values, then add an <literal>INTO</literal> clause:<programlisting>EXEC SQL BEGIN DECLARE SECTION;const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";int v1, v2;VARCHAR v3;EXEC SQL END DECLARE SECTION;EXEC SQL PREPARE mystmt FROM :stmt; ...EXEC SQL EXECUTE mystmt INTO v1, v2, v3 USING 37;</programlisting> An <command>EXECUTE</command> command may have an <literal>INTO</literal> clause, a <literal>USING</literal> clause, both, or neither. </para> <para> When you don't need the prepared statement anymore, you should deallocate it:<programlisting>EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;</programlisting> </para> </sect1> <sect1 id="ecpg-descriptors"> <title>Using SQL Descriptor Areas</title> <para> An SQL descriptor area is a more sophisticated method for processing the result of a <command>SELECT</command> or <command>FETCH</command> statement. An SQL descriptor area groups the data of one row of data together with metadata items into one data structure. The metadata is particularly useful when executing dynamic SQL statements, where the nature of the result columns may not be known ahead of time. </para> <para> An SQL descriptor area consists of a header, which contains information concerning the entire descriptor, and one or more item descriptor areas, which basically each describe one column in the result row. </para> <para> Before you can use an SQL descriptor area, you need to allocate one:<programlisting>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 DESCRIPTOR mydesc;</programlisting> </para> <para> Now how do you get the data out of the descriptor area? You can think of the descriptor area as a structure with named fields. To retrieve the value of a field from the header and store it into a host variable, use the following command:<programlisting>EXEC SQL GET DESCRIPTOR <replaceable>name</replaceable> :<replaceable>hostvar</replaceable> = <replaceable>field</replaceable>;</programlisting> Currently, there is only one header field defined: <replaceable>COUNT</replaceable>, which tells how many item descriptor areas exist (that is, how many columns are contained in the result). The host variable needs to be of an integer type. To get a field from the item descriptor area, use the following command:<programlisting>EXEC SQL GET DESCRIPTOR <replaceable>name</replaceable> VALUE <replaceable>num</replaceable> :<replaceable>hostvar</replaceable> = <replaceable>field</replaceable>;</programlisting> <replaceable>num</replaceable> can be a literal integer or a host variable containing an integer. Possible fields are: <variablelist> <varlistentry> <term><literal>CARDINALITY</literal> (integer)</term> <listitem> <para> number of rows in the result set </para> </listitem> </varlistentry> <varlistentry> <term><literal>DATA</literal></term> <listitem> <para> actual data item (therefore, the data type of this field depends on the query) </para> </listitem> </varlistentry> <varlistentry> <term><literal>DATETIME_INTERVAL_CODE</literal> (integer)</term> <listitem> <para> ? </para> </listitem> </varlistentry> <varlistentry> <term><literal>DATETIME_INTERVAL_PRECISION</literal> (integer)</term> <listitem> <para> not implemented </para> </listitem> </varlistentry> <varlistentry> <term><literal>INDICATOR</literal> (integer)</term> <listitem> <para> the indicator (indicating a null value or a value truncation) </para> </listitem> </varlistentry> <varlistentry> <term><literal>KEY_MEMBER</literal> (integer)</term> <listitem> <para> not implemented </para> </listitem> </varlistentry> <varlistentry> <term><literal>LENGTH</literal> (integer)</term> <listitem> <para> length of the datum in characters </para> </listitem> </varlistentry> <varlistentry> <term><literal>NAME</literal> (string)</term> <listitem> <para> name of the column </para> </listitem> </varlistentry> <varlistentry> <term><literal>NULLABLE</literal> (integer)</term> <listitem> <para> not implemented </para> </listitem> </varlistentry> <varlistentry> <term><literal>OCTET_LENGTH</literal> (integer)</term> <listitem> <para> length of the character representation of the datum in bytes </para> </listitem> </varlistentry> <varlistentry> <term><literal>PRECISION</literal> (integer)</term> <listitem> <para> precision (for type <type>numeric</type>) </para> </listitem> </varlistentry> <varlistentry> <term><literal>RETURNED_LENGTH</literal> (integer)</term> <listitem> <para> length of the datum in characters </para> </listitem> </varlistentry> <varlistentry> <term><literal>RETURNED_OCTET_LENGTH</literal> (integer)</term> <listitem> <para> length of the character representation of the datum in bytes </para> </listitem> </varlistentry> <varlistentry> <term><literal>SCALE</literal> (integer)</term> <listitem> <para> scale (for type <type>numeric</type>) </para> </listitem> </varlistentry> <varlistentry> <term><literal>TYPE</literal> (integer)</term> <listitem> <para> numeric code of the data type of the column </para> </listitem> </varlistentry> </variablelist> </para> </sect1> <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 several nonexclusive facilities for this. </para> <sect2> <title>Setting Callbacks</title> <para> One simple method to catch errors and warnings is to set a specific action to be executed whenever a particular condition occurs. In general:<programlisting>EXEC SQL WHENEVER <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> The specified action is called whenever an error occurs during the execution of an SQL statement. </para> </listitem> </varlistentry> <varlistentry> <term><literal>SQLWARNING</literal></term> <listitem> <para> The specified action is called whenever a warning occurs during the execution of an SQL statement. </para> </listitem> </varlistentry> <varlistentry> <term><literal>NOT FOUND</literal></term> <listitem> <para> The specified action is called whenever an SQL statement retrieves or affects zero rows. (This condition is not an error, but you might be interested in handling it specially.) </para> </listitem> </varlistentry> </variablelist> </para> <para> <replaceable>action</replaceable> can be one of the following: <variablelist> <varlistentry> <term><literal>CONTINUE</literal></term> <listitem> <para> This effectively means that the condition is ignored. This is the default. </para> </listitem> </varlistentry> <varlistentry> <term><literal>GOTO <replaceable>label</replaceable></literal></term> <term><literal>GO TO <replaceable>label</replaceable></literal></term> <listitem> <para> Jump to the specified label (using a C <literal>goto</literal> statement). </para> </listitem> </varlistentry> <varlistentry> <term><literal>SQLPRINT</literal></term> <listitem>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -