Commit ad8305a4 authored by Michael Meskes's avatar Michael Meskes

Add DECLARE STATEMENT command to ECPG

This command declares a SQL identifier for a SQL statement to be used in other
embedded SQL statements. The identifier is linked to a connection.

Author: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Reviewed-by: default avatarShawn Wang <shawn.wang.pg@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/TY2PR01MB24438A52DB04E71D0E501452F5630@TY2PR01MB2443.jpnprd01.prod.outlook.com
parent 37c99d30
......@@ -278,7 +278,7 @@ EXEC SQL CONNECT TO :target USER :user USING :passwd;
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.
three ways to handle this.
</para>
<para>
......@@ -350,6 +350,46 @@ main()
current=testdb3 (should be testdb3)
current=testdb2 (should be testdb2)
current=testdb1 (should be testdb1)
</screen>
</para>
<para>
The third option is to declare sql identifier linked to
the connection, for example:
<programlisting>
EXEC SQL AT <replaceable>connection-name</replaceable> DECLARE <replaceable>statement-name</replaceable> STATEMENT;
EXEC SQL PREPARE <replaceable>statement-name</replaceable> FROM :<replaceable>dyn-string</replaceable>;
</programlisting>
Once you link a sql identifier to a connection, you execute a dynamic SQL
without AT clause. Note that this option behaves like preprocessor directives,
therefore the link is enabled only in the file.
</para>
<para>
Here is an example program using this option:
<programlisting><![CDATA[
#include <stdio.h>
EXEC SQL BEGIN DECLARE SECTION;
char dbname[128];
char *dym_sql = "SELECT current_database()";
EXEC SQL END DECLARE SECTION;
int main(){
EXEC SQL CONNECT TO postgres AS con1;
EXEC SQL CONNECT TO testdb AS con2;
EXEC SQL AT con1 DECLARE stmt STATEMENT;
EXEC SQL PREPARE stmt FROM :dym_sql;
EXEC SQL EXECUTE stmt INTO :dbname;
printf("%s\n", dbname);
EXEC SQL DISCONNECT ALL;
return 0;
}
]]></programlisting>
This example would produce this output, even if the default connection is testdb:
<screen>
postgres
</screen>
</para>
</sect2>
......@@ -6855,6 +6895,104 @@ EXEC SQL DECLARE cur1 CURSOR FOR stmt1;
</refsect1>
</refentry>
<refentry id="ecpg-sql-declare-statement">
<refnamediv>
<refname>DECLARE STATEMENT</refname>
<refpurpose>declare SQL statement identifier</refpurpose>
</refnamediv>
<refsynopsisdiv>
<synopsis>
EXEC SQL [ AT <replaceable class="parameter">connection_name</replaceable> ] DECLARE <replaceable class="parameter">statement_name</replaceable> STATEMENT
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>DECLARE STATEMENT</command> declares SQL statement identifier.
SQL statement identifier can be associated with the connection.
When the identifier is used by dynamic SQL statements, these SQLs are executed
by using the associated connection.
The namespace of the declaration is the precompile unit, and multiple declarations to
the same SQL statement identifier is not allowed.
Note that if the precompiler run in the Informix compatibility mode and some SQL statement
is declared, "database" can not be used as a cursor name.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable class="parameter">connection_name</replaceable></term>
<listitem>
<para>
A database connection name established by the <command>CONNECT</command> command.
</para>
<para>
AT clause can be omitted, but such statement has no meaning.
</para>
</listitem>
</varlistentry>
</variablelist>
<variablelist>
<varlistentry>
<term><replaceable class="parameter">statement_name</replaceable></term>
<listitem>
<para>
The name of a SQL statement identifier, either as an SQL identifier or a host variable.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para>
This association is valid only if the declaration is physically placed on top of a dynamic statement.
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<programlisting>
EXEC SQL CONNECT TO postgres AS con1;
EXEC SQL AT con1 DECLARE sql_stmt STATEMENT;
EXEC SQL DECLARE cursor_name CURSOR FOR sql_stmt;
EXEC SQL PREPARE sql_stmt FROM :dyn_string;
EXEC SQL OPEN cursor_name;
EXEC SQL FETCH cursor_name INTO :column1;
EXEC SQL CLOSE cursor_name;
</programlisting>
</refsect1>
<refsect1>
<title>Compatibility</title>
<para>
<command>DECLARE STATEMENT</command> is a extension of the SQL standard,
but can be used in famous DBMSs.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="ecpg-sql-connect"/></member>
<member><xref linkend="ecpg-sql-declare"/></member>
<member><xref linkend="ecpg-sql-open"/></member>
</simplelist>
</refsect1>
</refentry>
<refentry id="ecpg-sql-describe">
<refnamediv>
<refname>DESCRIBE</refname>
......
......@@ -33,6 +33,7 @@ ECPG: stmtUpdateStmt block
{ output_statement($1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block
{
check_declared_list($1.name);
if ($1.type == NULL || strlen($1.type) == 0)
output_statement($1.name, 1, ECPGst_execute);
else
......@@ -56,6 +57,7 @@ ECPG: stmtExecuteStmt block
}
ECPG: stmtPrepareStmt block
{
check_declared_list($1.name);
if ($1.type == NULL)
output_prepare_statement($1.name, $1.stmt);
else if (strlen($1.type) == 0)
......@@ -104,6 +106,10 @@ ECPG: stmtViewStmt rule
whenever_action(2);
free($1);
}
| ECPGDeclareStmt
{
output_simple_statement($1, 0);
}
| ECPGCursorStmt
{
output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
......@@ -244,14 +250,20 @@ ECPG: var_valueNumericOnly addon
$1 = mm_strdup("$0");
}
ECPG: fetch_argscursor_name addon
add_additional_variables($1, false);
struct cursor *ptr = add_additional_variables($1, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
if ($1[0] == ':')
{
free($1);
$1 = mm_strdup("$0");
}
ECPG: fetch_argsfrom_incursor_name addon
add_additional_variables($2, false);
struct cursor *ptr = add_additional_variables($2, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
if ($2[0] == ':')
{
free($2);
......@@ -262,14 +274,20 @@ ECPG: fetch_argsPRIORopt_from_incursor_name addon
ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
ECPG: fetch_argsLAST_Popt_from_incursor_name addon
ECPG: fetch_argsALLopt_from_incursor_name addon
add_additional_variables($3, false);
struct cursor *ptr = add_additional_variables($3, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
if ($3[0] == ':')
{
free($3);
$3 = mm_strdup("$0");
}
ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
add_additional_variables($3, false);
struct cursor *ptr = add_additional_variables($3, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
if ($3[0] == ':')
{
free($3);
......@@ -282,7 +300,10 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
}
ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
add_additional_variables($4, false);
struct cursor *ptr = add_additional_variables($4, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
if ($4[0] == ':')
{
free($4);
......@@ -292,7 +313,10 @@ ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
add_additional_variables($4, false);
struct cursor *ptr = add_additional_variables($4, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
if ($4[0] == ':')
{
free($4);
......@@ -348,6 +372,9 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
char *comment, *c1, *c2;
int (* strcmp_fn)(const char *, const char *) = (($2[0] == ':' || $2[0] == '"') ? strcmp : pg_strcasecmp);
if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp_fn($2, ptr->name) == 0)
......@@ -388,6 +415,17 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
ECPG: ClosePortalStmtCLOSEcursor_name block
{
char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
struct cursor *ptr = NULL;
for (ptr = cur; ptr != NULL; ptr = ptr -> next)
{
if (strcmp($2, ptr -> name) == 0)
{
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
break;
}
}
$$ = cat2_str(mm_strdup("close"), cursor_marker);
}
ECPG: opt_hold block
......@@ -466,49 +504,73 @@ ECPG: FetchStmtMOVEfetch_args rule
| FETCH FORWARD cursor_name opt_ecpg_fetch_into
{
char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
add_additional_variables($3, false);
struct cursor *ptr = add_additional_variables($3, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("fetch forward"), cursor_marker);
}
| FETCH FORWARD from_in cursor_name opt_ecpg_fetch_into
{
char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
add_additional_variables($4, false);
struct cursor *ptr = add_additional_variables($4, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("fetch forward from"), cursor_marker);
}
| FETCH BACKWARD cursor_name opt_ecpg_fetch_into
{
char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
add_additional_variables($3, false);
struct cursor *ptr = add_additional_variables($3, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("fetch backward"), cursor_marker);
}
| FETCH BACKWARD from_in cursor_name opt_ecpg_fetch_into
{
char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
add_additional_variables($4, false);
struct cursor *ptr = add_additional_variables($4, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker);
}
| MOVE FORWARD cursor_name
{
char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
add_additional_variables($3, false);
struct cursor *ptr = add_additional_variables($3, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("move forward"), cursor_marker);
}
| MOVE FORWARD from_in cursor_name
{
char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
add_additional_variables($4, false);
struct cursor *ptr = add_additional_variables($4, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("move forward from"), cursor_marker);
}
| MOVE BACKWARD cursor_name
{
char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
add_additional_variables($3, false);
struct cursor *ptr = add_additional_variables($3, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("move backward"), cursor_marker);
}
| MOVE BACKWARD from_in cursor_name
{
char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
add_additional_variables($4, false);
struct cursor *ptr = add_additional_variables($4, false);
if (ptr -> connection)
connection = mm_strdup(ptr -> connection);
$$ = cat_str(2, mm_strdup("move backward from"), cursor_marker);
}
ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block
......
......@@ -28,6 +28,7 @@ struct _include_path *include_paths = NULL;
struct cursor *cur = NULL;
struct typedefs *types = NULL;
struct _defines *defines = NULL;
struct declared_list *g_declared_list = NULL;
static void
help(const char *progname)
......@@ -347,6 +348,7 @@ main(int argc, char *const argv[])
struct cursor *ptr;
struct _defines *defptr;
struct typedefs *typeptr;
struct declared_list *list;
/* remove old cursor definitions if any are still there */
for (ptr = cur; ptr != NULL;)
......@@ -373,6 +375,13 @@ main(int argc, char *const argv[])
}
cur = NULL;
/* remove old delared statements if any are still there */
for (list = g_declared_list; list != NULL;)
{
struct declared_list *this = list;
free(this);
}
/* remove non-pertinent old defines as well */
while (defines && !defines->pertinent)
{
......
......@@ -64,6 +64,8 @@ static struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NUL
static void vmmerror(int error_code, enum errortype type, const char *error, va_list ap) pg_attribute_printf(3, 0);
static bool check_declared_list(const char*);
/*
* Handle parsing errors and warnings
*/
......@@ -576,6 +578,29 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
types = this;
}
}
/*
* check an SQL identifier is declared or not.
* If it is already declared, the global variable
* connection will be changed to the related connection.
*/
static bool
check_declared_list(const char *name)
{
struct declared_list *ptr = NULL;
for (ptr = g_declared_list; ptr != NULL; ptr = ptr -> next)
{
if (strcmp(name, ptr -> name) == 0)
{
if (ptr -> connection)
{
connection = mm_strdup(ptr -> connection);
return true;
}
}
}
return false;
}
%}
%expect 0
......
......@@ -290,6 +290,42 @@ prepared_name: name
| char_variable { $$ = $1; }
;
/*
* Declare Statement
*/
ECPGDeclareStmt: DECLARE prepared_name STATEMENT
{
struct declared_list *ptr = NULL;
/* Check whether the declared name has been defined or not */
for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
{
if (strcmp($2, ptr->name) == 0)
{
/* re-definition is not allowed */
mmerror(PARSE_ERROR, ET_ERROR, "declared name %s is already defined", ptr->name);
}
}
/* Add a new declared name into the g_declared_list */
ptr = NULL;
ptr = (struct declared_list *)mm_alloc(sizeof(struct declared_list));
if (ptr)
{
/* initial definition */
ptr -> name = $2;
if (connection)
ptr -> connection = mm_strdup(connection);
else
ptr -> connection = NULL;
ptr -> next = g_declared_list;
g_declared_list = ptr;
}
$$ = cat_str(3 , mm_strdup("/* declare "), mm_strdup($2), mm_strdup(" as an SQL identifier */"));
}
;
/*
* Declare a prepared cursor. The syntax is different from the standard
* declare statement, so we create a new rule.
......@@ -300,9 +336,14 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared
char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
int (* strcmp_fn)(const char *, const char *) = (($2[0] == ':' || $2[0] == '"') ? strcmp : pg_strcasecmp);
struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
const char *con = connection ? connection : "NULL";
char *comment;
char *con;
if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
check_declared_list($7);
con = connection ? connection : "NULL";
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp_fn($2, ptr->name) == 0)
......@@ -321,7 +362,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared
this->next = cur;
this->name = $2;
this->function = (current_function ? mm_strdup(current_function) : NULL);
this->connection = connection;
this->connection = connection ? mm_strdup(connection) : NULL;
this->command = cat_str(6, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for $1"));
this->argsresult = NULL;
this->argsresult_oos = NULL;
......
......@@ -9,6 +9,7 @@
%type <str> ECPGDeallocateDescr
%type <str> ECPGDeclaration
%type <str> ECPGDeclare
%type <str> ECPGDeclareStmt
%type <str> ECPGDescribe
%type <str> ECPGDisconnect
%type <str> ECPGExecuteImmediateStmt
......
......@@ -47,6 +47,7 @@ extern struct _include_path *include_paths;
extern struct cursor *cur;
extern struct typedefs *types;
extern struct _defines *defines;
extern struct declared_list *g_declared_list;
extern struct ECPGtype ecpg_no_indicator;
extern struct variable no_indicator;
extern struct arguments *argsinsert;
......
......@@ -141,6 +141,13 @@ struct cursor
struct cursor *next;
};
struct declared_list
{
char *name;
char *connection;
struct declared_list *next;
};
struct typedefs
{
char *name;
......
......@@ -53,6 +53,7 @@ test: sql/show
test: sql/insupd
test: sql/parser
test: sql/prepareas
test: sql/declare
test: thread/thread
test: thread/thread_implicit
test: thread/prep
......
This diff is collapsed.
This diff is collapsed.
****testcase1 test results:****
f1=1, f2=10, f3=db on con2
f1=2, f2=20, f3=db on con2
****testcase2 test results:****
f1=1, f2=10, f3=db on con1
f1=2, f2=20, f3=db on con1
****testcase3 test results:****
f1=1, f2=10, f3=db on con1
f1=2, f2=20, f3=db on con1
****testcase4 test results:****
f1=1, f2=10, f3=db on con2
f1=2, f2=20, f3=db on con2
****testcase5 test results:****
f1=1, f2=10, f3=db on con2
f1=2, f2=20, f3=db on con2
......@@ -10,6 +10,8 @@
/copystdout.c
/createtableas
/createtableas.c
/declare
/declare.c
/define
/define.c
/desc
......
......@@ -26,6 +26,7 @@ TESTS = array array.c \
insupd insupd.c \
twophase twophase.c \
insupd insupd.c \
declare declare.c \
bytea bytea.c \
prepareas prepareas.c
......
#include <locale.h>
#include <string.h>
#include <stdlib.h>
EXEC SQL WHENEVER SQLERROR SQLPRINT;
EXEC SQL INCLUDE sqlca;
EXEC SQL INCLUDE ../regression;
#define ARRAY_SZIE 20
void execute_test(void);
void commitTable(void);
void reset(void);
void printResult(char *tc_name, int loop);
EXEC SQL BEGIN DECLARE SECTION;
int f1[ARRAY_SZIE];
int f2[ARRAY_SZIE];
char f3[ARRAY_SZIE][20];
EXEC SQL END DECLARE SECTION;
int main(void)
{
setlocale(LC_ALL, "C");
ECPGdebug(1, stderr);
EXEC SQL CONNECT TO REGRESSDB1 AS con1;
EXEC SQL CONNECT TO REGRESSDB2 AS con2;
EXEC SQL AT con1 CREATE TABLE source(f1 integer, f2 integer, f3 varchar(20));
EXEC SQL AT con2 CREATE TABLE source(f1 integer, f2 integer, f3 varchar(20));
EXEC SQL AT con1 INSERT INTO source VALUES(1, 10, 'db on con1');
EXEC SQL AT con1 INSERT INTO source VALUES(2, 20, 'db on con1');
EXEC SQL AT con2 INSERT INTO source VALUES(1, 10, 'db on con2');
EXEC SQL AT con2 INSERT INTO source VALUES(2, 20, 'db on con2');
commitTable();
execute_test();
EXEC SQL AT con1 DROP TABLE IF EXISTS source;
EXEC SQL AT con2 DROP TABLE IF EXISTS source;
commitTable();
EXEC SQL DISCONNECT ALL;
return 0;
}
/*
* default connection: con2
* Non-default connection: con1
*
*/
void execute_test(void)
{
EXEC SQL BEGIN DECLARE SECTION;
int i;
char *selectString = "SELECT f1,f2,f3 FROM source";
EXEC SQL END DECLARE SECTION;
/*
* testcase1. using DECLARE STATEMENT without using AT clause,
* using PREPARE and CURSOR statement without using AT clause
*/
reset();
EXEC SQL DECLARE stmt_1 STATEMENT;
EXEC SQL PREPARE stmt_1 FROM :selectString;
EXEC SQL DECLARE cur_1 CURSOR FOR stmt_1;
EXEC SQL OPEN cur_1;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
i = 0;
while (1)
{
EXEC SQL FETCH cur_1 INTO :f1[i], :f2[i], :f3[i];
i++;
}
EXEC SQL CLOSE cur_1;
EXEC SQL DEALLOCATE PREPARE stmt_1;
EXEC SQL WHENEVER NOT FOUND CONTINUE;
printResult("testcase1", 2);
/*
* testcase2. using DECLARE STATEMENT at con1,
* using PREPARE and CURSOR statement without using AT clause
*/
reset();
EXEC SQL AT con1 DECLARE stmt_2 STATEMENT;
EXEC SQL PREPARE stmt_2 FROM :selectString;
EXEC SQL DECLARE cur_2 CURSOR FOR stmt_2;
EXEC SQL OPEN cur_2;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
i = 0;
while (1)
{
EXEC SQL FETCH cur_2 INTO :f1[i], :f2[i], :f3[i];
i++;
}
EXEC SQL CLOSE cur_2;
EXEC SQL DEALLOCATE PREPARE stmt_2;
EXEC SQL WHENEVER NOT FOUND CONTINUE;
printResult("testcase2", 2);
/*
* testcase3. using DECLARE STATEMENT at con1,
* using PREPARE and CURSOR statement at con2
*/
reset();
EXEC SQL AT con1 DECLARE stmt_3 STATEMENT;
EXEC SQL AT con2 PREPARE stmt_3 FROM :selectString;
EXEC SQL AT con2 DECLARE cur_3 CURSOR FOR stmt_3;
EXEC SQL AT con2 OPEN cur_3;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
i = 0;
while (1)
{
EXEC SQL AT con2 FETCH cur_3 INTO :f1[i], :f2[i], :f3[i];
i++;
}
EXEC SQL AT con2 CLOSE cur_3;
EXEC SQL AT con2 DEALLOCATE PREPARE stmt_3;
EXEC SQL WHENEVER NOT FOUND CONTINUE;
printResult("testcase3", 2);
/*
* testcase4. using DECLARE STATEMENT without using AT clause,
* using PREPARE and CURSOR statement at con2
*/
reset();
EXEC SQL DECLARE stmt_4 STATEMENT;
EXEC SQL AT con2 PREPARE stmt_4 FROM :selectString;
EXEC SQL AT con2 DECLARE cur_4 CURSOR FOR stmt_4;
EXEC SQL AT con2 OPEN cur_4;
EXEC SQL WHENEVER NOT FOUND DO BREAK;
i = 0;
while (1)
{
EXEC SQL AT con2 FETCH cur_4 INTO :f1[i], :f2[i], :f3[i];
i++;
}
EXEC SQL AT con2 CLOSE cur_4;
EXEC SQL AT con2 DEALLOCATE PREPARE stmt_4;
EXEC SQL WHENEVER NOT FOUND CONTINUE;
printResult("testcase4", 2);
/*
* testcase5. using DECLARE STATEMENT without using AT clause,
* using PREPARE and EXECUTE statement without using AT clause
*/
reset();
EXEC SQL DECLARE stmt_5 STATEMENT;
EXEC SQL PREPARE stmt_5 FROM :selectString;
EXEC SQL EXECUTE stmt_5 INTO :f1, :f2, :f3;
EXEC SQL DEALLOCATE PREPARE stmt_5;
printResult("testcase5", 2);
}
void commitTable()
{
EXEC SQL AT con1 COMMIT;
EXEC SQL AT con2 COMMIT;
}
/*
* reset all the output variables
*/
void reset()
{
memset(f1, 0, sizeof(f1));
memset(f2, 0, sizeof(f2));
memset(f3, 0, sizeof(f3));
}
void printResult(char *tc_name, int loop)
{
int i;
if (tc_name)
printf("****%s test results:****\n", tc_name);
for (i = 0; i < loop; i++)
printf("f1=%d, f2=%d, f3=%s\n", f1[i], f2[i], f3[i]);
printf("\n");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment