SQL statement PREPARE does not work in ECPG
Hi,
In the PostgreSQL Documentation, both ECPG PREPARE and SQL statement PREPARE can be used in ECPG [1]https://www.postgresql.org/docs/11/ecpg-sql-prepare.html.
However, SQL statement PREPARE does not work.
I wrote the source code as follows.
<test_app.pgc>
============================
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where id = $1;
EXEC SQL EXECUTE test_prep (2);
============================
PostgreSQL 11.2 ECPG produced following code.
<test_app.c>
============================
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "prepare \"test_prep\" ( int ) as \" select id from test_table where id = $1 \"", ECPGt_EOIT, ECPGt_EORT);
#line 16 "test_app.pgc"
if (sqlca.sqlcode < 0) error_exit ( );}
#line 16 "test_app.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "test_prep", ECPGt_EOIT, ECPGt_EORT);
#line 17 "test_app.pgc"
if (sqlca.sqlcode < 0) error_exit ( );}
#line 17 "test_app.pgc"
============================
There are following problems.
(1)
When I run this program, it failed with "PostgreSQL error : -202[too few arguments on line 16]".
The reason is ECPGdo has no argument though prepare statement has "$1".
(2)
I want to execute test_prep (2), but ECPGst_execute does not have argument.
Can SQL statement PREPARE be really used in ECPG?
[1]: https://www.postgresql.org/docs/11/ecpg-sql-prepare.html
Regards,
Ryohei Takahashi
Hi
I think SQL statement PREPARE *without* parameter is supported,
but one with parameter is not supported (or has some fatal bugs).
Because route for SQL statement PREPARE (line-1837 of preproc.y) always has output an invalid SQL statement and
there is no regression test for SQL statement PREPARE.
[preproc.y]
1832 | PrepareStmt
1833 {
1834 if ($1.type == NULL || strlen($1.type) == 0)
1835 output_prepare_statement($1.name, $1.stmt);
1836 else
1837 output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
1838 }
The next is log of ECPGdebug() and PQtrace() for the following statement.
exec sql prepare st(int) as select col1 from foo;
[14968]: ecpg_execute on line 17: query: prepare "st" ( int ) as " select 1 "; with 0 parameter(s) on connection conn To backend> Msg Q To backend> "prepare "st" ( int ) as " select 1 "" To backend> Msg complete, length 42 2019-02-19 06:23:30.429 UTC [14969] ERROR: syntax error at or near "" select 1 "" at character 25 2019-02-19 06:23:30.429 UTC [14969] STATEMENT: prepare "st" ( int ) as " select 1 "
To backend> Msg Q
To backend> "prepare "st" ( int ) as " select 1 ""
To backend> Msg complete, length 42
2019-02-19 06:23:30.429 UTC [14969] ERROR: syntax error at or near "" select 1 "" at character 25
2019-02-19 06:23:30.429 UTC [14969] STATEMENT: prepare "st" ( int ) as " select 1 "
Regards
Ryo Matsumura
I think SQL statement PREPARE *without* parameter is supported,
but one with parameter is not supported (or has some fatal bugs).
It surely should be supported.
I wrote the source code as follows.
<test_app.pgc>
============================
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where
id = $1;
EXEC SQL EXECUTE test_prep (2);
============================
Please try this instead:
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where id
= $1;
EXEC SQL EXECUTE test_prep using 2;
This should work.
And yes, it does look like a bug to me, or better like changes in the
backend that were not synced to ecpg.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Meskes-san,
Thank you for your replying.
Please try this instead:
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where id
= $1;
EXEC SQL EXECUTE test_prep using 2;This should work.
I tried as follows.
<test_app.pgc>
============================
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where id = $1;
EXEC SQL EXECUTE test_prep using 2;
============================
<test_app.c>
============================
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "prepare \"test_prep\" ( int ) as \" select id from test_table where id = $1 \"", ECPGt_EOIT, ECPGt_EORT);
#line 16 "test_app.pgc"
if (sqlca.sqlcode < 0) error_exit ( );}
#line 16 "test_app.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "test_prep",
ECPGt_const,"2",(long)1,(long)1,strlen("2"),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
#line 17 "test_app.pgc"
if (sqlca.sqlcode < 0) error_exit ( );}
#line 17 "test_app.pgc"
============================
Unfortunately, this does not work.
ECPGst_execute seems good, but prepare statement is the same as my first post.
It fails with "PostgreSQL error : -202[too few arguments on line 16]".
This error is caused by following source code.
<execute.c ecpg_build_params()>
/* Check if there are unmatched things left. */
if (next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
{
ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
ecpg_free_params(stmt, false);
return false;
}
<execute.c next_insert()>
if (text[p] == '$' && isdigit((unsigned char) text[p + 1]))
{
/* this can be either a dollar quote or a variable */
int i;
for (i = p + 1; isdigit((unsigned char) text[i]); i++)
/* empty loop body */ ;
if (!isalpha((unsigned char) text[i]) &&
isascii((unsigned char) text[i]) &&text[i] != '_')
/* not dollar delimited quote */
return p;
}
I think next_insert() should ignore "$n" in the case of SQL statement PREPARE.
In addition, we should fix following, right?
(1)
As Matsumura-san wrote, ECPG should not produce '"' for SQL statement PREPARE.
(2)
ECPG should produce argument for execute statement such as "EXEC SQL EXECUTE test_prep (2);"
Regards,
Ryohei Takahashi
Hi,
Maybe, there is no work-around.
For supporting it, there are two steps.
step1. fix for PREPARE.
step2. fix for EXECUTE.
About step1, there are two way.
I want to choose Idea-2.
Idea-1.
ecpglib prepares Oids of type listed in PREPARE statement for 5th argument of PQprepare().
But it's difficult because ecpg has to know Oids of type.
# Just an idea, create an Oid list in parsing.
Idea-2.
Use ECPGdo with whole PREPARE statement. In this way, there is no problem about Oid type because backend resolves it.
I think the current implementation may aim to it.
If we choose Idea-2, we should make a valid SQL-command(remove double quotation) and avoid any processing about prep_type_clause and PreparableStmt except for parsing.
One of such processing is the checking a number of parameters that occured the error.
It may take time, but it's easier than Idea-1.
Is the direction of fixing good?
About step2, there is the work-arround pointed by Meskes-san.
Regards
Ryo Matsumura
Matsumura-san,
Maybe, there is no work-around.
Did you analyze the bug? Do you know where it comes from?
For supporting it, there are two steps.
Could you please start with explaining where you see the problem? I'm
actually not sure what you are trying to do here.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Takahashi-san,
I tried as follows.
...
Unfortunately, this does not work.
ECPGst_execute seems good, but prepare statement is the same as my
first post.
Ah right, my bad. The workaround should have been:
EXEC SQL PREPARE test_prep from "SELECT id from test_table where id =
$1";
EXEC SQL EXECUTE test_prep using 2;
It fails with "PostgreSQL error : -202[too few arguments on line
16]".This error is caused by following source code.
...
I think next_insert() should ignore "$n" in the case of SQL statement
PREPARE.
Actually I am not so sure.
In addition, we should fix following, right?
(1)
As Matsumura-san wrote, ECPG should not produce '"' for SQL statement
PREPARE.
Why's that?
(2)
ECPG should produce argument for execute statement such as "EXEC SQL
EXECUTE test_prep (2);"
Correct.
As for the PREPARE statement itself, could you try the attached small
patch please.
This seems to create the correct ECPGPrepare call, but I have not yet
tested it for many use cases.
Thanks.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Attachments:
prepare.difftext/x-patch; charset=UTF-8; name=prepare.diffDownload
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index e8052819b0..bfd76960a2 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -22,12 +22,7 @@ ECPG: stmtUpdateStmt block
ECPG: stmtExecuteStmt block
{ output_statement($1, 1, ECPGst_execute); }
ECPG: stmtPrepareStmt block
- {
- if ($1.type == NULL || strlen($1.type) == 0)
- output_prepare_statement($1.name, $1.stmt);
- else
- output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
- }
+ { output_prepare_statement($1.name, $1.stmt); }
ECPG: stmtTransactionStmt block
{
fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
Takahashi-san,
EXEC SQL EXECUTE test_prep (2);
Could you please also verify for me if this works correctly if you use
a variable instead of the const? As in:
EXEC SQL BEGIN DECLARE SECTION;
int i=2;
EXEC SQL END DECLARE SECTION;
...
EXEC SQL EXECUTE test_prep (:i);
I guess the problem is that constants in this subtree are move to the
output literally instead treated as parameters.
Thanks.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Meskes-san,
Ah right, my bad. The workaround should have been:
Thank you. It works.
As for the PREPARE statement itself, could you try the attached small
patch please.
It works well for my statement
"EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where id = $1;".
However, since data type information is not used, it does not works well
for prepare statements which need data type information such as
"EXEC SQL PREPARE test_prep (int, int) AS SELECT $1 + $2;".
It fails with "PostgreSQL error : -400[operator is not unique: unknown + unknown on line 20]".
(Of course, "EXEC SQL PREPARE test_prep AS SELECT $1::int + $2::int;" works well.)
Could you please also verify for me if this works correctly if you use
a variable instead of the const? As in:
EXEC SQL BEGIN DECLARE SECTION;
int i=2;
EXEC SQL END DECLARE SECTION;
...
EXEC SQL EXECUTE test_prep (:i);
It also works.
(Actually, I wrote "EXEC SQL EXECUTE test_prep (:i) INTO :ID;".)
ECPG produced as follows.
============================
ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "test_prep",
ECPGt_int,&(i),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,
ECPGt_int,&(ID),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
============================
Regards,
Ryohei Takahashi
Takahashi-san,
It works well for my statement
"EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where
id = $1;".However, since data type information is not used, it does not works
well
for prepare statements which need data type information such as
"EXEC SQL PREPARE test_prep (int, int) AS SELECT $1 + $2;".
Yes, that was to be expected. This is what Matsumura-san was trying to
fix. However, I'm not sure yet which of his ideas is the best.
It also works.
(Actually, I wrote "EXEC SQL EXECUTE test_prep (:i) INTO :ID;".)
Ok, thanks. That means the parser has to handle the list of execute
arguments differently, which in hindsight is pretty obvious.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Did you analyze the bug? Do you know where it comes from?
At first, I show the flow of Prepare statement without AS clause and
the flow of Prepare statement with AS clause but without parameter list.
[preproc/preproc.y]
1832 | PrepareStmt
1834 if ($1.type == NULL || strlen($1.type) == 0)
1835 output_prepare_statement($1.name, $1.stmt);
[preproc/output.c]
168 output_prepare_statement(char *name, char *stmt)
169 {
170 fprintf(base_yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
171 output_escaped_str(name, true);
172 fputs(", ", base_yyout);
173 output_escaped_str(stmt, true);
174 fputs(");", base_yyout);
It makes the following C-program and it can work.
/* exec sql prepare st as select 1; */
ECPGprepare(__LINE__, NULL, 0, "st", " select 1 ");
/* exec sql prepare st from "select 1"; */
ECPGprepare(__LINE__, NULL, 0, "st", "select 1");
/* exec sql prepare st from "select ?"; */
ECPGprepare(__LINE__, NULL, 0, "st", "select ?");
ecpglib processes as the following:
[ecpglib/prepare.c]
174 ECPGprepare(int lineno, const char *connection_name, const bool questionmarks,
175 const char *name, const char *variable)
199 this = ecpg_find_prepared_statement(name, con, &prev);
200 if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
201 return false;
203 return prepare_common(lineno, con, name, variable);
[ecpglib/prepare.c]
115 prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
135 stmt->lineno = lineno;
136 stmt->connection = con;
137 stmt->command = ecpg_strdup(variable, lineno);
138 stmt->inlist = stmt->outlist = NULL;
141 replace_variables(&(stmt->command), lineno);
144 this->name = ecpg_strdup(name, lineno);
145 this->stmt = stmt;
148 query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
The following is log of PQtrace().
To backend> Msg P
To backend> "st"
To backend> "select $1"
To backend (2#)> 0
[6215]: prepare_common on line 21: name st; query: "select $1"
An important point of the route is that it calls PQprepare() and PQprepare()
needs type-Oid list. (Idea-1) If we fix for Prepare statement with AS clause and
with parameter list to walk through the route, preprocessor must parse the parameter list and
preprocessor or ecpglib must make type-Oid list. I think it's difficult.
Especially, I wonder if it can treat user defined type and complex structure type.
At second, I show the flow of Prepare statement with AS clause.
1836 else
1837 output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
It makes the following C-program, but it cannot work because AS clause is double quoted.
So there is no work-around for this route.
/* exec sql prepare st(int) as select $1; */
ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "prepare \"st\" ( int ) as \" select $1 \"", ECPGt_EOIT, ECPGt_EORT);
When it runs, the following error is occured.
[5895]: raising sqlcode -202 on line 20: too few arguments on line 20 SQL error: too few arguments on line 20
SQL error: too few arguments on line 20
The following may be expected.
ECPGdo(__LINE__, 0 , 1, NULL, 0, ECPGst_normal, "prepare st ( int ) as select $1 ", ECPGt_EOIT, ECPGt_EORT);
Even if the above C-program is made, another error is occured.
The error is occured in the following flow.
[ecpglib/execute.c]
1196 ecpg_build_params(struct statement *stmt)
1214 var = stmt->inlist;
1215 while (var)
ecpg_store_input(var--->tobeinserted)
1393 if ((position = next_insert(stmt->command, position, stmt->questionmarks, std_strings) + 1) == 0)
1411 if (var->type == ECPGt_char_variable)
1413 int ph_len = (stmt->command[position] == '?') ? strlen("?") : strlen("$1");
1415 if (!insert_tobeinserted(position, ph_len, stmt, tobeinserted))
1428 else if (stmt->command[position] == '0')
1430 if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
1437 else
1468 if (stmt->command[position] == '?')
1480 snprintf(tobeinserted, buffersize, "$%d", counter++);
1474 if (!(tobeinserted = (char *) ecpg_alloc(buffersize, stmt->lineno)))
1492 var = var->next;
1493 }
1495 /* Check if there are unmatched things left. */
1496 if (next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
1497 {
1498 ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
1499 ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
*** The above is raised. ***
The checking (line-1495) is meaningless for AS clause.
It checks if all $0 is replaced to literal and all ? is replaced to $[0-9]* by insert_tobeinserted(),
but it always fails because $[0-9]* in AS clause are not replaced (and should not be replaced).
I don't search if there is other similar case. It is Idea-2.
What is ECPGt_char_variable?
[preproc.y]
65 static struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NULL}, 0};
15333 ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
15367 thisquery->type = &ecpg_query;
15381 add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
What is $0?
In ECPG, the followings can be specified by host variable.
- cursor name
- value of ALTER SYSTEM SET statement
e.g. ALTER SYSTEM SET aaaa = $1
- fetch counter
e.g. FETCH ABSOLUTE count
Basically, ECPG-preprocessor changes the host variables to $[0-9]* and adds
variables to arguments of ECPGdo, and ecpglib calls PQexecParams(stmt, vars).
In case of the above, they cannot be passed to vars of PQexecParams() because
backend cannot accept them.
So ecpg_build_params() replace $0 to literal.
Regards
Ryo Matsumura
Meskes-san
I made mistake.
The checking (line-1495) is meaningless for AS clause.
It checks if all $0 is replaced to literal and all ? is replaced to $[0-9]* by insert_tobeinserted(),
but it always fails because $[0-9]* in AS clause are not replaced (and should not be replaced).
I don't search if there is other similar case. It is Idea-2.
It checks if a number of variables equals a number of $* after replacing $0 and ?.
It always fails because there is no variable for $* in AS clause.
We should skip AS clause at the cheking.
Umm... The skipping seems to be not easy too.
next_insert(char *text, int pos, bool questionmarks, bool std_strings)
{
pos = get_pos_of_as_clause(text); <-- parse text in ecpglib???
for (; text[p] != '\0'; p++)
if(is_prepare_statement(stmt) && invalid_pos(pos))
break;
Regards
Ryo Matsumura
Hi Matsumura-san,
Thank you for your explaining.
An important point of the route is that it calls PQprepare() and PQprepare()
needs type-Oid list. (Idea-1) If we fix for Prepare statement with AS clause and
with parameter list to walk through the route, preprocessor must parse the parameter list and
preprocessor or ecpglib must make type-Oid list. I think it's difficult.
Especially, I wonder if it can treat user defined type and complex structure type.
I agree.
In the case of standard types, ECPG can get oids from pg_type.h.
However, in the case of user defined types, ECPG needs to access pg_type table and it is overhead.
Therefore, the second idea seems better.
By the way, should we support prepare statement like following?
(I think yes.)
============================
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where id = :ID or id =$1;
============================
Current ECPG produces following code.
============================
ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "prepare \"test_prep\" ( int ) as \" select id from test_table where id = $1 or id = $1 \"",
ECPGt_int,&(ID),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
============================
In this case, both ":ID" and "$1" in the original statement are converted to "$1" and ECPGdo() cannot distinguish them.
Therefore, ECPG should produce different code.
For example,
- ECPG convert ":ID" to "$1" and "$1" in the original statement to "$$1"
- next_insert() do not check "$$1"
- ECPGdo() reconvert "$$1" to "$1"
Regards,
Ryohei Takahashi
Hi Takahashi-san
In the case of standard types, ECPG can get oids from pg_type.h.
However, in the case of user defined types, ECPG needs to access
pg_type table and it is overhead.
The overhead wouldn't be too bad. In fact it's already done, at least
sometimes. Please check ecpg_is_type_an_array().
By the way, should we support prepare statement like following?
(I think yes.)
If the standard allows it, we want to be able to process it.
============================
EXEC SQL PREPARE test_prep (int) AS SELECT id from test_table where
id = :ID or id =$1;
============================Current ECPG produces following code.
============================
ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "prepare \"test_prep\"
( int ) as \" select id from test_table where id = $1 or id = $1
\"",
ECPGt_int,&(ID),(long)1,(long)1,sizeof(int),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT,
ECPGt_EORT);
============================In this case, both ":ID" and "$1" in the original statement are
converted to "$1" and ECPGdo() cannot distinguish them.
Therefore, ECPG should produce different code.
I agree. It seems that stuff really broke over the years and nobody
noticed, sigh.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Meskes-san, Takahashi-san
If the standard allows it, we want to be able to process it.
I will try to implement it with the Idea-2 that doesn't use PQprepare() and
Takahasi-san's following idea.
For example,
- ECPG convert ":ID" to "$1" and "$1" in the original statement to "$$1"
- next_insert() do not check "$$1"
- ECPGdo() reconvert "$$1" to "$1"
But I will probably be late because I don't understand parse.pl very well.
I think that the following rule is made by parse.pl.
PreparableStmt:
SelectStmt
{
is_in_preparable_stmt = true; <--- I want to add it.
$$ = $1;
}
| InsertStmt
.....
The above variable is used in ecpg.trailer.
ecpg_param: PARAM {
if(is_in_preparable_stmt)
$$ = mm_strdup(replace_dollar_to_something());
else
$$ = make_name();
} ;
I will use @1 instend of $$1 because the replacing is almost same as the existing replacing function in ecpglib.
Is it good?
Regards
Ryo Matsumura
Hi Matsumura-san,
But I will probably be late because I don't understand parse.pl very
well.
I think that the following rule is made by parse.pl.PreparableStmt:
SelectStmt
{
is_in_preparable_stmt = true; <--- I want to add it.
$$ = $1;
}
| InsertStmt
.....
The only way to add this is by creating a replacement production for
this rule. parse.pl cannot do it itself.
I will use @1 instend of $$1 because the replacing is almost same as
the existing replacing function in ecpglib.
Is it good?
I'd say so.
Thanks.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Thank you for your comment.
The only way to add this is by creating a replacement production for
this rule. parse.pl cannot do it itself.
I will read README.parser, ecpg.addons, and *.pl to understand.
I will use @1 instend of $$1 because the replacing is almost same as
the existing replacing function in ecpglib.
Is it good?I'd say so.
I try it.
Regards
Ryo Matsumura
Hi Matsumura-san,
I will read README.parser, ecpg.addons, and *.pl to understand.
Feel free to ask, when anything comes up.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Meskes-san
I must use a midrule action like the following that works as expected.
I wonder how to write the replacement to ecpg.addons.
I think it's impossible, right? Please give me some advice.
PrepareStmt:
PREPARE prepared_name prep_type_clause AS
{
prepared_name = $2;
prepare_type_clause = $3;
is_in_preparable_stmt = true;
}
PreparableStmt
{
$$.name = prepared_name;
$$.type = prepare_type_clause;
$$.stmt = $6;
is_in_preparable_stmt = false;
}
| PREPARE prepared_name FROM execstring
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Michael Meskes [mailto:meskes@postgresql.org]
Sent: Friday, March 1, 2019 8:42 PM
To: Matsumura, Ryo/松村 量 <matsumura.ryo@jp.fujitsu.com>; Takahashi,
Ryohei/高橋 良平 <r.takahashi_2@jp.fujitsu.com>;
'pgsql-hackers@postgresql.org' <pgsql-hackers@postgresql.org>
Subject: Re: SQL statement PREPARE does not work in ECPGHi Matsumura-san,
I will read README.parser, ecpg.addons, and *.pl to understand.
Feel free to ask, when anything comes up.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Matsumura-san,
I must use a midrule action like the following that works as
expected.
I wonder how to write the replacement to ecpg.addons.
I think it's impossible, right? Please give me some advice.
You are right, for this change you have to replace the whole rule. This
cannot be done with an addon. Please see the attached for a way to do
this, untested though.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Attachments:
prep.difftext/x-patch; charset=UTF-8; name=prep.diffDownload
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 1d58c778d9..be6c9d0ae9 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -1,5 +1,26 @@
/* src/interfaces/ecpg/preproc/ecpg.trailer */
+PrepareStmt: PREPARE prepared_name prep_type_clause AS
+ {
+ prepared_name = $2;
+ prepare_type_clause = $3;
+ is_in_preparable_stmt = true;
+ }
+ PreparableStmt
+ {
+ $$.name = prepared_name;
+ $$.type = prepare_type_clause;
+ $$.stmt = $6;
+ is_in_preparable_stmt = false;
+ }
+ | PREPARE prepared_name FROM execstring
+ {
+ $$.name = $2;
+ $$.type = NULL;
+ $$.stmt = $4;
+ }
+ ;
+
statements: /*EMPTY*/
| statements statement
;
diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type
index 519b737dde..1c68095274 100644
--- a/src/interfaces/ecpg/preproc/ecpg.type
+++ b/src/interfaces/ecpg/preproc/ecpg.type
@@ -145,3 +145,5 @@
%type <type> var_type
%type <action> action
+
+%type <prep> PrepareStmt
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 6f67a5e942..cafca22f7a 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -64,6 +64,7 @@ my %replace_types = (
'stmtblock' => 'ignore',
'stmtmulti' => 'ignore',
'CreateAsStmt' => 'ignore',
+ 'PrepareStmt' => 'ignore',
'DeallocateStmt' => 'ignore',
'ColId' => 'ignore',
'type_function_name' => 'ignore',
Hi Meskes-san
Thank you for your advice.
I attach a patch.
I didn't add additional tests to regression yet.
The patch allows the following:
exec sql prepare(int) as select $1;
exec sql execute st(1) into :out;
exec sql prepare(text, text) as select $1 || $2;
exec sql execute st('aaa', 'bbb') into :out;
But it doesn't allow to use host variable in parameter clause of EXECUTE statement like the following.
I'm afraid that it's not usefull. I will research the standard and other RDBMS.
If you have some information, please adivise to me.
exec sql begin declare section;
int var;
exec sql end declare section;
exec sql prepare(int) as select $1;
exec sql execute st(:var) into :out;
SQL error: bind message supplies 1 parameters, but prepared statement "" requires 0
I explain about the patch.
* PREPARE FROM or PREPARE AS without type clause
It uses PQprepare(). It's not changed.
[Preprocessor output]
/* exec sql prepare st from "select ?"; */
{ ECPGprepare(__LINE__, NULL, 0, "st", "select ?");
/* exec sql prepare st as select 1; */
{ ECPGprepare(__LINE__, NULL, 0, "st", " select 1 ");
* PREPARE AS with type clause
It doesn't use PQprepare() but uses PQexecuteParams().
[Preprocessor output]
/* exec sql prepare st(text, text) as select $1 || '@2'; */
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "prepare \"st\" ( text , text ) as select @1 || '@2'", ECPGt_EOIT, ECPGt_EORT);
$1 in as clause is replaced by preprocessor at ecpg_param rule.
@1 is replaced to $1 by ecpglib at end of ecpg_build_params().
* EXECUTE without type clause
It uses PQexecPrepared(). It's not changed.
[Preprocessor output]
/* exec sql execute st into :ovar using :var; */
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "st",
ECPGt_int,&(var),(long)1,.....
* EXECUTE with parameter clause
It uses PQexecuteParams().
[Preprocessor output]
/* exec sql execute st('abcde') into :ovar_s; */
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "execute \"st\" ( 'abcde' )", ECPGt_EOIT,
.....
This approach causes the above constraint that users cannot use host variables in parameter clause in EXECUTE statement
because ecpglib sends 'P' message with "execute \"st\" ($1)" and sends 'B' one parameter, but backend always regards the number of parameters in EXECUTE statement as zero.
I don't have any other idea...
Regards
Ryo Matsumura
Attachments:
ecpg_prepare_as_v1_0.patchapplication/octet-stream; name=ecpg_prepare_as_v1_0.patchDownload
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 8e61339..ab9e7f9 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1190,6 +1190,23 @@ store_input_from_desc(struct statement *stmt, struct descriptor_item *desc_item,
return true;
}
+static void
+replace_param_marks_in_as_clause(char *text, bool std_strings)
+{
+ int p;
+ bool string = false;
+
+ for (p = 0; text[p]; ++p)
+ {
+ if (string && !std_strings && text[p] == '\\') /* escape character */
+ p++;
+ else if (text[p] == '\'')
+ string = string ? false : true;
+ else if (!string && text[p] == '@')
+ text[p] = '$';
+ }
+}
+
/*
* ecpg_build_params
* Build statement parameters
@@ -1502,6 +1519,8 @@ ecpg_build_params(struct statement *stmt)
return false;
}
+ replace_param_marks_in_as_clause(stmt->command, std_strings);
+
return true;
}
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index e805281..5340136 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -20,11 +20,18 @@ ECPG: stmtSelectStmt block
ECPG: stmtUpdateStmt block
{ output_statement($1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block
- { output_statement($1, 1, ECPGst_execute); }
-ECPG: stmtPrepareStmt block
{
if ($1.type == NULL || strlen($1.type) == 0)
+ output_statement($1.name, 1, ECPGst_execute);
+ else
+ output_statement($1.stmt, 0, ECPGst_normal);
+ }
+ECPG: stmtPrepareStmt block
+ {
+ if ($1.type == NULL)
output_prepare_statement($1.name, $1.stmt);
+ else if (strlen($1.type) == 0)
+ output_prepare_statement($1.name, cat_str(3, mm_strdup("\""), $1.stmt, mm_strdup("\"")));
else
output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
}
@@ -276,20 +283,28 @@ ECPG: cursor_namename rule
$1 = curname;
$$ = $1;
}
-ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
+ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
{
- $$.name = $2;
+ $$.name = mm_strdup($2);
+ $$.stmt = cat_str(4, mm_strdup("execute"), $2, mm_strdup($3), $4);
$$.type = $3;
- $$.stmt = cat_str(3, mm_strdup("\""), $5, mm_strdup("\""));
}
- | PREPARE prepared_name FROM execstring
+ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data block
{
- $$.name = $2;
+ $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table"),$4,mm_strdup("as execute"),$7,$8,$9);
+ $$.stmt = NULL;
$$.type = NULL;
- $$.stmt = $4;
}
-ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
- { $$ = $2; }
+ECPG: ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data block
+ {
+ $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table if not exists"),$7,mm_strdup("as execute"),$10,$11,$12);
+ $$.stmt = NULL;
+ $$.type = NULL;
+ }
+ECPG: ExplainableStmtExecuteStmt block
+ {
+ $$ = $1.stmt;
+ }
ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
{
struct cursor *ptr, *this;
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 366dc23..6462121 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -48,6 +48,9 @@ static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
static int bytea_counter = 1;
+static bool is_in_preparable_stmt = false;
+static char *prepared_name = NULL;
+static char *prepare_type_clause = NULL;
/* temporarily store struct members while creating the data structure */
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -201,6 +204,14 @@ make_name(void)
}
static char *
+replace_params_to_special_notation()
+{
+ char *p = mm_strdup(base_yytext);
+ *p = '@';
+ return p;
+}
+
+static char *
create_questionmarks(char *name, bool array)
{
struct variable *p = find_variable(name);
@@ -574,6 +585,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
types = this;
}
}
+
%}
%expect 0
@@ -593,4 +605,5 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
struct fetch_desc descriptor;
struct su_symbol struct_union;
struct prep prep;
+ struct exec exec;
}
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 1d58c77..1ba139e 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -45,6 +45,27 @@ CreateAsStmt: CREATE OptTemp TABLE create_as_target AS {FoundInto = 0;} SelectSt
}
;
+PrepareStmt: PREPARE prepared_name prep_type_clause AS
+ {
+ prepared_name = $2;
+ prepare_type_clause = $3;
+ is_in_preparable_stmt = true;
+ }
+ PreparableStmt
+ {
+ $$.name = prepared_name;
+ $$.type = mm_strdup(prepare_type_clause);
+ $$.stmt = $6;
+ is_in_preparable_stmt = false;
+ }
+ | PREPARE prepared_name FROM execstring
+ {
+ $$.name = $2;
+ $$.type = NULL;
+ $$.stmt = $4;
+ }
+ ;
+
at: AT connection_object
{
connection = $2;
@@ -1775,7 +1796,14 @@ cvariable: CVARIABLE
}
;
-ecpg_param: PARAM { $$ = make_name(); } ;
+ecpg_param: PARAM
+ {
+ if (is_in_preparable_stmt)
+ $$ = replace_params_to_special_notation();
+ else
+ $$ = make_name();
+ }
+ ;
ecpg_bconst: BCONST { $$ = make_name(); } ;
diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type
index 519b737..da77a85 100644
--- a/src/interfaces/ecpg/preproc/ecpg.type
+++ b/src/interfaces/ecpg/preproc/ecpg.type
@@ -145,3 +145,6 @@
%type <type> var_type
%type <action> action
+
+%type <prep> PrepareStmt
+
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 6f67a5e..9521a94 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -58,6 +58,7 @@ my %replace_string = (
# ECPG-only replace_types are defined in ecpg-replace_types
my %replace_types = (
'PrepareStmt' => '<prep>',
+ 'ExecuteStmt' => '<exec>',
'opt_array_bounds' => '<index>',
# "ignore" means: do not create type and rules for this non-term-id
@@ -65,6 +66,7 @@ my %replace_types = (
'stmtmulti' => 'ignore',
'CreateAsStmt' => 'ignore',
'DeallocateStmt' => 'ignore',
+ 'PrepareStmt' => 'ignore',
'ColId' => 'ignore',
'type_function_name' => 'ignore',
'ColLabel' => 'ignore',
diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h
index 94377ff..e913f05 100644
--- a/src/interfaces/ecpg/preproc/type.h
+++ b/src/interfaces/ecpg/preproc/type.h
@@ -106,6 +106,13 @@ struct prep
char *type;
};
+struct exec
+{
+ char *name;
+ char *stmt;
+ char *type;
+};
+
struct this_type
{
enum ECPGttype type_enum;
Hi all,
...
But it doesn't allow to use host variable in parameter clause of
EXECUTE statement like the following.
I'm afraid that it's not usefull. I will research the standard and
other RDBMS.
If you have some information, please adivise to me.
This also seems to be conflicting with
bd7c95f0c1a38becffceb3ea7234d57167f6d4bf. If we want to keep this
commit in for the release, I think we need to get these things fixed.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Meskes-san
Thank you for your comment.
I write three points in this mail.
1.
This also seems to be conflicting with
bd7c95f0c1a38becffceb3ea7234d57167f6d4bf. If we want to keep this
commit in for the release, I think we need to get these things fixed.
I understand it.
My idea is that add an argument for statement-name to ECPGdo() or
add a new function that is a wrapper of ECPGdo() and has the argument.
The argument is used for lookup related connection. Is it good?
2.
I wrote:
But it doesn't allow to use host variable in parameter clause of EXECUTE statement like the following.
I found a way to support host variables in parameter list of EXECUTE statement.
ecpg_build_params() replace each parameter to string-formatted data that can be
created by ecpg_store_input(). I will try it.
3.
I found a bug in my patch. Replacing $ to @ in AS clause is not good
because @ is absolute value operator.
Therefore, the replacing cannot accept valid statement like the following.
exec sql prepare st(int) select $1 + @1; -- It equals to "select $1 + 1"
I choose $$1 unavoidably.
Other character seems to be used as any operator.
P.S.
- PREPARE with FROM is the standard for Embedded SQL.
- PREPARE with AS is not defined in the standard.
- PREPARE with AS clause is PostgreSQL style.
- Oracle and MySQL support only the standard.
Regards
Ryo Matsumura
Hi Meskes-san
cc: Takahashi-san, Kuroda-san, Ideriha-san
I attach a new patch. Please review it.
Excuse:
It doesn't include regression tests and pass them.
Because I must reset all expected C program of regression.
# I add an argument to ECPGdo().
I explain the patch as follows:
1. Specification
It accepts the following .pgc.
I confirmed it works well for AT clause.
All results for st1 and st2 are same.
exec sql prepare st0 as select 1;
exec sql prepare st1(int,int) as select $1 + 5 + $2;
exec sql prepare st2 from "select ? + 5 + ?";
exec sql prepare st3(bytea) as select octet_length($1);
exec sql execute st0 into :ovar;
exec sql execute st1(:var1,:var2) into :ovar;
exec sql execute st1(11, :var2) into :ovar;
exec sql execute st2(:var1,:var2) into :ovar;
exec sql execute st2(11, :var2) into :ovar;
exec sql execute st1 into :ovar using :var1,:var2;
exec sql execute st2 into :ovar using :var1,:var2;
exec sql execute st3(:b) into :ovar;
2. Behavior of ecpglib
(1) PREPARE with AS clause
Ecpglib sends the PREPARE statement to backend as is. (using PQexec).
(2) EXECUTE with parameter list
Ecpglib sends the EXECUTE statement as is (using PQexec), but all host variables in
the list are converted to string-formatted and embedded into the EXECUTE statement.
(3) PREPARE with FROM clause (not changed)
Ecpglib sends 'P' libpq-message with statement (using PQprepare).
(4) EXECUTE without parameter list (not changed)
Ecpglib sends 'B' libpq-message with parameters. (using PQexecPrepared).
3. Change of preprocessor
- I add ECPGst_prepare and ECPGst_execnormal.
ECPGst_prepare is only for (1) and ECPGst_execnormal is only for (2).
# I think the names are not good.
- I add one argument to ECPGdo(). It's for prepared statement name.
4.
I wonder whether I should merge (3) to (1) and (4) to (4) or not.
Regards
Ryo Matsumura
Attachments:
ecpg_prepare_as_v1_1.patchapplication/octet-stream; name=ecpg_prepare_as_v1_1.patchDownload
diff --git a/src/interfaces/ecpg/ecpglib/cursor.c b/src/interfaces/ecpg/ecpglib/cursor.c
index 133d623..6cb236b 100644
--- a/src/interfaces/ecpg/ecpglib/cursor.c
+++ b/src/interfaces/ecpg/ecpglib/cursor.c
@@ -65,7 +65,7 @@ ECPGopen(const char *cursor_name,const char *prepared_name,
va_start(args, query);
- status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args);
+ status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, NULL, query, args);
va_end(args);
@@ -107,7 +107,7 @@ ECPGfetch(const char *cursor_name,
va_start(args, query);
- status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args);
+ status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, NULL, query, args);
va_end(args);
@@ -153,7 +153,7 @@ ECPGclose(const char *cursor_name,
/* send the query to backend */
va_start(args, query);
- status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args);
+ status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, NULL, query, args);
va_end(args);
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 1ec2bf4..213718b 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -214,7 +214,7 @@ bool ecpg_store_result(const PGresult *results, int act_field,
bool ecpg_store_input(const int, const bool, const struct variable *, char **, bool);
void ecpg_free_params(struct statement *stmt, bool print);
bool ecpg_do_prologue(int, const int, const int, const char *, const bool,
- enum ECPG_statement_type, const char *, va_list,
+ enum ECPG_statement_type, const char *, const char *, va_list,
struct statement **);
bool ecpg_build_params(struct statement *);
bool ecpg_autostart_transaction(struct statement *stmt);
@@ -222,7 +222,7 @@ bool ecpg_execute(struct statement *stmt);
bool ecpg_process_output(struct statement *, bool);
void ecpg_do_epilogue(struct statement *);
bool ecpg_do(const int, const int, const int, const char *, const bool,
- const int, const char *, va_list);
+ const int, const char *, const char *, va_list);
bool ecpg_check_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
void ecpg_raise(int line, int code, const char *sqlstate, const char *str);
@@ -231,6 +231,7 @@ char *ecpg_prepared(const char *, struct connection *);
bool ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *conn);
void ecpg_log(const char *format,...) pg_attribute_printf(1, 2);
bool ecpg_auto_prepare(int, const char *, const int, char **, const char *);
+bool ecpg_register_prepared_stmt(struct statement *);
void ecpg_init_sqlca(struct sqlca_t *sqlca);
struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_MODE);
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 8e61339..0723be7 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1143,6 +1143,23 @@ insert_tobeinserted(int position, int ph_len, struct statement *stmt, char *tobe
return true;
}
+static char*
+convert_bytea_to_string(char *from_data, int from_len, int lineno)
+{
+ char *to_data;
+ int to_len = ecpg_hex_enc_len(from_len) + 4 + 1; /* backslash + 'x' + quote + quote */
+
+ to_data = ecpg_alloc(to_len, lineno);
+ if (!to_data)
+ return NULL;
+
+ strcpy(to_data, "'\\x");
+ ecpg_hex_encode(from_data, from_len, to_data + 3);
+ strcpy(to_data + 3 + ecpg_hex_enc_len(from_len), "\'");
+
+ return to_data;
+}
+
static bool
store_input_from_desc(struct statement *stmt, struct descriptor_item *desc_item,
char **tobeinserted)
@@ -1440,6 +1457,32 @@ ecpg_build_params(struct statement *stmt)
}
tobeinserted = NULL;
}
+ else if (stmt->statement_type == ECPGst_execnormal)
+ {
+ int param_id_len;
+ char *p;
+
+ if (binary_format)
+ {
+ p = convert_bytea_to_string(tobeinserted, binary_length, stmt->lineno);
+ if (!p)
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = p;
+ }
+
+ for (param_id_len = 1, p = stmt->command + position; isdigit(*p); ++param_id_len, ++p)
+ ;
+
+ if (!insert_tobeinserted(position, param_id_len, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
else
{
if (!(stmt->paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
@@ -1493,13 +1536,19 @@ ecpg_build_params(struct statement *stmt)
var = var->next;
}
- /* Check if there are unmatched things left. */
- if (next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
+ /*
+ * PREPARE with AS clause has no parameter to be converted.
+ */
+ if (stmt->statement_type != ECPGst_prepare)
{
- ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
- ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
- ecpg_free_params(stmt, false);
- return false;
+ /* Check if there are unmatched things left. */
+ if (next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
+ {
+ ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
+ ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
+ ecpg_free_params(stmt, false);
+ return false;
+ }
}
return true;
@@ -1536,6 +1585,9 @@ ecpg_execute(struct statement *stmt)
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
if (stmt->statement_type == ECPGst_execute)
{
+ /*
+ * send only 'B' message and parameters
+ */
stmt->results = PQexecPrepared(stmt->connection->connection,
stmt->name,
stmt->nparams,
@@ -1545,6 +1597,28 @@ ecpg_execute(struct statement *stmt)
0);
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
}
+ else if (stmt->statement_type == ECPGst_execnormal)
+ {
+ /*
+ * send 'Q' with 'EXECUTE st(param,...)'. Parameters are string-formatted and embedded into the query.
+ */
+ stmt->results = PQexec(stmt->connection->connection, stmt->command);
+ ecpg_log("ecpg_execute on line %d: using PQexec\n", stmt->lineno);
+ }
+ else if (stmt->statement_type == ECPGst_prepare)
+ {
+ /*
+ * send 'Q' with 'PREPARE st(type,...) as ...'.
+ */
+ stmt->results = PQexec(stmt->connection->connection, stmt->command);
+ ecpg_log("ecpg_execute on line %d: using PQexec\n", stmt->lineno);
+
+ if(! ecpg_register_prepared_stmt(stmt))
+ {
+ ecpg_free_params(stmt, true);
+ return false;
+ }
+ }
else
{
if (stmt->nparams == 0)
@@ -1866,7 +1940,7 @@ ecpg_process_output(struct statement *stmt, bool clear_result)
bool
ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
const char *connection_name, const bool questionmarks,
- enum ECPG_statement_type statement_type, const char *query,
+ enum ECPG_statement_type statement_type, const char* prepared_name, const char *query,
va_list args, struct statement **stmt_out)
{
struct statement *stmt = NULL;
@@ -1926,6 +2000,14 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
ecpg_pthreads_init();
#endif
+
+ if (prepared_name)
+ {
+ char *con_name = ecpg_get_con_name_by_declared_name(prepared_name);
+ if (con_name)
+ connection_name = con_name;
+ }
+
con = ecpg_get_connection(connection_name);
if (!ecpg_init(con, connection_name, lineno))
@@ -1938,42 +2020,69 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
* If statement type is ECPGst_prepnormal we are supposed to prepare the
* statement before executing them
*/
- if (statement_type == ECPGst_prepnormal)
+ switch (statement_type)
{
- if (!ecpg_auto_prepare(lineno, connection_name, compat, &prepname, query))
- {
- ecpg_do_epilogue(stmt);
- return false;
- }
-
- /*
- * statement is now prepared, so instead of the query we have to
- * execute the name
- */
- stmt->command = prepname;
- statement_type = ECPGst_execute;
- }
- else
- stmt->command = ecpg_strdup(query, lineno);
+ case ECPGst_prepnormal:
+ /*
+ * statement is now prepared, so instead of the query we have to
+ * execute the name
+ */
+ if (!ecpg_auto_prepare(lineno, connection_name, compat, &prepname, query))
+ {
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+ stmt->name = prepname;
+ stmt->command = ecpg_strdup(query, lineno);
+ statement_type = ECPGst_execute;
+ break;
- stmt->name = NULL;
+ case ECPGst_prepare:
+ /*
+ * If statent type is ECPGst_prepare that is PREPARE with AS clause and
+ * typelist clause, ecpg_execute will call PQexec("PREPARE name(typelist)")
+ */
+ stmt->name = ecpg_strdup(prepared_name, lineno);
+ stmt->command = ecpg_strdup(query, lineno);
+ break;
- if (statement_type == ECPGst_execute)
- {
- /* if we have an EXECUTE command, only the name is send */
- char *command = ecpg_prepared(stmt->command, con);
+ case ECPGst_execnormal:
+ /*
+ * Send EXECUTE stm_name($1, 'x', ...). Params will be replaced with vars in build_params.
+ */
+ if (!ecpg_prepared(prepared_name, con))
+ {
+ ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, prepared_name);
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+ /* st->command is prepared-name in case of prepnormal and EXECUTE without parameter-list-clause. */
+ stmt->name = ecpg_strdup(prepared_name, lineno);
+ stmt->command = ecpg_strdup(query, lineno);
+ break;
- if (command)
+ case ECPGst_execute:
{
- stmt->name = stmt->command;
+ /*
+ * Send Bind message only. Prepared query is used only for checking number of params.
+ */
+ char *command = ecpg_prepared(prepared_name, con);
+ if (!command)
+ {
+ ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, prepared_name);
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+ /* st->command is prepared-name in case of prepnormal and EXECUTE without parameter-list-clause. */
+ stmt->name = ecpg_strdup(prepared_name, lineno);
stmt->command = ecpg_strdup(command, lineno);
+ break;
}
- else
- {
- ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt->command);
- ecpg_do_epilogue(stmt);
- return false;
- }
+
+ default:
+ stmt->name = ecpg_strdup(prepared_name, lineno);
+ stmt->command = ecpg_strdup(query, lineno);
+ break;
}
stmt->connection = con;
@@ -2150,13 +2259,13 @@ ecpg_do_epilogue(struct statement *stmt)
* in a va_list, so other functions can use this interface.
*/
bool
-ecpg_do(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query, va_list args)
+ecpg_do(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *prepared_name, const char *query, va_list args)
{
struct statement *stmt = NULL;
if (!ecpg_do_prologue(lineno, compat, force_indicator, connection_name,
questionmarks, (enum ECPG_statement_type) st,
- query, args, &stmt))
+ prepared_name, query, args, &stmt))
goto fail;
if (!ecpg_build_params(stmt))
@@ -2184,13 +2293,10 @@ fail:
* The input/output parameters are passed as variable-length argument list.
*/
bool
-ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query,...)
+ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *prepared_name, const char *query,...)
{
va_list args;
bool ret;
- const char *real_connection_name = NULL;
-
- real_connection_name = connection_name;
if (!query)
{
@@ -2198,23 +2304,9 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
return false;
}
- /* Handle the EXEC SQL EXECUTE... statement */
- if (ECPGst_execute == st)
- {
- real_connection_name = ecpg_get_con_name_by_declared_name(query);
- if (real_connection_name == NULL)
- {
- /*
- * If can't get the connection name by declared name then using connection name
- * coming from the parameter connection_name
- */
- real_connection_name = connection_name;
- }
- }
-
va_start(args, query);
- ret = ecpg_do(lineno, compat, force_indicator, real_connection_name,
- questionmarks, st, query, args);
+ ret = ecpg_do(lineno, compat, force_indicator, connection_name,
+ questionmarks, st, prepared_name, query, args);
va_end(args);
return ret;
@@ -2225,7 +2317,7 @@ bool
ECPGdo_descriptor(int line, const char *connection,
const char *descriptor, const char *query)
{
- return ECPGdo(line, ECPG_COMPAT_PGSQL, true, connection, '\0', 0, query, ECPGt_EOIT,
+ return ECPGdo(line, ECPG_COMPAT_PGSQL, true, connection, '\0', ECPGst_normal, NULL, query, ECPGt_EOIT,
ECPGt_descriptor, descriptor, 0L, 0L, 0L,
ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT);
}
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
index e04312c..30def76 100644
--- a/src/interfaces/ecpg/ecpglib/prepare.c
+++ b/src/interfaces/ecpg/ecpglib/prepare.c
@@ -56,6 +56,60 @@ isvarchar(unsigned char c)
return false;
}
+bool
+ecpg_register_prepared_stmt(struct statement *stmt)
+{
+ struct statement *prep_stmt;
+ struct prepared_statement *this;
+ struct connection *con = NULL;
+ struct prepared_statement *prev = NULL;
+ char *real_connection_name;
+ int lineno = stmt->lineno;
+
+ real_connection_name = ecpg_get_con_name_by_declared_name(stmt->name);
+ if (real_connection_name == NULL)
+ real_connection_name = stmt->connection->name;
+
+ con = ecpg_get_connection(real_connection_name);
+ if (!ecpg_init(con, real_connection_name, stmt->lineno))
+ return false;
+
+ /* check if we already have prepared this statement */
+ this = ecpg_find_prepared_statement(stmt->name, con, &prev);
+ if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
+ return false;
+
+ /* allocate new statement */
+ this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
+ if (!this)
+ return false;
+
+ prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
+ if (!stmt)
+ {
+ ecpg_free(this);
+ return false;
+ }
+ memset(prep_stmt, 0, sizeof(struct statement));
+
+ /* create statement */
+ prep_stmt->lineno = lineno;
+ prep_stmt->connection = con;
+ prep_stmt->command = ecpg_strdup(stmt->command, lineno);
+ prep_stmt->inlist = prep_stmt->outlist = NULL;
+ this->name = ecpg_strdup(stmt->name, lineno);
+ this->stmt = prep_stmt;
+ this->prepared = true;
+
+ if (con->prep_stmts == NULL)
+ this->next = NULL;
+ else
+ this->next = con->prep_stmts;
+
+ con->prep_stmts = this;
+ return true;
+}
+
static bool
replace_variables(char **text, int lineno)
{
diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h
index 1edf272..77c2980 100644
--- a/src/interfaces/ecpg/include/ecpglib.h
+++ b/src/interfaces/ecpg/include/ecpglib.h
@@ -49,7 +49,7 @@ bool ECPGstatus(int, const char *);
bool ECPGsetcommit(int, const char *, const char *);
bool ECPGsetconn(int, const char *);
bool ECPGconnect(int, int, const char *, const char *, const char *, const char *, int);
-bool ECPGdo(const int, const int, const int, const char *, const bool, const int, const char *,...);
+bool ECPGdo(const int, const int, const int, const char *, const bool, const int, const char *, const char *,...);
bool ECPGtrans(int, const char *, const char *);
bool ECPGdisconnect(int, const char *);
bool ECPGprepare(int, const char *, const bool, const char *, const char *);
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 4a7e8e7..56cb996 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -96,8 +96,10 @@ enum ECPG_statement_type
{
ECPGst_normal,
ECPGst_execute,
+ ECPGst_execnormal,
ECPGst_exec_immediate,
- ECPGst_prepnormal
+ ECPGst_prepnormal,
+ ECPGst_prepare
};
enum ECPG_cursor_statement_type
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index e805281..d1b3484 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -20,13 +20,18 @@ ECPG: stmtSelectStmt block
ECPG: stmtUpdateStmt block
{ output_statement($1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block
- { output_statement($1, 1, ECPGst_execute); }
-ECPG: stmtPrepareStmt block
{
if ($1.type == NULL || strlen($1.type) == 0)
+ output_statement($1.name, 1, ECPGst_execute);
+ else
+ output_execute_stmt($1.stmt, 1, $1.name);
+ }
+ECPG: stmtPrepareStmt block
+ {
+ if ($1.type == NULL)
output_prepare_statement($1.name, $1.stmt);
else
- output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
+ output_prepare_stmt(cat_str(5, mm_strdup("prepare"), mm_strdup($1.name), $1.type, mm_strdup("as"), $1.stmt), 0, $1.name);
}
ECPG: stmtTransactionStmt block
{
@@ -280,7 +285,7 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
{
$$.name = $2;
$$.type = $3;
- $$.stmt = cat_str(3, mm_strdup("\""), $5, mm_strdup("\""));
+ $$.stmt = $5;
}
| PREPARE prepared_name FROM execstring
{
@@ -289,7 +294,27 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
$$.stmt = $4;
}
ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
- { $$ = $2; }
+ {
+ $$.name = mm_strdup($2);
+ $$.stmt = cat_str(4, mm_strdup("execute"), $2, mm_strdup($3), $4);
+ $$.type = $3;
+ }
+ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data block
+ {
+ $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table"),$4,mm_strdup("as execute"),$7,$8,$9);
+ $$.stmt = NULL;
+ $$.type = NULL;
+ }
+ECPG: ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data block
+ {
+ $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table if not exists"),$7,mm_strdup("as execute"),$10,$11,$12);
+ $$.stmt = NULL;
+ $$.type = NULL;
+ }
+ECPG: ExplainableStmtExecuteStmt block
+ {
+ $$ = $1.stmt;
+ }
ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
{
struct cursor *ptr, *this;
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 366dc23..18b7bec 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -593,4 +593,5 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
struct fetch_desc descriptor;
struct su_symbol struct_union;
struct prep prep;
+ struct exec exec;
}
diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c
index 6b46ae6..7d364bb 100644
--- a/src/interfaces/ecpg/preproc/output.c
+++ b/src/interfaces/ecpg/preproc/output.c
@@ -127,28 +127,26 @@ hashline_number(void)
static char *ecpg_statement_type_name[] = {
"ECPGst_normal",
"ECPGst_execute",
+ "ECPGst_execnormal",
"ECPGst_exec_immediate",
- "ECPGst_prepnormal"
+ "ECPGst_prepnormal",
+ "ECPGst_prepare"
};
-void
-output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+static void
+output_stmt(char *stmt, int whenever_mode, enum ECPG_statement_type st, char *prepared_name)
{
- fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);
- if (st == ECPGst_execute || st == ECPGst_exec_immediate)
- {
- fprintf(base_yyout, "%s, %s, ", ecpg_statement_type_name[st], stmt);
- }
- else
- {
- if (st == ECPGst_prepnormal && auto_prepare)
- fputs("ECPGst_prepnormal, \"", base_yyout);
- else
- fputs("ECPGst_normal, \"", base_yyout);
+ if (st == ECPGst_prepnormal && ! auto_prepare)
+ st = ECPGst_normal;
- output_escaped_str(stmt, false);
- fputs("\", ", base_yyout);
- }
+ if (st == ECPGst_execute)
+ prepared_name = stmt;
+
+ fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, %s, %s, \"",
+ compat,force_indicator, connection ? connection : "NULL", questionmarks,
+ ecpg_statement_type_name[st], prepared_name ? prepared_name : "NULL");
+ output_escaped_str(stmt, false);
+ fputs("\", ", base_yyout);
/* dump variables to C file */
dump_variables(argsinsert, 1);
@@ -165,6 +163,24 @@ output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
}
void
+output_execute_stmt(char *stmt, int whenever_mode, char *prepared_name)
+{
+ output_stmt(stmt, whenever_mode, ECPGst_execnormal, prepared_name);
+}
+
+void
+output_prepare_stmt(char *stmt, int whenever_mode, char *prepared_name)
+{
+ output_stmt(stmt, whenever_mode, ECPGst_prepare, prepared_name);
+}
+
+void
+output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+{
+ output_stmt(stmt, whenever_mode, st, NULL);
+}
+
+void
output_prepare_statement(char *name, char *stmt)
{
fprintf(base_yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 6f67a5e..76c4dfa 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -58,6 +58,7 @@ my %replace_string = (
# ECPG-only replace_types are defined in ecpg-replace_types
my %replace_types = (
'PrepareStmt' => '<prep>',
+ 'ExecuteStmt' => '<exec>',
'opt_array_bounds' => '<index>',
# "ignore" means: do not create type and rules for this non-term-id
diff --git a/src/interfaces/ecpg/preproc/preproc_extern.h b/src/interfaces/ecpg/preproc/preproc_extern.h
index 8ccfdf0..834fa24 100644
--- a/src/interfaces/ecpg/preproc/preproc_extern.h
+++ b/src/interfaces/ecpg/preproc/preproc_extern.h
@@ -67,6 +67,8 @@ extern const uint16 SQLScanKeywordTokens[];
extern const char *get_dtype(enum ECPGdtype);
extern void lex_init(void);
extern void output_line_number(void);
+extern void output_execute_stmt(char *, int, char *);
+extern void output_prepare_stmt(char *, int, char *);
extern void output_statement(char *, int, enum ECPG_statement_type);
extern void output_prepare_statement(char *, char *);
extern void output_deallocate_prepare_statement(char *);
diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h
index 94377ff..e913f05 100644
--- a/src/interfaces/ecpg/preproc/type.h
+++ b/src/interfaces/ecpg/preproc/type.h
@@ -106,6 +106,13 @@ struct prep
char *type;
};
+struct exec
+{
+ char *name;
+ char *stmt;
+ char *type;
+};
+
struct this_type
{
enum ECPGttype type_enum;
Hi all and thank you Matsumura-san.
Excuse:
It doesn't include regression tests and pass them.
Because I must reset all expected C program of regression.
# I add an argument to ECPGdo().
Sure, let's do this at the very end.
1. Specification
It accepts the following .pgc.
I confirmed it works well for AT clause.
All results for st1 and st2 are same.
I have a similar text case and can confirm that the output is the same
for both ways of preparing the statement.
2. Behavior of ecpglib
(1) PREPARE with AS clause
Ecpglib sends the PREPARE statement to backend as is. (using
PQexec).(2) EXECUTE with parameter list
Ecpglib sends the EXECUTE statement as is (using PQexec), but all
host variables in
the list are converted to string-formatted and embedded into the
EXECUTE statement.(3) PREPARE with FROM clause (not changed)
Ecpglib sends 'P' libpq-message with statement (using PQprepare).(4) EXECUTE without parameter list (not changed)
Ecpglib sends 'B' libpq-message with parameters. (using
PQexecPrepared).3. Change of preprocessor
- I add ECPGst_prepare and ECPGst_execnormal.
ECPGst_prepare is only for (1) and ECPGst_execnormal is only for
(2).
# I think the names are not good.- I add one argument to ECPGdo().p It's for prepared statement name.
One question though, why is the statement name always quoted? Do we
really need that? Seems to be more of a hassle than and advantage.
4.
I wonder whether I should merge (3) to (1) and (4) to (4) or not.
I would prefer to merge as much as possible, as I am afraid that if we
do not merge the approaches, we will run into issues later. There was a
reason why we added PQprepare, but I do not remember it from the top of
my head. Need to check when I'm online again.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Thank you for your comment.
One question though, why is the statement name always quoted? Do we
really need that? Seems to be more of a hassle than and advantage.
The following can be accepted by preproc, ecpglib, libpq, and backend in previous versions.
exec sql prepare "st x" from "select 1";
exec sql execute "st x";
The above was preprocessed to the following.
PQprepare(conn, "\"st x\"", "select 1");
PQexec(conn, "\"st x\"");
By the way, the following also can be accepted.
PQexecParams(conn, "prepare \"st x\" ( int ) as select $1", 0, NULL, NULL, NULL, NULL, 0);
PQexecParams(conn, "execute \"st x\"( 1 )", 0, NULL, NULL, NULL, NULL, 0);
Therefore, I think that the quoting statement name is needed in PREPARE/EXECUTE case, too.
I would prefer to merge as much as possible, as I am afraid that if we
do not merge the approaches, we will run into issues later. There was a
reason why we added PQprepare, but I do not remember it from the top of
my head. Need to check when I'm online again.
I will also consider it.
Regards
Ryo Matsumura
Matsumura-san,
Therefore, I think that the quoting statement name is needed in
PREPARE/EXECUTE case, too.
I agree that we have to accept a quoted statement name and your
observations are correct of course, I am merely wondering if we need
the escaped quotes in the call to the ecpg functions or the libpq
functions.
I would prefer to merge as much as possible, as I am afraid that if
we
do not merge the approaches, we will run into issues later. There
was a
reason why we added PQprepare, but I do not remember it from the
top of
my head. Need to check when I'm online again.I will also consider it.
Thank you.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
I'm sorry for my slow reply.
I agree that we have to accept a quoted statement name and your
observations are correct of course, I am merely wondering if we need
the escaped quotes in the call to the ecpg functions or the libpq
functions.
The following allows to use statement name including white space not double-quoted statement name.
exec sql prepare "st1 x" from "select 1";
# I don't know whether the existing ECPG allows it intentionally or not.
# In honestly, I think that it's not necessary to allow it.
If we also allow the statement name including white space in PREPRARE AS,
we have to make backend parser to scan it as IDENT.
Double-quoting is one way. There may be another way.
If we want to pass double-quoted statement name to backend through libpq,
preprocessor have to escape it.
I would prefer to merge as much as possible, as I am afraid that if
we
do not merge the approaches, we will run into issues later. There
was a
reason why we added PQprepare, but I do not remember it from the
top of
my head. Need to check when I'm online again.I will also consider it.
I cannot think of anything.
I may notice if I try to merge.
Regards
Ryo Matsumura
Hi Matsumura-san,
If we also allow the statement name including white space in PREPRARE
AS,
we have to make backend parser to scan it as IDENT.
Correct, without quoting would only work when using PQprepare() et al.
I cannot think of anything.
I may notice if I try to merge.
Thanks.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi, Meskes-san
I'm sorry for my long blank. I restarted.
Review of previous discussion:
I made a patch that makes ecpglib to send "PREPARE st(typelist) AS PreparableStmt"
with PQexec(), because the type resolution is difficult.
I tried to merge PREPARE FROM that uses PQprepare() to the PREPARE AS.
Meskes-san pointed that there may be a problem that PREPARE FROM cannot use PQexec().
Now, I noticed a problem of the merging.
Therefore, I will not change the existing implementation of PREPARE FROM.
The problem is:
Statement name of PREPARE FROM can include double quote, because the statement name
is sent by PQprepare() directly and backend doesn't parse it.
In other hand, the statement name of PREPARE AS cannot include double quote,
because it is embedded into query and backend parser disallows it.
This is a specification of PostgreSQL's PREPARE AS.
I defined the following specifications. Please review it.
* ECPG can accept any valid PREPARE AS statement.
* A char-type host variable can be used as the statement name of PREPARE AS,
but its value is constrained by the specification of PREPARE AS.
(e.g. the name cannot include double quotation.)
* The above also allows the following. It's a bit strange but there is no reason
for forbidding.
prepare :st(type_list) as select $1
* ECPG can accept EXECUTE statement with expression list that is allocated
by both PREPARE FROM and PREPARE AS under the following constraints:
- It must not include a using-clause.
- The statement name must follow to the specification of PREPARE AS.
Regards
Ryo Matsumura
Hi Matsumura-san,
I defined the following specifications. Please review it.
* ECPG can accept any valid PREPARE AS statement.
* A char-type host variable can be used as the statement name of
PREPARE AS,
but its value is constrained by the specification of PREPARE AS.
(e.g. the name cannot include double quotation.)
* The above also allows the following. It's a bit strange but there
is no reason
for forbidding.
prepare :st(type_list) as select $1
* ECPG can accept EXECUTE statement with expression list that is
allocated
by both PREPARE FROM and PREPARE AS under the following
constraints:
- It must not include a using-clause.
- The statement name must follow to the specification of PREPARE
AS.
This look very reasonable to me. I'm completely fine with this
restriction being placed on PREPARE FROM.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
This look very reasonable to me. I'm completely fine with this
restriction being placed on PREPARE FROM.
Thank you. I start making a new patch.
Regards
Ryo Matsumura
Hi Meskes-san
There are two points.
(1)
I attach a new patch. Please review it.
- Preproc replaces any prepared_name to "$0" and changes it to an input-variable
for PREARE with typelist and EXECUTE with paramlist.
$0 is replaced in ecpg_build_params().
It's enable not to change ECPGdo interface.
- Host variables can be used in paramlist of EXECUTE.
(2)
I found some bugs (two types). I didn't fix them and only avoid bison error.
Type1. Bugs or intentional unsupported features.
- EXPLAIN EXECUTE
- CREATE TABLE AS with using clause
e.g.
EXPLAIN EXECUTE st; /* It has not been supported before.*/
ExplainableStmt:
ExecuteStmt
{
- $$ = $1;
+ $$ = $1.name; /* only work arround for bison error */
}
Type2. In multi-bytes encoding environment, a part of character of message is cut off.
It may be very difficult to fix. I pretend I didn't see it for a while.
[ecpglib/error.c]
snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "%s on line %d", message, line);
sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
ecpg_log("raising sqlstate %.*s (sqlcode %ld): %s\n",
(int) sizeof(sqlca->sqlstate), sqlca->sqlstate, sqlca->sqlcode, sqlca->sqlerrm.sqlerrmc);
Regards
Ryo Matsumura
Attachments:
ecpg_prepare_as_v1_2.patchapplication/octet-stream; name=ecpg_prepare_as_v1_2.patchDownload
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 1ec2bf4..d63efd5 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -231,6 +231,7 @@ char *ecpg_prepared(const char *, struct connection *);
bool ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *conn);
void ecpg_log(const char *format,...) pg_attribute_printf(1, 2);
bool ecpg_auto_prepare(int, const char *, const int, char **, const char *);
+bool ecpg_register_prepared_stmt(struct statement *);
void ecpg_init_sqlca(struct sqlca_t *sqlca);
struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_MODE);
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 8e61339..2db1b62 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -488,6 +488,23 @@ sprintf_float_value(char *ptr, float value, const char *delim)
sprintf(ptr, "%.15g%s", value, delim);
}
+static char*
+convert_bytea_to_string(char *from_data, int from_len, int lineno)
+{
+ char *to_data;
+ int to_len = ecpg_hex_enc_len(from_len) + 4 + 1; /* backslash + 'x' + quote + quote */
+
+ to_data = ecpg_alloc(to_len, lineno);
+ if (!to_data)
+ return NULL;
+
+ strcpy(to_data, "'\\x");
+ ecpg_hex_encode(from_data, from_len, to_data + 3);
+ strcpy(to_data + 3 + ecpg_hex_enc_len(from_len), "\'");
+
+ return to_data;
+}
+
bool
ecpg_store_input(const int lineno, const bool force_indicator, const struct variable *var,
char **tobeinserted_p, bool quote)
@@ -1433,6 +1450,36 @@ ecpg_build_params(struct statement *stmt)
*/
else if (stmt->command[position] == '0')
{
+ if (stmt->statement_type == ECPGst_prepare ||
+ stmt->statement_type == ECPGst_exec_with_exprlist)
+ {
+ /* Add double quote both side for embedding statement name. */
+ char *str = ecpg_alloc(strlen(tobeinserted) + 2 + 1, stmt->lineno);
+ sprintf(str, "\"%s\"", tobeinserted);
+ ecpg_free(tobeinserted);
+ tobeinserted = str;
+ }
+ if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
+ else if (stmt->statement_type == ECPGst_exec_with_exprlist)
+ {
+
+ if (binary_format)
+ {
+ char *p = convert_bytea_to_string(tobeinserted, binary_length, stmt->lineno);
+ if (!p)
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = p;
+ }
+
if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
{
ecpg_free_params(stmt, false);
@@ -1493,8 +1540,12 @@ ecpg_build_params(struct statement *stmt)
var = var->next;
}
- /* Check if there are unmatched things left. */
- if (next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
+ /*
+ * Check if there are unmatched things left.
+ * PREPARE AS has no parameter. Check other statement.
+ */
+ if (stmt->statement_type != ECPGst_prepare &&
+ next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
{
ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
@@ -1560,8 +1611,18 @@ ecpg_execute(struct statement *stmt)
(const int *) stmt->paramlengths,
(const int *) stmt->paramformats,
0);
+
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
+
+ if (stmt->statement_type == ECPGst_prepare)
+ {
+ if(! ecpg_register_prepared_stmt(stmt))
+ {
+ ecpg_free_params(stmt, true);
+ return false;
+ }
+ }
}
ecpg_free_params(stmt, true);
@@ -1874,6 +1935,7 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
enum ECPGttype type;
struct variable **list;
char *prepname;
+ bool is_prepared_name_set;
*stmt_out = NULL;
@@ -1975,6 +2037,7 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
return false;
}
}
+ /* name of PREPARE AS will be set in loop of inlist */
stmt->connection = con;
stmt->lineno = lineno;
@@ -2004,6 +2067,8 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
*------
*/
+ is_prepared_name_set = false;
+
list = &(stmt->inlist);
type = va_arg(args, enum ECPGttype);
@@ -2092,6 +2157,12 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
*list = var;
else
ptr->next = var;
+
+ if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
+ {
+ stmt->name = ecpg_strdup(var->value, lineno);
+ is_prepared_name_set = true;
+ }
}
type = va_arg(args, enum ECPGttype);
@@ -2105,6 +2176,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
return false;
}
+ if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
+ {
+ ecpg_raise(lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>"));
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
/* initialize auto_mem struct */
ecpg_clear_auto_mem();
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
index 0ef85c9..41dcf4c 100644
--- a/src/interfaces/ecpg/ecpglib/prepare.c
+++ b/src/interfaces/ecpg/ecpglib/prepare.c
@@ -56,6 +56,60 @@ isvarchar(unsigned char c)
return false;
}
+bool
+ecpg_register_prepared_stmt(struct statement *stmt)
+{
+ struct statement *prep_stmt;
+ struct prepared_statement *this;
+ struct connection *con = NULL;
+ struct prepared_statement *prev = NULL;
+ char *real_connection_name;
+ int lineno = stmt->lineno;
+
+ real_connection_name = ecpg_get_con_name_by_declared_name(stmt->name);
+ if (real_connection_name == NULL)
+ real_connection_name = stmt->connection->name;
+
+ con = ecpg_get_connection(real_connection_name);
+ if (!ecpg_init(con, real_connection_name, stmt->lineno))
+ return false;
+
+ /* check if we already have prepared this statement */
+ this = ecpg_find_prepared_statement(stmt->name, con, &prev);
+ if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
+ return false;
+
+ /* allocate new statement */
+ this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
+ if (!this)
+ return false;
+
+ prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
+ if (!stmt)
+ {
+ ecpg_free(this);
+ return false;
+ }
+ memset(prep_stmt, 0, sizeof(struct statement));
+
+ /* create statement */
+ prep_stmt->lineno = lineno;
+ prep_stmt->connection = con;
+ prep_stmt->command = ecpg_strdup(stmt->command, lineno);
+ prep_stmt->inlist = prep_stmt->outlist = NULL;
+ this->name = ecpg_strdup(stmt->name, lineno);
+ this->stmt = prep_stmt;
+ this->prepared = true;
+
+ if (con->prep_stmts == NULL)
+ this->next = NULL;
+ else
+ this->next = con->prep_stmts;
+
+ con->prep_stmts = this;
+ return true;
+}
+
static bool
replace_variables(char **text, int lineno)
{
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 4a7e8e7..cdeb43d 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -97,7 +97,9 @@ enum ECPG_statement_type
ECPGst_normal,
ECPGst_execute,
ECPGst_exec_immediate,
- ECPGst_prepnormal
+ ECPGst_prepnormal,
+ ECPGst_prepare,
+ ECPGst_exec_with_exprlist
};
enum ECPG_cursor_statement_type
diff --git a/src/interfaces/ecpg/preproc/check_rules.pl b/src/interfaces/ecpg/preproc/check_rules.pl
index 9d8c708..b6f0d0e 100644
--- a/src/interfaces/ecpg/preproc/check_rules.pl
+++ b/src/interfaces/ecpg/preproc/check_rules.pl
@@ -39,8 +39,11 @@ my %replace_line = (
'ExecuteStmtEXECUTEnameexecute_param_clause' =>
'EXECUTE prepared_name execute_param_clause execute_rest',
- 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clause'
- => 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause',
+ 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' =>
+ 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest' ,
+
+ 'ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' =>
+ 'CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest' ,
'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' =>
'PREPARE prepared_name prep_type_clause AS PreparableStmt');
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index e805281..4e30375 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -20,13 +20,54 @@ ECPG: stmtSelectStmt block
ECPG: stmtUpdateStmt block
{ output_statement($1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block
- { output_statement($1, 1, ECPGst_execute); }
-ECPG: stmtPrepareStmt block
{
if ($1.type == NULL || strlen($1.type) == 0)
+ output_statement($1.name, 1, ECPGst_execute);
+ else
+ {
+ if ($1.name[0] != '"')
+ /* case of char_variable */
+ add_variable_to_tail(&argsinsert, find_variable($1.name), &no_indicator);
+ else
+ {
+ /* case of ecpg_ident or CSTRING */
+ char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+ char *str = mm_strdup($1.name + 1);
+
+ /* It must be cut off double quotation because new_variable() double-quotes. */
+ str[strlen(str) - 1] = '\0';
+ sprintf(length, "%d", (int) strlen(str));
+ add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
+ }
+ output_statement(cat_str(3, mm_strdup("execute"), mm_strdup("$0"), $1.type), 0, ECPGst_exec_with_exprlist);
+ }
+ }
+ECPG: stmtPrepareStmt block
+ {
+ if ($1.type == NULL)
output_prepare_statement($1.name, $1.stmt);
+ else if (strlen($1.type) == 0)
+ {
+ char *stmt = cat_str(3, mm_strdup("\""), $1.stmt, mm_strdup("\""));
+ output_prepare_statement($1.name, stmt);
+ }
else
- output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
+ {
+ if ($1.name[0] != '"')
+ /* case of char_variable */
+ add_variable_to_tail(&argsinsert, find_variable($1.name), &no_indicator);
+ else
+ {
+ char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+ char *str = mm_strdup($1.name + 1);
+
+ /* It must be cut off double quotation because new_variable() double-quotes. */
+ str[strlen(str) - 1] = '\0';
+ sprintf(length, "%d", (int) strlen(str));
+ add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
+ }
+ output_statement(cat_str(5, mm_strdup("prepare"), mm_strdup("$0"), $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_prepare);
+ }
}
ECPG: stmtTransactionStmt block
{
@@ -276,11 +317,15 @@ ECPG: cursor_namename rule
$1 = curname;
$$ = $1;
}
+ECPG: ExplainableStmtExecuteStmt block
+ {
+ $$ = $1.name;
+ }
ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
{
$$.name = $2;
$$.type = $3;
- $$.stmt = cat_str(3, mm_strdup("\""), $5, mm_strdup("\""));
+ $$.stmt = $5;
}
| PREPARE prepared_name FROM execstring
{
@@ -289,7 +334,18 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
$$.stmt = $4;
}
ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
- { $$ = $2; }
+ {
+ $$.name = $2;
+ $$.type = $3;
+ }
+ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block
+ {
+ $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table"),$4,mm_strdup("as execute"),$7,$8,$9);
+ }
+ECPG: ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest block
+ {
+ $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table if not exists"),$7,mm_strdup("as execute"),$10,$11,$12);
+ }
ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
{
struct cursor *ptr, *this;
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 366dc23..18b7bec 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -593,4 +593,5 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
struct fetch_desc descriptor;
struct su_symbol struct_union;
struct prep prep;
+ struct exec exec;
}
diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c
index 6b46ae6..1851809 100644
--- a/src/interfaces/ecpg/preproc/output.c
+++ b/src/interfaces/ecpg/preproc/output.c
@@ -128,24 +128,30 @@ static char *ecpg_statement_type_name[] = {
"ECPGst_normal",
"ECPGst_execute",
"ECPGst_exec_immediate",
- "ECPGst_prepnormal"
+ "ECPGst_prepnormal",
+ "ECPGst_prepare",
+ "ECPGst_exec_with_exprlist"
};
void
output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
{
fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);
+
+ if (st == ECPGst_prepnormal && !auto_prepare)
+ st = ECPGst_normal;
+
+ /*
+ * In following cases, stmt is CSTRING or char_variable. They must be output directly.
+ * - prepared_name of EXECUTE without exprlist
+ * - execstring of EXECUTE IMMEDIATE
+ */
+ fprintf(base_yyout, "%s, ", ecpg_statement_type_name[st]);
if (st == ECPGst_execute || st == ECPGst_exec_immediate)
- {
- fprintf(base_yyout, "%s, %s, ", ecpg_statement_type_name[st], stmt);
- }
+ fprintf(base_yyout, "%s, ", stmt);
else
{
- if (st == ECPGst_prepnormal && auto_prepare)
- fputs("ECPGst_prepnormal, \"", base_yyout);
- else
- fputs("ECPGst_normal, \"", base_yyout);
-
+ fputs("\"", base_yyout);
output_escaped_str(stmt, false);
fputs("\", ", base_yyout);
}
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 6f67a5e..ceabe4e 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -58,6 +58,7 @@ my %replace_string = (
# ECPG-only replace_types are defined in ecpg-replace_types
my %replace_types = (
'PrepareStmt' => '<prep>',
+ 'ExecuteStmt' => '<exec>',
'opt_array_bounds' => '<index>',
# "ignore" means: do not create type and rules for this non-term-id
@@ -102,11 +103,13 @@ my %replace_line = (
'RETURNING target_list opt_ecpg_into',
'ExecuteStmtEXECUTEnameexecute_param_clause' =>
'EXECUTE prepared_name execute_param_clause execute_rest',
- 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clause'
- => 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause',
+ 'ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' =>
+ 'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest',
+ 'ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEnameexecute_param_clauseopt_with_data' =>
+ 'CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE prepared_name execute_param_clause opt_with_data execute_rest',
'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' =>
'PREPARE prepared_name prep_type_clause AS PreparableStmt',
- 'var_nameColId' => 'ECPGColId',);
+ 'var_nameColId' => 'ECPGColId');
preload_addons();
diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h
index 94377ff..4d6afd9 100644
--- a/src/interfaces/ecpg/preproc/type.h
+++ b/src/interfaces/ecpg/preproc/type.h
@@ -106,6 +106,12 @@ struct prep
char *type;
};
+struct exec
+{
+ char *name;
+ char *type;
+};
+
struct this_type
{
enum ECPGttype type_enum;
diff --git a/src/interfaces/ecpg/test/expected/sql-prepareas.c b/src/interfaces/ecpg/test/expected/sql-prepareas.c
new file mode 100644
index 0000000..6fc5f2d
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-prepareas.c
@@ -0,0 +1,663 @@
+/* Processed by ecpg (12devel) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+
+#line 1 "prepareas.pgc"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+#line 1 "./../regression.h"
+
+
+
+
+
+
+#line 5 "prepareas.pgc"
+
+/* exec sql whenever sqlerror sqlprint ; */
+#line 6 "prepareas.pgc"
+
+
+static void
+check_result_of_insert(void)
+{
+ /* exec sql begin declare section */
+
+
+#line 12 "prepareas.pgc"
+ int ivar1 = 0 , ivar2 = 0 ;
+/* exec sql end declare section */
+#line 13 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select c1 , c2 from test", ECPGt_EOIT,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 15 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 15 "prepareas.pgc"
+
+ printf("%d %d\n", ivar1, ivar2);
+}
+
+int main(void)
+{
+ /* exec sql begin declare section */
+
+
+
+#line 22 "prepareas.pgc"
+ int ivar1 = 1 , ivar2 = 2 ;
+
+#line 23 "prepareas.pgc"
+ char v_include_dq_name [ 16 ] , v_include_ws_name [ 16 ] , v_normal_name [ 16 ] , v_query [ 64 ] ;
+/* exec sql end declare section */
+#line 24 "prepareas.pgc"
+
+
+ strcpy(v_normal_name, "normal_name");
+ strcpy(v_include_dq_name, "include_\"_name");
+ strcpy(v_include_ws_name, "include_ _name");
+ strcpy(v_query, "insert into test values(?,?)");
+
+ /*
+ * preparing for test
+ */
+ { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
+#line 34 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 34 "prepareas.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "begin");
+#line 35 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 35 "prepareas.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test ( c1 int , c2 int )", ECPGt_EOIT, ECPGt_EORT);
+#line 36 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 36 "prepareas.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit work");
+#line 37 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 37 "prepareas.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "begin");
+#line 38 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 38 "prepareas.pgc"
+
+
+ /*
+ * Non dynamic statement
+ */
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 43 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 43 "prepareas.pgc"
+
+ printf("+++++ Test for prepnormal +++++\n");
+ printf("insert into test values(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 46 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 46 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 49 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 49 "prepareas.pgc"
+
+ printf("+++++ Test for execute immediate +++++\n");
+ printf("execute immediate \"insert into test values(1,2)\"\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_immediate, "insert into test values(1,2)", ECPGt_EOIT, ECPGt_EORT);
+#line 52 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 52 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ /*
+ * PREPARE FROM
+ */
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 58 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 58 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE ident FROM CString +++++\n");
+ printf("prepare ident_name from \"insert into test values(?,?)\"\n");
+ { ECPGprepare(__LINE__, NULL, 0, "ident_name", "insert into test values(?,?)");
+#line 61 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 61 "prepareas.pgc"
+
+ printf("execute ident_name using :ivar1,:ivar2\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ident_name",
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 63 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 66 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 66 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE char_variable_normal_name FROM char_variable +++++\n");
+ printf("prepare :v_normal_name from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, v_normal_name, v_query);
+#line 69 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "prepareas.pgc"
+
+ printf("execute :v_normal_name using :ivar1,:ivar2\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, v_normal_name,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 71 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 71 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 74 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 74 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE char_variable_inc_dq_name FROM char_variable +++++\n");
+ printf("prepare :v_include_dq_name from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, v_include_dq_name, v_query);
+#line 77 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 77 "prepareas.pgc"
+
+ printf("execute :v_include_dq_name using :ivar1,:ivar2\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, v_include_dq_name,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 79 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 79 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 82 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 82 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE char_variable_inc_ws_name FROM char_variable +++++\n");
+ printf("prepare :v_include_ws_name from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, v_include_ws_name, v_query);
+#line 85 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 85 "prepareas.pgc"
+
+ printf("execute :v_include_ws_name using :ivar1,:ivar2\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, v_include_ws_name,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 87 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 90 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 90 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE CString_inc_ws_name FROM char_variable +++++\n");
+ printf("prepare \"include_ _name\" from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, "include_ _name", v_query);
+#line 93 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 93 "prepareas.pgc"
+
+ printf("exec sql execute \"include_ _name\" using :ivar1,:ivar2\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "include_ _name",
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 95 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 98 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 98 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE CString_normal_name FROM char_variable +++++\n");
+ printf("prepare \"norma_name\" from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, "normal_name", v_query);
+#line 101 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 101 "prepareas.pgc"
+
+ printf("exec sql execute \"normal_name\" using :ivar1,:ivar2\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "normal_name",
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 103 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 103 "prepareas.pgc"
+
+ check_result_of_insert();
+
+ /*
+ * PREPARE AS
+ */
+ { ECPGdeallocate(__LINE__, 0, NULL, "ident_name");
+#line 109 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 109 "prepareas.pgc"
+
+ { ECPGdeallocate(__LINE__, 0, NULL, "normal_name");
+#line 110 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 110 "prepareas.pgc"
+
+ { ECPGdeallocate(__LINE__, 0, NULL, "include_ _name");
+#line 111 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 111 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 113 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 113 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE ident(typelist) AS +++++\n");
+ printf("prepare ident_name(int,int) as insert into test values($1,$2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_prepare, "prepare $0 ( int , int ) as insert into test values ( $1 , $2 )",
+ ECPGt_const,"ident_name",(long)10,(long)1,strlen("ident_name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 116 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 116 "prepareas.pgc"
+
+ printf("execute ident_name(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_const,"ident_name",(long)10,(long)1,strlen("ident_name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 118 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 118 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "ident_name");
+#line 120 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 120 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 122 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 122 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE CString_normal_name(typelist) AS +++++\n");
+ printf("prepare \"normal_name\"(int,int) as insert into test values($1,$2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_prepare, "prepare $0 ( int , int ) as insert into test values ( $1 , $2 )",
+ ECPGt_const,"normal_name",(long)11,(long)1,strlen("normal_name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 125 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 125 "prepareas.pgc"
+
+ printf("execute \"normal_name\"(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_const,"normal_name",(long)11,(long)1,strlen("normal_name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 127 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 127 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "normal_name");
+#line 129 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 129 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 131 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 131 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE CString_include_ws_name(typelist) AS +++++\n");
+ printf("prepare \"include_ _name\"(int,int) as insert into test values($1,$2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_prepare, "prepare $0 ( int , int ) as insert into test values ( $1 , $2 )",
+ ECPGt_const,"include_ _name",(long)14,(long)1,strlen("include_ _name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 134 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 134 "prepareas.pgc"
+
+ printf("execute \"include_ _name\"(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_const,"include_ _name",(long)14,(long)1,strlen("include_ _name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 136 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 136 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "include_ _name");
+#line 138 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 138 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 140 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 140 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE char_variable_normal_name(typelist) AS +++++\n");
+ printf("prepare :v_normal_name(int,int) as insert into test values($1,$2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_prepare, "prepare $0 ( int , int ) as insert into test values ( $1 , $2 )",
+ ECPGt_char,(v_normal_name),(long)16,(long)1,(16)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 143 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 143 "prepareas.pgc"
+
+ printf("execute :v_normal_name(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_char,(v_normal_name),(long)16,(long)1,(16)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 145 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 145 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "normal_name");
+#line 147 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 147 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 149 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 149 "prepareas.pgc"
+
+ printf("+++++ Test for PREPARE char_variable_include_ws_name(typelist) AS +++++\n");
+ printf("prepare :v_include_ws_name(int,int) as insert into test values($1,$2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_prepare, "prepare $0 ( int , int ) as insert into test values ( $1 , $2 )",
+ ECPGt_char,(v_include_ws_name),(long)16,(long)1,(16)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 152 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 152 "prepareas.pgc"
+
+ printf("execute :v_include_ws_name(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_char,(v_include_ws_name),(long)16,(long)1,(16)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 154 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 154 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "include_ _name");
+#line 156 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 156 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 158 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 158 "prepareas.pgc"
+
+ printf("+++++ Test for EXECUTE :v_normal_name(const,const) +++++\n");
+ printf("prepare :v_normal_name from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, v_normal_name, v_query);
+#line 161 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 161 "prepareas.pgc"
+
+ printf("execute :v_normal_name(1,2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( 1 , 2 )",
+ ECPGt_char,(v_normal_name),(long)16,(long)1,(16)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 163 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 163 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "normal_name");
+#line 165 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 165 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 167 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 167 "prepareas.pgc"
+
+ printf("+++++ Test for EXECUTE :v_normal_name(expr,expr) +++++\n");
+ printf("prepare :v_normal_name from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, v_normal_name, v_query);
+#line 170 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 170 "prepareas.pgc"
+
+ printf("execute :v_normal_name(0+1,1+1)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( 0 + 1 , 1 + 1 )",
+ ECPGt_char,(v_normal_name),(long)16,(long)1,(16)*sizeof(char),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 172 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 172 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "normal_name");
+#line 174 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 174 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 176 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 176 "prepareas.pgc"
+
+ printf("+++++ Test for combination PREPARE FROM and EXECUTE ident(typelist) +++++\n");
+ printf("prepare ident_name from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, "ident_name", v_query);
+#line 179 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 179 "prepareas.pgc"
+
+ printf("execute ident_name(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_const,"ident_name",(long)10,(long)1,strlen("ident_name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 181 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 181 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "ident_name");
+#line 183 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 183 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 185 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 185 "prepareas.pgc"
+
+ printf("+++++ Test for combination PREPARE FROM and EXECUTE CString_include_ws_name(typelist) +++++\n");
+ printf("prepare \"include_ _name\" from :v_query\n");
+ { ECPGprepare(__LINE__, NULL, 0, "include_ _name", v_query);
+#line 188 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 188 "prepareas.pgc"
+
+ printf("execute \"include_ _name\"(:ivar1,:ivar2)\n");
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_exec_with_exprlist, "execute $0 ( $1 , $2 )",
+ ECPGt_const,"include_ _name",(long)14,(long)1,strlen("include_ _name"),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar1),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_int,&(ivar2),(long)1,(long)1,sizeof(int),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 190 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 190 "prepareas.pgc"
+
+ check_result_of_insert();
+ { ECPGdeallocate(__LINE__, 0, NULL, "include_ _name");
+#line 192 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 192 "prepareas.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 194 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 194 "prepareas.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit work");
+#line 195 "prepareas.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 195 "prepareas.pgc"
+
+
+ return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-prepareas.stderr b/src/interfaces/ecpg/test/expected/sql-prepareas.stderr
new file mode 100644
index 0000000..e69de29
diff --git a/src/interfaces/ecpg/test/expected/sql-prepareas.stdout b/src/interfaces/ecpg/test/expected/sql-prepareas.stdout
new file mode 100644
index 0000000..d875442
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-prepareas.stdout
@@ -0,0 +1,66 @@
++++++ Test for prepnormal +++++
+insert into test values(:ivar1,:ivar2)
+1 2
++++++ Test for execute immediate +++++
+execute immediate "insert into test values(1,2)"
+1 2
++++++ Test for PREPARE ident FROM CString +++++
+prepare ident_name from "insert into test values(?,?)"
+execute ident_name using :ivar1,:ivar2
+1 2
++++++ Test for PREPARE char_variable_normal_name FROM char_variable +++++
+prepare :v_normal_name from :v_query
+execute :v_normal_name using :ivar1,:ivar2
+1 2
++++++ Test for PREPARE char_variable_inc_dq_name FROM char_variable +++++
+prepare :v_include_dq_name from :v_query
+execute :v_include_dq_name using :ivar1,:ivar2
+1 2
++++++ Test for PREPARE char_variable_inc_ws_name FROM char_variable +++++
+prepare :v_include_ws_name from :v_query
+execute :v_include_ws_name using :ivar1,:ivar2
+1 2
++++++ Test for PREPARE CString_inc_ws_name FROM char_variable +++++
+prepare "include_ _name" from :v_query
+exec sql execute "include_ _name" using :ivar1,:ivar2
+1 2
++++++ Test for PREPARE CString_normal_name FROM char_variable +++++
+prepare "norma_name" from :v_query
+exec sql execute "normal_name" using :ivar1,:ivar2
+1 2
++++++ Test for PREPARE ident(typelist) AS +++++
+prepare ident_name(int,int) as insert into test values($1,$2)
+execute ident_name(:ivar1,:ivar2)
+1 2
++++++ Test for PREPARE CString_normal_name(typelist) AS +++++
+prepare "normal_name"(int,int) as insert into test values($1,$2)
+execute "normal_name"(:ivar1,:ivar2)
+1 2
++++++ Test for PREPARE CString_include_ws_name(typelist) AS +++++
+prepare "include_ _name"(int,int) as insert into test values($1,$2)
+execute "include_ _name"(:ivar1,:ivar2)
+1 2
++++++ Test for PREPARE char_variable_normal_name(typelist) AS +++++
+prepare :v_normal_name(int,int) as insert into test values($1,$2)
+execute :v_normal_name(:ivar1,:ivar2)
+1 2
++++++ Test for PREPARE char_variable_include_ws_name(typelist) AS +++++
+prepare :v_include_ws_name(int,int) as insert into test values($1,$2)
+execute :v_include_ws_name(:ivar1,:ivar2)
+1 2
++++++ Test for EXECUTE :v_normal_name(const,const) +++++
+prepare :v_normal_name from :v_query
+execute :v_normal_name(1,2)
+1 2
++++++ Test for EXECUTE :v_normal_name(expr,expr) +++++
+prepare :v_normal_name from :v_query
+execute :v_normal_name(0+1,1+1)
+1 2
++++++ Test for combination PREPARE FROM and EXECUTE ident(typelist) +++++
+prepare ident_name from :v_query
+execute ident_name(:ivar1,:ivar2)
+1 2
++++++ Test for combination PREPARE FROM and EXECUTE CString_include_ws_name(typelist) +++++
+prepare "include_ _name" from :v_query
+execute "include_ _name"(:ivar1,:ivar2)
+1 2
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index 4e018cc..876ca8d 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -27,7 +27,8 @@ TESTS = array array.c \
twophase twophase.c \
insupd insupd.c \
declare declare.c \
- bytea bytea.c
+ bytea bytea.c \
+ prepareas prepareas.c
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/sql/prepareas.pgc b/src/interfaces/ecpg/test/sql/prepareas.pgc
new file mode 100644
index 0000000..85f03d7
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/prepareas.pgc
@@ -0,0 +1,198 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+exec sql include ../regression;
+exec sql whenever sqlerror sqlprint;
+
+static void
+check_result_of_insert(void)
+{
+ exec sql begin declare section;
+ int ivar1 = 0, ivar2 = 0;
+ exec sql end declare section;
+
+ exec sql select c1,c2 into :ivar1,:ivar2 from test;
+ printf("%d %d\n", ivar1, ivar2);
+}
+
+int main(void)
+{
+ exec sql begin declare section;
+ int ivar1 = 1, ivar2 = 2;
+ char v_include_dq_name[16], v_include_ws_name[16], v_normal_name[16], v_query[64];
+ exec sql end declare section;
+
+ strcpy(v_normal_name, "normal_name");
+ strcpy(v_include_dq_name, "include_\"_name");
+ strcpy(v_include_ws_name, "include_ _name");
+ strcpy(v_query, "insert into test values(?,?)");
+
+ /*
+ * preparing for test
+ */
+ exec sql connect to REGRESSDB1;
+ exec sql begin;
+ exec sql create table test (c1 int, c2 int);
+ exec sql commit work;
+ exec sql begin;
+
+ /*
+ * Non dynamic statement
+ */
+ exec sql truncate test;
+ printf("+++++ Test for prepnormal +++++\n");
+ printf("insert into test values(:ivar1,:ivar2)\n");
+ exec sql insert into test values(:ivar1,:ivar2);
+ check_result_of_insert();
+
+ exec sql truncate test;
+ printf("+++++ Test for execute immediate +++++\n");
+ printf("execute immediate \"insert into test values(1,2)\"\n");
+ exec sql execute immediate "insert into test values(1,2)";
+ check_result_of_insert();
+
+ /*
+ * PREPARE FROM
+ */
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE ident FROM CString +++++\n");
+ printf("prepare ident_name from \"insert into test values(?,?)\"\n");
+ exec sql prepare ident_name from "insert into test values(?,?)";
+ printf("execute ident_name using :ivar1,:ivar2\n");
+ exec sql execute ident_name using :ivar1,:ivar2;
+ check_result_of_insert();
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE char_variable_normal_name FROM char_variable +++++\n");
+ printf("prepare :v_normal_name from :v_query\n");
+ exec sql prepare :v_normal_name from :v_query;
+ printf("execute :v_normal_name using :ivar1,:ivar2\n");
+ exec sql execute :v_normal_name using :ivar1,:ivar2;
+ check_result_of_insert();
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE char_variable_inc_dq_name FROM char_variable +++++\n");
+ printf("prepare :v_include_dq_name from :v_query\n");
+ exec sql prepare :v_include_dq_name from :v_query;
+ printf("execute :v_include_dq_name using :ivar1,:ivar2\n");
+ exec sql execute :v_include_dq_name using :ivar1,:ivar2;
+ check_result_of_insert();
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE char_variable_inc_ws_name FROM char_variable +++++\n");
+ printf("prepare :v_include_ws_name from :v_query\n");
+ exec sql prepare :v_include_ws_name from :v_query;
+ printf("execute :v_include_ws_name using :ivar1,:ivar2\n");
+ exec sql execute :v_include_ws_name using :ivar1,:ivar2;
+ check_result_of_insert();
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE CString_inc_ws_name FROM char_variable +++++\n");
+ printf("prepare \"include_ _name\" from :v_query\n");
+ exec sql prepare "include_ _name" from :v_query;
+ printf("exec sql execute \"include_ _name\" using :ivar1,:ivar2\n");
+ exec sql execute "include_ _name" using :ivar1,:ivar2;
+ check_result_of_insert();
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE CString_normal_name FROM char_variable +++++\n");
+ printf("prepare \"norma_name\" from :v_query\n");
+ exec sql prepare "normal_name" from :v_query;
+ printf("exec sql execute \"normal_name\" using :ivar1,:ivar2\n");
+ exec sql execute "normal_name" using :ivar1,:ivar2;
+ check_result_of_insert();
+
+ /*
+ * PREPARE AS
+ */
+ exec sql deallocate "ident_name";
+ exec sql deallocate "normal_name";
+ exec sql deallocate "include_ _name";
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE ident(typelist) AS +++++\n");
+ printf("prepare ident_name(int,int) as insert into test values($1,$2)\n");
+ exec sql prepare ident_name(int,int) as insert into test values($1,$2);
+ printf("execute ident_name(:ivar1,:ivar2)\n");
+ exec sql execute ident_name(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "ident_name";
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE CString_normal_name(typelist) AS +++++\n");
+ printf("prepare \"normal_name\"(int,int) as insert into test values($1,$2)\n");
+ exec sql prepare "normal_name"(int,int) as insert into test values($1,$2);
+ printf("execute \"normal_name\"(:ivar1,:ivar2)\n");
+ exec sql execute "normal_name"(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "normal_name";
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE CString_include_ws_name(typelist) AS +++++\n");
+ printf("prepare \"include_ _name\"(int,int) as insert into test values($1,$2)\n");
+ exec sql prepare "include_ _name"(int,int) as insert into test values($1,$2);
+ printf("execute \"include_ _name\"(:ivar1,:ivar2)\n");
+ exec sql execute "include_ _name"(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "include_ _name";
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE char_variable_normal_name(typelist) AS +++++\n");
+ printf("prepare :v_normal_name(int,int) as insert into test values($1,$2)\n");
+ exec sql prepare :v_normal_name(int,int) as insert into test values($1,$2);
+ printf("execute :v_normal_name(:ivar1,:ivar2)\n");
+ exec sql execute :v_normal_name(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "normal_name";
+
+ exec sql truncate test;
+ printf("+++++ Test for PREPARE char_variable_include_ws_name(typelist) AS +++++\n");
+ printf("prepare :v_include_ws_name(int,int) as insert into test values($1,$2)\n");
+ exec sql prepare :v_include_ws_name(int,int) as insert into test values($1,$2);
+ printf("execute :v_include_ws_name(:ivar1,:ivar2)\n");
+ exec sql execute :v_include_ws_name(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "include_ _name";
+
+ exec sql truncate test;
+ printf("+++++ Test for EXECUTE :v_normal_name(const,const) +++++\n");
+ printf("prepare :v_normal_name from :v_query\n");
+ exec sql prepare :v_normal_name from :v_query;
+ printf("execute :v_normal_name(1,2)\n");
+ exec sql execute :v_normal_name(1,2);
+ check_result_of_insert();
+ exec sql deallocate "normal_name";
+
+ exec sql truncate test;
+ printf("+++++ Test for EXECUTE :v_normal_name(expr,expr) +++++\n");
+ printf("prepare :v_normal_name from :v_query\n");
+ exec sql prepare :v_normal_name from :v_query;
+ printf("execute :v_normal_name(0+1,1+1)\n");
+ exec sql execute :v_normal_name(0+1,1+1);
+ check_result_of_insert();
+ exec sql deallocate "normal_name";
+
+ exec sql truncate test;
+ printf("+++++ Test for combination PREPARE FROM and EXECUTE ident(typelist) +++++\n");
+ printf("prepare ident_name from :v_query\n");
+ exec sql prepare ident_name from :v_query;
+ printf("execute ident_name(:ivar1,:ivar2)\n");
+ exec sql execute ident_name(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "ident_name";
+
+ exec sql truncate test;
+ printf("+++++ Test for combination PREPARE FROM and EXECUTE CString_include_ws_name(typelist) +++++\n");
+ printf("prepare \"include_ _name\" from :v_query\n");
+ exec sql prepare "include_ _name" from :v_query;
+ printf("execute \"include_ _name\"(:ivar1,:ivar2)\n");
+ exec sql execute "include_ _name"(:ivar1,:ivar2);
+ check_result_of_insert();
+ exec sql deallocate "include_ _name";
+
+ exec sql drop table test;
+ exec sql commit work;
+
+ return 0;
+}
Hi Matsumura-san,
(1)
I attach a new patch. Please review it.
...
This looks good to me. It passes all my tests, too.
There were two minor issues, the regression test did not run and gcc
complained about the indentation in ECPGprepare(). Both I easily fixed.
Unless somebody objects I will commit it as soon as I have time at
hand. Given that this patch also and mostly fixes some completely
broken old logic I'm tempted to do so despite us being pretty far in
the release cycle. Any objections?
(2)
I found some bugs (two types). I didn't fix them and only avoid bison
error.Type1. Bugs or intentional unsupported features.
- EXPLAIN EXECUTE
- CREATE TABLE AS with using clause
...
Please send a patch. I'm on vacation and won't be able to spend time on
this for the next couple of weeks.
Type2. In multi-bytes encoding environment, a part of character of
message is cut off.It may be very difficult to fix. I pretend I didn't see it for a
while.
...
Hmm, any suggestion?
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Michael Meskes <meskes@postgresql.org> writes:
Unless somebody objects I will commit it as soon as I have time at
hand. Given that this patch also and mostly fixes some completely
broken old logic I'm tempted to do so despite us being pretty far in
the release cycle. Any objections?
None here. You might want to get it in in the next 12 hours or so
so you don't have to rebase over a pgindent run.
regards, tom lane
None here. You might want to get it in in the next 12 hours or so
so you don't have to rebase over a pgindent run.
Thanks for the heads-up Tom, pushed.
And thanks to Matsumura-san for the patch.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
On Wed, May 22, 2019 at 05:10:14AM +0200, Michael Meskes wrote:
Thanks for the heads-up Tom, pushed.
And thanks to Matsumura-san for the patch.
This patch seems to have little incidence on the stability, but FWIW I
am not cool with the concept of asking for objections and commit a
patch only 4 hours after-the-fact, particularly after feature freeze.
--
Michael
This patch seems to have little incidence on the stability, but FWIW
I
am not cool with the concept of asking for objections and commit a
patch only 4 hours after-the-fact, particularly after feature freeze.
This was only done to beat the pg_indent run as Tom pointed out. I
figured worse case we can revert the patch if people object.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Michael Paquier <michael@paquier.xyz> writes:
This patch seems to have little incidence on the stability, but FWIW I
am not cool with the concept of asking for objections and commit a
patch only 4 hours after-the-fact, particularly after feature freeze.
FWIW, I figured it was okay since ECPG has essentially no impact on
the rest of the system. The motivation for having feature freeze is
to get us to concentrate on stability, but any new bugs in ECPG
aren't going to affect the stability of anything else.
Also, I don't think it's that hard to look at this as a bug fix
rather than a new feature. The general expectation is that ECPG
can parse any command the backend can --- that's why we went to
all the trouble of automatically building its grammar from the
backend's. So I was surprised to hear that it didn't work on
some EXECUTE variants, and filling in that gap doesn't seem like a
"new feature" to me. Note the lack of any documentation additions
in the patch.
regards, tom lane
Meskes-san
This looks good to me. It passes all my tests, too.
There were two minor issues, the regression test did not run and gcc
complained about the indentation in ECPGprepare(). Both I easily fixed.
Thank you so much !
(2)
I found some bugs (two types). I didn't fix them and only avoid bison
error.Type1. Bugs or intentional unsupported features.
- EXPLAIN EXECUTE
- CREATE TABLE AS with using clause
...Please send a patch. I'm on vacation and won't be able to spend time on
this for the next couple of weeks.
I begin to fix it. It may spend a while (1 or 2 week).
Type2. In multi-bytes encoding environment, a part of character of
message is cut off.It may be very difficult to fix. I pretend I didn't see it for a
while.
...Hmm, any suggestion?
I think that it's better to import length_in_encoding() defined in backend/utils/mb/mbutils.c into client side.
Just an idea.
Regards
Ryo Matsumura