From db63e8523e06ba4645ac335e58f0a60c6377e571 Mon Sep 17 00:00:00 2001 From: Zhibai Song Date: Thu, 2 Nov 2023 12:52:09 +0000 Subject: [PATCH 1/4] insert varchar column mismatch through INSERT INTO EXEC into a table Task: BABEL-2999 Signed-off-by: Zhibai Song --- contrib/babelfishpg_tsql/src/pl_exec-2.c | 234 ++++++++++++++++++- contrib/babelfishpg_tsql/src/pl_exec.c | 1 - contrib/babelfishpg_tsql/src/pl_handler.c | 2 + test/JDBC/expected/BABEL-2999-vu-cleanup.out | 29 +++ test/JDBC/expected/BABEL-2999-vu-prepare.out | 32 +++ test/JDBC/expected/BABEL-2999-vu-verify.out | 216 +++++++++++++++++ test/JDBC/input/BABEL-2999-vu-cleanup.sql | 29 +++ test/JDBC/input/BABEL-2999-vu-prepare.sql | 32 +++ test/JDBC/input/BABEL-2999-vu-verify.sql | 104 +++++++++ test/JDBC/upgrade/13_4/schedule | 1 + test/JDBC/upgrade/13_5/schedule | 1 + test/JDBC/upgrade/13_6/schedule | 1 + test/JDBC/upgrade/13_7/schedule | 1 + test/JDBC/upgrade/13_8/schedule | 1 + test/JDBC/upgrade/13_9/schedule | 1 + test/JDBC/upgrade/14_10/schedule | 1 + test/JDBC/upgrade/14_3/schedule | 1 + test/JDBC/upgrade/14_5/schedule | 1 + test/JDBC/upgrade/14_6/schedule | 1 + test/JDBC/upgrade/14_7/schedule | 1 + test/JDBC/upgrade/14_8/schedule | 1 + test/JDBC/upgrade/14_9/schedule | 1 + test/JDBC/upgrade/15_1/schedule | 1 + test/JDBC/upgrade/15_2/schedule | 1 + test/JDBC/upgrade/15_3/schedule | 1 + test/JDBC/upgrade/15_4/schedule | 1 + test/JDBC/upgrade/latest/schedule | 1 + 27 files changed, 691 insertions(+), 6 deletions(-) create mode 100644 test/JDBC/expected/BABEL-2999-vu-cleanup.out create mode 100644 test/JDBC/expected/BABEL-2999-vu-prepare.out create mode 100644 test/JDBC/expected/BABEL-2999-vu-verify.out create mode 100644 test/JDBC/input/BABEL-2999-vu-cleanup.sql create mode 100644 test/JDBC/input/BABEL-2999-vu-prepare.sql create mode 100644 test/JDBC/input/BABEL-2999-vu-verify.sql diff --git a/contrib/babelfishpg_tsql/src/pl_exec-2.c b/contrib/babelfishpg_tsql/src/pl_exec-2.c index ca90cfe7d4..d9f0f97fe3 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec-2.c +++ b/contrib/babelfishpg_tsql/src/pl_exec-2.c @@ -4,7 +4,9 @@ #include "funcapi.h" #include "access/table.h" +#include "access/attmap.h" #include "catalog/namespace.h" +#include "catalog/pg_attribute.h" #include "catalog/pg_language.h" #include "commands/proclang.h" #include "executor/tstoreReceiver.h" @@ -24,6 +26,17 @@ PLtsql_execstate *get_current_tsql_estate(void); PLtsql_execstate *get_outermost_tsql_estate(int *nestlevel); +typedef struct TupleCastMap +{ + TupleDesc indesc; /* tupdesc for source rowtype */ + TupleDesc outdesc; /* tupdesc for result rowtype */ + AttrMap *attrMap; /* indexes of input fields, or 0 for null */ + Datum *invalues; /* workspace for deconstructing source */ + bool *inisnull; + Datum *outvalues; /* workspace for constructing result */ + bool *outisnull; +} TupleCastMap; + /* * NOTE: * A SET...(SELECT) statement that returns more than one row will raise an error @@ -113,6 +126,15 @@ static bool prev_insert_bulk_keep_nulls = false; /* return a underlying node if n is implicit casting and underlying node is a certain type of node */ static Node *get_underlying_node_from_implicit_casting(Node *n, NodeTag underlying_nodetype); +static TupleCastMap *cast_tuples_by_position(TupleDesc indesc, + TupleDesc outdesc, + const char *msg); +static AttrMap* build_typecast_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); +static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleCastMap *tupleCastMap); +static bool +check_attrmap_match(TupleDesc indesc, + TupleDesc outdesc, + AttrMap *attrMap); /* * The pltsql_proc_return_code global variable is used to record the * return code (RETURN 41 + 1) of the most recently completed procedure @@ -2995,7 +3017,7 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) { Portal portal; uint64 processed = 0; - TupleConversionMap *tupmap; + TupleCastMap *tupmap; MemoryContext oldcontext; if (estate->tuple_store == NULL) @@ -3007,7 +3029,7 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) /* Use eval_mcontext for tuple conversion work */ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate)); - tupmap = convert_tuples_by_position(portal->tupDesc, + tupmap = cast_tuples_by_position(portal->tupDesc, estate->tuple_store_desc, gettext_noop("structure of query does not match function result type")); @@ -3028,7 +3050,7 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) HeapTuple tuple = SPI_tuptable->vals[i]; if (tupmap) - tuple = execute_attr_map_tuple(tuple, tupmap); + tuple = exec_cast_tuple(tuple, tupmap); tuplestore_puttuple(estate->tuple_store, tuple); if (tupmap) heap_freetuple(tuple); @@ -3047,6 +3069,210 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) return PLTSQL_RC_OK; } +static AttrMap* build_typecast_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg) +{ + AttrMap *attrMap; + int nincols; + int noutcols; + int n; + int i; + int j; + bool same; + + /* + * The length is computed as the number of attributes of the expected + * rowtype as it includes dropped attributes in its count. + */ + n = outdesc->natts; + attrMap = make_attrmap(n); + + j = 0; /* j is next physical input attribute */ + nincols = noutcols = 0; /* these count non-dropped attributes */ + same = true; + for (i = 0; i < n; i++) + { + Form_pg_attribute outatt = TupleDescAttr(outdesc, i); + + if (outatt->attisdropped) + continue; /* attrMap->attnums[i] is already 0 */ + noutcols++; + for (; j < indesc->natts; j++) + { + Form_pg_attribute inatt = TupleDescAttr(outdesc, j); + if (inatt->attisdropped) + continue; + nincols++; + + attrMap->attnums[i] = (AttrNumber) (j + 1); + j++; + break; + } + if (attrMap->attnums[i] == 0) + same = false; /* we'll complain below */ + } + + /* Check for unused input columns */ + for (; j < indesc->natts; j++) + { + if (TupleDescAttr(indesc, j)->attisdropped) + continue; + nincols++; + same = false; /* we'll complain below */ + } + + /* Report column count mismatch using the non-dropped-column counts */ + if (!same) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg_internal("%s", _(msg)), + errdetail("Number of returned columns (%d) does not match " + "expected column count (%d).", + nincols, noutcols))); + + /* Check if the map has a one-to-one match */ + if (check_attrmap_match(indesc, outdesc, attrMap)) + { + /* Runtime conversion is not needed */ + free_attrmap(attrMap); + return NULL; + } + + return attrMap; +} + +/* + * check_attrmap_match + * + * Check to see if the map is a one-to-one match, in which case we need + * not to do a tuple conversion, and the attribute map is not necessary. + */ +static bool +check_attrmap_match(TupleDesc indesc, + TupleDesc outdesc, + AttrMap *attrMap) +{ + int i; + + /* no match if attribute numbers are not the same */ + if (indesc->natts != outdesc->natts) + return false; + + for (i = 0; i < attrMap->maplen; i++) + { + Form_pg_attribute inatt = TupleDescAttr(indesc, i); + Form_pg_attribute outatt = TupleDescAttr(outdesc, i); + + /* + * If the input column has a missing attribute, we need a conversion. + */ + if (inatt->atthasmissing) + return false; + + if (inatt->atttypid != outatt->atttypid || + inatt->atttypmod != outatt->atttypmod) + return false; + + if (attrMap->attnums[i] == (i + 1)) + continue; + + /* + * If it's a dropped column and the corresponding input column is also + * dropped, we don't need a conversion. However, attlen and attalign + * must agree. + */ + if (attrMap->attnums[i] == 0 && + inatt->attisdropped && + inatt->attlen == outatt->attlen && + inatt->attalign == outatt->attalign) + continue; + + return false; + } + + return true; +} + +static TupleCastMap *cast_tuples_by_position(TupleDesc indesc, + TupleDesc outdesc, + const char *msg) +{ + TupleCastMap *map; + int n; + AttrMap *attrMap; + + /* Verify compatibility and prepare attribute-number map */ + attrMap = build_typecast_attrmap_by_position(indesc, outdesc, msg); + + if (attrMap == NULL) + { + /* runtime cast is not needed */ + return NULL; + } + + /* Prepare the map structure */ + map = (TupleCastMap *) palloc(sizeof(TupleCastMap)); + map->indesc = indesc; + map->outdesc = outdesc; + map->attrMap = attrMap; + /* preallocate workspace for Datum arrays */ + n = outdesc->natts + 1; /* +1 for NULL */ + map->outvalues = (Datum *) palloc(n * sizeof(Datum)); + map->outisnull = (bool *) palloc(n * sizeof(bool)); + n = indesc->natts + 1; /* +1 for NULL */ + map->invalues = (Datum *) palloc(n * sizeof(Datum)); + map->inisnull = (bool *) palloc(n * sizeof(bool)); + map->invalues[0] = (Datum) 0; /* set up the NULL entry */ + map->inisnull[0] = true; + + return map; +} + +static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleCastMap *tupleCastMap) +{ + AttrMap *attrMap = tupleCastMap->attrMap; + Oid intypeid; + Oid outtypeid; + int inttypemod; + int outtypemod; + Datum *invalues = tupleCastMap->invalues; + bool *inisnull = tupleCastMap->inisnull; + Datum *outvalues = tupleCastMap->outvalues; + bool *outisnull = tupleCastMap->outisnull; + int i; + + /* + * Extract all the values of the old tuple, offsetting the arrays so that + * invalues[0] is left NULL and invalues[1] is the first source attribute; + * this exactly matches the numbering convention in attrMap. + */ + heap_deform_tuple(tuple, tupleCastMap->indesc, invalues + 1, inisnull + 1); + + /* + * Transpose into proper fields of the new tuple. + */ + Assert(attrMap->maplen == tupleCastMap->outdesc->natts); + for (i = 0; i < attrMap->maplen; i++) + { + int j = attrMap->attnums[i]; + Form_pg_attribute outatt = TupleDescAttr(tupleCastMap->outdesc, i); + Form_pg_attribute inatt = TupleDescAttr(tupleCastMap->indesc, i); + + outtypeid = outatt->atttypid; + outtypemod = outatt->atttypmod; + intypeid = inatt->atttypid; + inttypemod = inatt->atttypmod; + + outisnull[i] = inisnull[j]; + outvalues[i] = exec_cast_value(get_current_tsql_estate(), + invalues[j], &outisnull[i], + intypeid, inttypemod, + outtypeid, outtypemod + ); + } + // now form the new tuple + return heap_form_tuple(tupleCastMap->outdesc, outvalues, outisnull); +} + int exec_stmt_insert_bulk(PLtsql_execstate *estate, PLtsql_stmt_insert_bulk *stmt) { @@ -3118,7 +3344,6 @@ exec_stmt_insert_bulk(PLtsql_execstate *estate, PLtsql_stmt_insert_bulk *stmt) return PLTSQL_RC_OK; } - int exec_stmt_dbcc(PLtsql_execstate *estate, PLtsql_stmt_dbcc *stmt) { switch (stmt->dbcc_stmt_type) @@ -3132,7 +3357,6 @@ int exec_stmt_dbcc(PLtsql_execstate *estate, PLtsql_stmt_dbcc *stmt) return PLTSQL_RC_OK; } - void exec_stmt_dbcc_checkident(PLtsql_stmt_dbcc *stmt) { struct dbcc_checkident dbcc_stmt = stmt->dbcc_stmt_data.dbcc_checkident; diff --git a/contrib/babelfishpg_tsql/src/pl_exec.c b/contrib/babelfishpg_tsql/src/pl_exec.c index 2beef6a34a..95551b94e2 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec.c +++ b/contrib/babelfishpg_tsql/src/pl_exec.c @@ -4347,7 +4347,6 @@ pltsql_estate_setup(PLtsql_execstate *estate, estate->insert_exec = (func->fn_prokind == PROKIND_PROCEDURE || strcmp(func->fn_signature, "inline_code_block") == 0) && rsi; - estate->pivot_number = 0; estate->pivot_parsetree_list = NIL; diff --git a/contrib/babelfishpg_tsql/src/pl_handler.c b/contrib/babelfishpg_tsql/src/pl_handler.c index 9376b161aa..45f546e4b2 100644 --- a/contrib/babelfishpg_tsql/src/pl_handler.c +++ b/contrib/babelfishpg_tsql/src/pl_handler.c @@ -4783,6 +4783,7 @@ pltsql_inline_handler(PG_FUNCTION_ARGS) rsinfo.isDone = ExprSingleResult; rsinfo.setResult = NULL; rsinfo.setDesc = NULL; + ReleaseTupleDesc(reldesc); } /* And run the function */ @@ -4849,6 +4850,7 @@ pltsql_inline_handler(PG_FUNCTION_ARGS) dest->receiveSlot(slot, dest); ExecClearTuple(slot); } + ReleaseTupleDesc(rsinfo.expectedDesc); ExecDropSingleTupleTableSlot(slot); } diff --git a/test/JDBC/expected/BABEL-2999-vu-cleanup.out b/test/JDBC/expected/BABEL-2999-vu-cleanup.out new file mode 100644 index 0000000000..4b79d8c4f6 --- /dev/null +++ b/test/JDBC/expected/BABEL-2999-vu-cleanup.out @@ -0,0 +1,29 @@ +drop table if exists t2_BABEL2999; +GO + +drop table t1_BABEL2999; +GO + +drop table t3_BABEL2999; +GO + +drop table t3_BABEL2999_2; +GO + +drop procedure p1_BABEL2999; +GO + +drop procedure p2_BABEL2999 +GO + +drop procedure p3_BABEL2999 +GO + +drop table t4_BABEL2999; +GO + +drop table t5_BABEL2999; +GO + +drop table t6_BABEL2999 +GO diff --git a/test/JDBC/expected/BABEL-2999-vu-prepare.out b/test/JDBC/expected/BABEL-2999-vu-prepare.out new file mode 100644 index 0000000000..7d9b0bda16 --- /dev/null +++ b/test/JDBC/expected/BABEL-2999-vu-prepare.out @@ -0,0 +1,32 @@ +drop table if exists t1_BABEL2999; +GO + +create table t1_BABEL2999(b varchar(10)); +GO + +create table t2_BABEL2999(b int); +GO + +create table t3_BABEL2999(b varchar); +GO + +create procedure p1_BABEL2999 as select 'abc'; +GO + +create procedure p2_BABEL2999 as select 555; +GO + +create table t3_BABEL2999_2(a int, b datetime, c varchar(20)) +GO + +create procedure p3_BABEL2999 as select '123', 123, 123; +GO + +create table t4_BABEL2999( a binary(30), b varbinary(30), c varchar(30), d datetime, e smalldatetime) +GO + +create table t5_BABEL2999( a decimal, b numeric) +GO + +create table t6_BABEL2999( a int, b tinyint, c smallint) +GO diff --git a/test/JDBC/expected/BABEL-2999-vu-verify.out b/test/JDBC/expected/BABEL-2999-vu-verify.out new file mode 100644 index 0000000000..1fa89ffdd3 --- /dev/null +++ b/test/JDBC/expected/BABEL-2999-vu-verify.out @@ -0,0 +1,216 @@ +insert into t1_BABEL2999 exec('Select ''5'''); +GO +~~ROW COUNT: 1~~ + + +insert into t1_BABEL2999 exec('Select 5'); +GO +~~ROW COUNT: 1~~ + + +insert into t1_BABEL2999 exec('Select ''5'''); +GO +~~ROW COUNT: 1~~ + + +insert into t1_BABEL2999 exec('Select ''hello'''); +GO +~~ROW COUNT: 1~~ + + +insert into t1_BABEL2999 exec('SELECT ''helloworld'''); +GO +~~ROW COUNT: 1~~ + + +insert into t1_BABEL2999 exec('SELECT ''helloworldhello'''); +GO +~~ERROR (Code: 8152)~~ + +~~ERROR (Message: value too long for type character varying(10))~~ + + +select b from t1_BABEL2999 order by b; +GO +~~START~~ +varchar +5 +5 +5 +hello +helloworld +~~END~~ + + +insert into t2_BABEL2999 exec('Select ''5'''); -- varchar to int +GO +~~ROW COUNT: 1~~ + + +insert into t2_BABEL2999 exec('Select 5'); -- int to int +GO +~~ROW COUNT: 1~~ + + +insert into t2_BABEL2999 SELECT '5'; +GO +~~ROW COUNT: 1~~ + + +select b from t2_BABEL2999 order by b; +GO +~~START~~ +int +5 +5 +5 +~~END~~ + + +insert into t3_BABEL2999 exec('Select ''5'''); +GO +~~ROW COUNT: 1~~ + + +insert into t3_BABEL2999 exec('Select 5'); +GO +~~ROW COUNT: 1~~ + + +insert into t3_BABEL2999 exec('Select ''5'''); +GO +~~ROW COUNT: 1~~ + + +select b from t3_BABEL2999 order by b; +GO +~~START~~ +varchar +5 +5 +5 +~~END~~ + + +delete from t1_BABEL2999 +GO +~~ROW COUNT: 5~~ + + +insert into t1_BABEL2999 exec p1_BABEL2999; +GO +~~ROW COUNT: 1~~ + + +insert into t1_BABEL2999 exec('exec p1_BABEL2999'); +GO +~~ROW COUNT: 1~~ + + +select * from t1_BABEL2999; +GO +~~START~~ +varchar +abc +abc +~~END~~ + + +insert t3_BABEL2999_2 exec('select ''123'', 123, 123'); +GO +~~ROW COUNT: 1~~ + + +insert into t3_BABEL2999_2 exec p3_BABEL2999 +GO +~~ROW COUNT: 1~~ + + +insert into t3_BABEL2999_2 select '123', 123, 123 +GO +~~ROW COUNT: 1~~ + + +select * from t3_BABEL2999_2; +GO +~~START~~ +int#!#datetime#!#varchar +123#!#1900-05-04 00:00:00.0#!#123 +123#!#1900-05-04 00:00:00.0#!#123 +123#!#1900-05-04 00:00:00.0#!#123 +~~END~~ + + +insert into t3_BABEL_2999_2 exec('select ''123'''); +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t3_babel_2999_2" does not exist)~~ + + +insert into t3_BABEL_2999_2 exec('select 123, 123'); +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t3_babel_2999_2" does not exist)~~ + + +insert into t4_BABEL2999 exec('select 123, 123, 123, 123, 123') +GO +~~ROW COUNT: 1~~ + + +insert into t4_BABEL2999 exec('select cast(123 as binary), cast(123 as varbinary), 123, cast(123 as datetime), cast(123 as smalldatetime)') +GO +~~ROW COUNT: 1~~ + + +insert into t4_BABEL2999 select 123, 123, 123, 123, 123 +GO +~~ROW COUNT: 1~~ + + +insert into t4_BABEL2999 exec('select ''123'', ''123'', ''123'', ''123'', ''123'''); +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Implicit conversion from data type varchar to binary is not allowed. Use the CONVERT function to run this query.)~~ + + +select * from t4_BABEL2999 +GO +~~START~~ +binary#!#varbinary#!#varchar#!#datetime#!#smalldatetime +00000000000000000000000000000000000000000000000000000000007B#!#0000007B#!#123#!#1900-05-04 00:00:00.0#!#1900-05-04 00:00:00.0 +00000000000000000000000000000000000000000000000000000000007B#!#0000007B#!#123#!#1900-05-04 00:00:00.0#!#1900-05-04 00:00:00.0 +00000000000000000000000000000000000000000000000000000000007B#!#0000007B#!#123#!#1900-05-04 00:00:00.0#!#1900-05-04 00:00:00.0 +~~END~~ + + +insert into t5_BABEL2999 exec('select ''1.234'', ''33.33'''); +GO +~~ROW COUNT: 1~~ + + +select * from t5_BABEL2999 +GO +~~START~~ +numeric#!#numeric +1#!#33 +~~END~~ + + +insert into t6_BABEL2999 exec('select 1,2,3') +GO +~~ROW COUNT: 1~~ + + +insert into t6_BABEL2999 exec('select ''1'',''2'',''3''') +GO +~~ROW COUNT: 1~~ + + +insert into t6_BABEL2999 exec('select c,b,a from t6_BABEL2999') +GO +~~ROW COUNT: 2~~ + diff --git a/test/JDBC/input/BABEL-2999-vu-cleanup.sql b/test/JDBC/input/BABEL-2999-vu-cleanup.sql new file mode 100644 index 0000000000..4b79d8c4f6 --- /dev/null +++ b/test/JDBC/input/BABEL-2999-vu-cleanup.sql @@ -0,0 +1,29 @@ +drop table if exists t2_BABEL2999; +GO + +drop table t1_BABEL2999; +GO + +drop table t3_BABEL2999; +GO + +drop table t3_BABEL2999_2; +GO + +drop procedure p1_BABEL2999; +GO + +drop procedure p2_BABEL2999 +GO + +drop procedure p3_BABEL2999 +GO + +drop table t4_BABEL2999; +GO + +drop table t5_BABEL2999; +GO + +drop table t6_BABEL2999 +GO diff --git a/test/JDBC/input/BABEL-2999-vu-prepare.sql b/test/JDBC/input/BABEL-2999-vu-prepare.sql new file mode 100644 index 0000000000..7d9b0bda16 --- /dev/null +++ b/test/JDBC/input/BABEL-2999-vu-prepare.sql @@ -0,0 +1,32 @@ +drop table if exists t1_BABEL2999; +GO + +create table t1_BABEL2999(b varchar(10)); +GO + +create table t2_BABEL2999(b int); +GO + +create table t3_BABEL2999(b varchar); +GO + +create procedure p1_BABEL2999 as select 'abc'; +GO + +create procedure p2_BABEL2999 as select 555; +GO + +create table t3_BABEL2999_2(a int, b datetime, c varchar(20)) +GO + +create procedure p3_BABEL2999 as select '123', 123, 123; +GO + +create table t4_BABEL2999( a binary(30), b varbinary(30), c varchar(30), d datetime, e smalldatetime) +GO + +create table t5_BABEL2999( a decimal, b numeric) +GO + +create table t6_BABEL2999( a int, b tinyint, c smallint) +GO diff --git a/test/JDBC/input/BABEL-2999-vu-verify.sql b/test/JDBC/input/BABEL-2999-vu-verify.sql new file mode 100644 index 0000000000..1e4aa2d5ab --- /dev/null +++ b/test/JDBC/input/BABEL-2999-vu-verify.sql @@ -0,0 +1,104 @@ +insert into t1_BABEL2999 exec('Select ''5'''); +GO + +insert into t1_BABEL2999 exec('Select 5'); +GO + +insert into t1_BABEL2999 exec('Select ''5'''); +GO + +insert into t1_BABEL2999 exec('Select ''hello'''); +GO + +insert into t1_BABEL2999 exec('SELECT ''helloworld'''); +GO + +insert into t1_BABEL2999 exec('SELECT ''helloworldhello'''); +GO + +select b from t1_BABEL2999 order by b; +GO + +insert into t2_BABEL2999 exec('Select ''5'''); -- varchar to int +GO + +insert into t2_BABEL2999 exec('Select 5'); -- int to int +GO + +insert into t2_BABEL2999 SELECT '5'; +GO + +select b from t2_BABEL2999 order by b; +GO + +insert into t3_BABEL2999 exec('Select ''5'''); +GO + +insert into t3_BABEL2999 exec('Select 5'); +GO + +insert into t3_BABEL2999 exec('Select ''5'''); +GO + +select b from t3_BABEL2999 order by b; +GO + +delete from t1_BABEL2999 +GO + +insert into t1_BABEL2999 exec p1_BABEL2999; +GO + +insert into t1_BABEL2999 exec('exec p1_BABEL2999'); +GO + +select * from t1_BABEL2999; +GO + +insert t3_BABEL2999_2 exec('select ''123'', 123, 123'); +GO + +insert into t3_BABEL2999_2 exec p3_BABEL2999 +GO + +insert into t3_BABEL2999_2 select '123', 123, 123 +GO + +select * from t3_BABEL2999_2; +GO + +insert into t3_BABEL_2999_2 exec('select ''123'''); +GO + +insert into t3_BABEL_2999_2 exec('select 123, 123'); +GO + +insert into t4_BABEL2999 exec('select 123, 123, 123, 123, 123') +GO + +insert into t4_BABEL2999 exec('select cast(123 as binary), cast(123 as varbinary), 123, cast(123 as datetime), cast(123 as smalldatetime)') +GO + +insert into t4_BABEL2999 select 123, 123, 123, 123, 123 +GO + +insert into t4_BABEL2999 exec('select ''123'', ''123'', ''123'', ''123'', ''123'''); +GO + +select * from t4_BABEL2999 +GO + +insert into t5_BABEL2999 exec('select ''1.234'', ''33.33'''); +GO + +select * from t5_BABEL2999 +GO + +insert into t6_BABEL2999 exec('select 1,2,3') +GO + +insert into t6_BABEL2999 exec('select ''1'',''2'',''3''') +GO + +insert into t6_BABEL2999 exec('select c,b,a from t6_BABEL2999') +GO diff --git a/test/JDBC/upgrade/13_4/schedule b/test/JDBC/upgrade/13_4/schedule index 1689f9545a..802400bc3f 100644 --- a/test/JDBC/upgrade/13_4/schedule +++ b/test/JDBC/upgrade/13_4/schedule @@ -220,3 +220,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/13_5/schedule b/test/JDBC/upgrade/13_5/schedule index 836d2b4746..b2c9e5df3d 100644 --- a/test/JDBC/upgrade/13_5/schedule +++ b/test/JDBC/upgrade/13_5/schedule @@ -273,3 +273,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/13_6/schedule b/test/JDBC/upgrade/13_6/schedule index 6e9d73d368..1bbbdb8426 100644 --- a/test/JDBC/upgrade/13_6/schedule +++ b/test/JDBC/upgrade/13_6/schedule @@ -328,3 +328,4 @@ GRANT_SCHEMA AUTO_ANALYZE-before-15-5-or-14-10 TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/13_7/schedule b/test/JDBC/upgrade/13_7/schedule index 59c7d32a8e..e7634f4b39 100644 --- a/test/JDBC/upgrade/13_7/schedule +++ b/test/JDBC/upgrade/13_7/schedule @@ -321,3 +321,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/13_8/schedule b/test/JDBC/upgrade/13_8/schedule index 59c7d32a8e..e7634f4b39 100644 --- a/test/JDBC/upgrade/13_8/schedule +++ b/test/JDBC/upgrade/13_8/schedule @@ -321,3 +321,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/13_9/schedule b/test/JDBC/upgrade/13_9/schedule index 83572965b9..ef6a4961de 100644 --- a/test/JDBC/upgrade/13_9/schedule +++ b/test/JDBC/upgrade/13_9/schedule @@ -325,3 +325,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_10/schedule b/test/JDBC/upgrade/14_10/schedule index 25c32572d7..1064d1bdec 100644 --- a/test/JDBC/upgrade/14_10/schedule +++ b/test/JDBC/upgrade/14_10/schedule @@ -417,3 +417,4 @@ BABEL-3326 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_3/schedule b/test/JDBC/upgrade/14_3/schedule index 6921c752f4..7d0863cec1 100644 --- a/test/JDBC/upgrade/14_3/schedule +++ b/test/JDBC/upgrade/14_3/schedule @@ -343,3 +343,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_5/schedule b/test/JDBC/upgrade/14_5/schedule index fb6f50b15d..7b2c45e1f3 100644 --- a/test/JDBC/upgrade/14_5/schedule +++ b/test/JDBC/upgrade/14_5/schedule @@ -358,3 +358,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_6/schedule b/test/JDBC/upgrade/14_6/schedule index 4ae28e1831..00ec3f4823 100644 --- a/test/JDBC/upgrade/14_6/schedule +++ b/test/JDBC/upgrade/14_6/schedule @@ -393,3 +393,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_7/schedule b/test/JDBC/upgrade/14_7/schedule index 74a4494ecc..d71c279168 100644 --- a/test/JDBC/upgrade/14_7/schedule +++ b/test/JDBC/upgrade/14_7/schedule @@ -413,3 +413,4 @@ AUTO_ANALYZE-before-15-5-or-14-10 cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_8/schedule b/test/JDBC/upgrade/14_8/schedule index b3461a470e..deb6e59d05 100644 --- a/test/JDBC/upgrade/14_8/schedule +++ b/test/JDBC/upgrade/14_8/schedule @@ -412,3 +412,4 @@ default_params cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/14_9/schedule b/test/JDBC/upgrade/14_9/schedule index ec61ad67fd..fa471c060f 100644 --- a/test/JDBC/upgrade/14_9/schedule +++ b/test/JDBC/upgrade/14_9/schedule @@ -414,3 +414,4 @@ default_params cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/15_1/schedule b/test/JDBC/upgrade/15_1/schedule index 9cabb981b7..745d4ace3f 100644 --- a/test/JDBC/upgrade/15_1/schedule +++ b/test/JDBC/upgrade/15_1/schedule @@ -391,3 +391,4 @@ default_params cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 \ No newline at end of file diff --git a/test/JDBC/upgrade/15_2/schedule b/test/JDBC/upgrade/15_2/schedule index 68ad7cbede..2d053948d7 100644 --- a/test/JDBC/upgrade/15_2/schedule +++ b/test/JDBC/upgrade/15_2/schedule @@ -422,3 +422,4 @@ default_params cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/15_3/schedule b/test/JDBC/upgrade/15_3/schedule index 90da16c402..73a390d814 100644 --- a/test/JDBC/upgrade/15_3/schedule +++ b/test/JDBC/upgrade/15_3/schedule @@ -443,3 +443,4 @@ default_params cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/15_4/schedule b/test/JDBC/upgrade/15_4/schedule index 0ffb2ed104..b18a4b72b0 100644 --- a/test/JDBC/upgrade/15_4/schedule +++ b/test/JDBC/upgrade/15_4/schedule @@ -456,3 +456,4 @@ default_params cast_eliminate TestDatatypeAggSort babel_index_nulls_order-before-15-5 +BABEL-2999 diff --git a/test/JDBC/upgrade/latest/schedule b/test/JDBC/upgrade/latest/schedule index 7ea5127c17..c246df93f7 100644 --- a/test/JDBC/upgrade/latest/schedule +++ b/test/JDBC/upgrade/latest/schedule @@ -488,3 +488,4 @@ pivot cast_eliminate TestDatatypeAggSort babel_index_nulls_order +BABEL-2999 From 1f7c26398c5f0886db72dd86f0562ac93c39b887 Mon Sep 17 00:00:00 2001 From: Zhibai Song Date: Tue, 14 Nov 2023 23:20:52 +0000 Subject: [PATCH 2/4] delete copied code --- contrib/babelfishpg_tsql/src/hooks.c | 6 + contrib/babelfishpg_tsql/src/pl_exec-2.c | 199 ++--------------------- 2 files changed, 22 insertions(+), 183 deletions(-) diff --git a/contrib/babelfishpg_tsql/src/hooks.c b/contrib/babelfishpg_tsql/src/hooks.c index 9fb053e1f4..b36ad4a19c 100644 --- a/contrib/babelfishpg_tsql/src/hooks.c +++ b/contrib/babelfishpg_tsql/src/hooks.c @@ -164,6 +164,7 @@ static bool plsql_TriggerRecursiveCheck(ResultRelInfo *resultRelInfo); static bool bbf_check_rowcount_hook(int es_processed); static char *get_local_schema_for_bbf_functions(Oid proc_nsp_oid); +extern bool called_from_tsql_insert_exec(); /***************************************** * Replication Hooks @@ -231,6 +232,7 @@ static set_local_schema_for_func_hook_type prev_set_local_schema_for_func_hook = static bbf_get_sysadmin_oid_hook_type prev_bbf_get_sysadmin_oid_hook = NULL; /* TODO: do we need to use variable to store hook value before transfrom pivot? No other function uses the same hook, should be redundant */ static transform_pivot_clause_hook_type pre_transform_pivot_clause_hook = NULL; +static called_from_tsql_insert_exec_hook_type pre_called_from_tsql_insert_exec_hook = NULL; /***************************************** * Install / Uninstall @@ -396,6 +398,9 @@ InstallExtendedHooks(void) prev_optimize_explicit_cast_hook = optimize_explicit_cast_hook; optimize_explicit_cast_hook = optimize_explicit_cast; + + pre_called_from_tsql_insert_exec_hook = called_from_tsql_insert_exec_hook; + called_from_tsql_insert_exec_hook = called_from_tsql_insert_exec; } void @@ -459,6 +464,7 @@ UninstallExtendedHooks(void) bbf_get_sysadmin_oid_hook = prev_bbf_get_sysadmin_oid_hook; transform_pivot_clause_hook = pre_transform_pivot_clause_hook; optimize_explicit_cast_hook = prev_optimize_explicit_cast_hook; + called_from_tsql_insert_exec_hook = pre_called_from_tsql_insert_exec_hook; } /***************************************** diff --git a/contrib/babelfishpg_tsql/src/pl_exec-2.c b/contrib/babelfishpg_tsql/src/pl_exec-2.c index d9f0f97fe3..7167e5dd1e 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec-2.c +++ b/contrib/babelfishpg_tsql/src/pl_exec-2.c @@ -26,17 +26,6 @@ PLtsql_execstate *get_current_tsql_estate(void); PLtsql_execstate *get_outermost_tsql_estate(int *nestlevel); -typedef struct TupleCastMap -{ - TupleDesc indesc; /* tupdesc for source rowtype */ - TupleDesc outdesc; /* tupdesc for result rowtype */ - AttrMap *attrMap; /* indexes of input fields, or 0 for null */ - Datum *invalues; /* workspace for deconstructing source */ - bool *inisnull; - Datum *outvalues; /* workspace for constructing result */ - bool *outisnull; -} TupleCastMap; - /* * NOTE: * A SET...(SELECT) statement that returns more than one row will raise an error @@ -77,6 +66,7 @@ static bool is_char_identstart(char c); static bool is_char_identpart(char c); void read_param_def(InlineCodeBlockArgs *args, const char *paramdefstr); +bool called_from_tsql_insert_exec(void); void cache_inline_args(PLtsql_function *func, InlineCodeBlockArgs *args); InlineCodeBlockArgs *create_args(int numargs); InlineCodeBlockArgs *clone_inline_args(InlineCodeBlockArgs *args); @@ -114,6 +104,7 @@ extern SPIPlanPtr prepare_stmt_exec(PLtsql_execstate *estate, PLtsql_function *f extern int sp_prepare_count; BulkCopyStmt *cstmt = NULL; +bool called_from_tsql_insert_execute = false; int insert_bulk_rows_per_batch = DEFAULT_INSERT_BULK_ROWS_PER_BATCH; int insert_bulk_kilobytes_per_batch = DEFAULT_INSERT_BULK_PACKET_SIZE; @@ -125,16 +116,8 @@ static bool prev_insert_bulk_keep_nulls = false; /* return a underlying node if n is implicit casting and underlying node is a certain type of node */ static Node *get_underlying_node_from_implicit_casting(Node *n, NodeTag underlying_nodetype); - -static TupleCastMap *cast_tuples_by_position(TupleDesc indesc, - TupleDesc outdesc, - const char *msg); -static AttrMap* build_typecast_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); -static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleCastMap *tupleCastMap); -static bool -check_attrmap_match(TupleDesc indesc, - TupleDesc outdesc, - AttrMap *attrMap); +static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleConversionMap *tupleCastMap); + /* * The pltsql_proc_return_code global variable is used to record the * return code (RETURN 41 + 1) of the most recently completed procedure @@ -3006,6 +2989,13 @@ exec_stmt_grantdb(PLtsql_execstate *estate, PLtsql_stmt_grantdb *stmt) return PLTSQL_RC_OK; } +bool called_from_tsql_insert_exec() +{ + if (sql_dialect != SQL_DIALECT_TSQL) + return false; + return called_from_tsql_insert_execute; +} + /* * For naked SELECT stmt in INSERT ... EXECUTE, instead of pushing the result to * the client, we accumulate the result in estate->tuple_store (similar to @@ -3017,7 +3007,7 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) { Portal portal; uint64 processed = 0; - TupleCastMap *tupmap; + TupleConversionMap *tupmap; MemoryContext oldcontext; if (estate->tuple_store == NULL) @@ -3029,10 +3019,11 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) /* Use eval_mcontext for tuple conversion work */ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate)); - tupmap = cast_tuples_by_position(portal->tupDesc, + called_from_tsql_insert_execute = true; + tupmap = convert_tuples_by_position(portal->tupDesc, estate->tuple_store_desc, gettext_noop("structure of query does not match function result type")); - + called_from_tsql_insert_execute = false; while (true) { uint64 i; @@ -3069,165 +3060,7 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) return PLTSQL_RC_OK; } -static AttrMap* build_typecast_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg) -{ - AttrMap *attrMap; - int nincols; - int noutcols; - int n; - int i; - int j; - bool same; - - /* - * The length is computed as the number of attributes of the expected - * rowtype as it includes dropped attributes in its count. - */ - n = outdesc->natts; - attrMap = make_attrmap(n); - - j = 0; /* j is next physical input attribute */ - nincols = noutcols = 0; /* these count non-dropped attributes */ - same = true; - for (i = 0; i < n; i++) - { - Form_pg_attribute outatt = TupleDescAttr(outdesc, i); - - if (outatt->attisdropped) - continue; /* attrMap->attnums[i] is already 0 */ - noutcols++; - for (; j < indesc->natts; j++) - { - Form_pg_attribute inatt = TupleDescAttr(outdesc, j); - if (inatt->attisdropped) - continue; - nincols++; - - attrMap->attnums[i] = (AttrNumber) (j + 1); - j++; - break; - } - if (attrMap->attnums[i] == 0) - same = false; /* we'll complain below */ - } - - /* Check for unused input columns */ - for (; j < indesc->natts; j++) - { - if (TupleDescAttr(indesc, j)->attisdropped) - continue; - nincols++; - same = false; /* we'll complain below */ - } - - /* Report column count mismatch using the non-dropped-column counts */ - if (!same) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg_internal("%s", _(msg)), - errdetail("Number of returned columns (%d) does not match " - "expected column count (%d).", - nincols, noutcols))); - - /* Check if the map has a one-to-one match */ - if (check_attrmap_match(indesc, outdesc, attrMap)) - { - /* Runtime conversion is not needed */ - free_attrmap(attrMap); - return NULL; - } - - return attrMap; -} - -/* - * check_attrmap_match - * - * Check to see if the map is a one-to-one match, in which case we need - * not to do a tuple conversion, and the attribute map is not necessary. - */ -static bool -check_attrmap_match(TupleDesc indesc, - TupleDesc outdesc, - AttrMap *attrMap) -{ - int i; - - /* no match if attribute numbers are not the same */ - if (indesc->natts != outdesc->natts) - return false; - - for (i = 0; i < attrMap->maplen; i++) - { - Form_pg_attribute inatt = TupleDescAttr(indesc, i); - Form_pg_attribute outatt = TupleDescAttr(outdesc, i); - - /* - * If the input column has a missing attribute, we need a conversion. - */ - if (inatt->atthasmissing) - return false; - - if (inatt->atttypid != outatt->atttypid || - inatt->atttypmod != outatt->atttypmod) - return false; - - if (attrMap->attnums[i] == (i + 1)) - continue; - - /* - * If it's a dropped column and the corresponding input column is also - * dropped, we don't need a conversion. However, attlen and attalign - * must agree. - */ - if (attrMap->attnums[i] == 0 && - inatt->attisdropped && - inatt->attlen == outatt->attlen && - inatt->attalign == outatt->attalign) - continue; - - return false; - } - - return true; -} - -static TupleCastMap *cast_tuples_by_position(TupleDesc indesc, - TupleDesc outdesc, - const char *msg) -{ - TupleCastMap *map; - int n; - AttrMap *attrMap; - - /* Verify compatibility and prepare attribute-number map */ - attrMap = build_typecast_attrmap_by_position(indesc, outdesc, msg); - - if (attrMap == NULL) - { - /* runtime cast is not needed */ - return NULL; - } - - /* Prepare the map structure */ - map = (TupleCastMap *) palloc(sizeof(TupleCastMap)); - map->indesc = indesc; - map->outdesc = outdesc; - map->attrMap = attrMap; - /* preallocate workspace for Datum arrays */ - n = outdesc->natts + 1; /* +1 for NULL */ - map->outvalues = (Datum *) palloc(n * sizeof(Datum)); - map->outisnull = (bool *) palloc(n * sizeof(bool)); - n = indesc->natts + 1; /* +1 for NULL */ - map->invalues = (Datum *) palloc(n * sizeof(Datum)); - map->inisnull = (bool *) palloc(n * sizeof(bool)); - map->invalues[0] = (Datum) 0; /* set up the NULL entry */ - map->inisnull[0] = true; - - return map; -} - -static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleCastMap *tupleCastMap) +static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleConversionMap *tupleCastMap) { AttrMap *attrMap = tupleCastMap->attrMap; Oid intypeid; From 399770780ae3f0214c21c6ba91f668cbf1bdc523 Mon Sep 17 00:00:00 2001 From: Zhibai Song Date: Tue, 14 Nov 2023 23:33:41 +0000 Subject: [PATCH 3/4] change test cases --- test/JDBC/expected/BABEL-2999-vu-verify.out | 19 +++++++++++++++---- test/JDBC/input/BABEL-2999-vu-verify.sql | 7 +++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/test/JDBC/expected/BABEL-2999-vu-verify.out b/test/JDBC/expected/BABEL-2999-vu-verify.out index 1fa89ffdd3..8d6ceb3d3e 100644 --- a/test/JDBC/expected/BABEL-2999-vu-verify.out +++ b/test/JDBC/expected/BABEL-2999-vu-verify.out @@ -141,18 +141,18 @@ int#!#datetime#!#varchar ~~END~~ -insert into t3_BABEL_2999_2 exec('select ''123'''); +insert into t3_BABEL2999_2 exec('select ''123'''); GO ~~ERROR (Code: 33557097)~~ -~~ERROR (Message: relation "t3_babel_2999_2" does not exist)~~ +~~ERROR (Message: structure of query does not match function result type)~~ -insert into t3_BABEL_2999_2 exec('select 123, 123'); +insert into t3_BABEL2999_2 exec('select 123, 123'); GO ~~ERROR (Code: 33557097)~~ -~~ERROR (Message: relation "t3_babel_2999_2" does not exist)~~ +~~ERROR (Message: structure of query does not match function result type)~~ insert into t4_BABEL2999 exec('select 123, 123, 123, 123, 123') @@ -214,3 +214,14 @@ insert into t6_BABEL2999 exec('select c,b,a from t6_BABEL2999') GO ~~ROW COUNT: 2~~ + +select * from t6_BABEL2999 +GO +~~START~~ +int#!#tinyint#!#smallint +1#!#2#!#3 +1#!#2#!#3 +3#!#2#!#1 +3#!#2#!#1 +~~END~~ + diff --git a/test/JDBC/input/BABEL-2999-vu-verify.sql b/test/JDBC/input/BABEL-2999-vu-verify.sql index 1e4aa2d5ab..3984efd33c 100644 --- a/test/JDBC/input/BABEL-2999-vu-verify.sql +++ b/test/JDBC/input/BABEL-2999-vu-verify.sql @@ -67,10 +67,10 @@ GO select * from t3_BABEL2999_2; GO -insert into t3_BABEL_2999_2 exec('select ''123'''); +insert into t3_BABEL2999_2 exec('select ''123'''); GO -insert into t3_BABEL_2999_2 exec('select 123, 123'); +insert into t3_BABEL2999_2 exec('select 123, 123'); GO insert into t4_BABEL2999 exec('select 123, 123, 123, 123, 123') @@ -102,3 +102,6 @@ GO insert into t6_BABEL2999 exec('select c,b,a from t6_BABEL2999') GO + +select * from t6_BABEL2999 +GO From c0c42f19e66746534f1edcdbb1a4caabc7b81df0 Mon Sep 17 00:00:00 2001 From: Zhibai Song Date: Wed, 15 Nov 2023 07:57:43 +0000 Subject: [PATCH 4/4] add cast value hook --- contrib/babelfishpg_tsql/src/hooks.c | 7 ++++ contrib/babelfishpg_tsql/src/pl_exec-2.c | 53 +++--------------------- contrib/babelfishpg_tsql/src/pl_exec.c | 13 ++++++ 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/contrib/babelfishpg_tsql/src/hooks.c b/contrib/babelfishpg_tsql/src/hooks.c index b36ad4a19c..b0f955b5d5 100644 --- a/contrib/babelfishpg_tsql/src/hooks.c +++ b/contrib/babelfishpg_tsql/src/hooks.c @@ -165,6 +165,9 @@ static bool bbf_check_rowcount_hook(int es_processed); static char *get_local_schema_for_bbf_functions(Oid proc_nsp_oid); extern bool called_from_tsql_insert_exec(); +extern Datum pltsql_exec_tsql_cast_value(Datum value, bool *isnull, + Oid valtype, int32 valtypmod, + Oid reqtype, int32 reqtypmod); /***************************************** * Replication Hooks @@ -233,6 +236,7 @@ static bbf_get_sysadmin_oid_hook_type prev_bbf_get_sysadmin_oid_hook = NULL; /* TODO: do we need to use variable to store hook value before transfrom pivot? No other function uses the same hook, should be redundant */ static transform_pivot_clause_hook_type pre_transform_pivot_clause_hook = NULL; static called_from_tsql_insert_exec_hook_type pre_called_from_tsql_insert_exec_hook = NULL; +static exec_tsql_cast_value_hook_type pre_exec_tsql_cast_value_hook = NULL; /***************************************** * Install / Uninstall @@ -401,6 +405,9 @@ InstallExtendedHooks(void) pre_called_from_tsql_insert_exec_hook = called_from_tsql_insert_exec_hook; called_from_tsql_insert_exec_hook = called_from_tsql_insert_exec; + + pre_exec_tsql_cast_value_hook = exec_tsql_cast_value_hook; + exec_tsql_cast_value_hook = pltsql_exec_tsql_cast_value; } void diff --git a/contrib/babelfishpg_tsql/src/pl_exec-2.c b/contrib/babelfishpg_tsql/src/pl_exec-2.c index 7167e5dd1e..341901bc1d 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec-2.c +++ b/contrib/babelfishpg_tsql/src/pl_exec-2.c @@ -116,7 +116,6 @@ static bool prev_insert_bulk_keep_nulls = false; /* return a underlying node if n is implicit casting and underlying node is a certain type of node */ static Node *get_underlying_node_from_implicit_casting(Node *n, NodeTag underlying_nodetype); -static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleConversionMap *tupleCastMap); /* * The pltsql_proc_return_code global variable is used to record the @@ -3041,7 +3040,11 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) HeapTuple tuple = SPI_tuptable->vals[i]; if (tupmap) - tuple = exec_cast_tuple(tuple, tupmap); + { + called_from_tsql_insert_execute = true; + tuple = execute_attr_map_tuple(tuple, tupmap); + called_from_tsql_insert_execute = false; + } tuplestore_puttuple(estate->tuple_store, tuple); if (tupmap) heap_freetuple(tuple); @@ -3060,52 +3063,6 @@ exec_stmt_insert_execute_select(PLtsql_execstate *estate, PLtsql_expr *query) return PLTSQL_RC_OK; } -static HeapTuple exec_cast_tuple(HeapTuple tuple, TupleConversionMap *tupleCastMap) -{ - AttrMap *attrMap = tupleCastMap->attrMap; - Oid intypeid; - Oid outtypeid; - int inttypemod; - int outtypemod; - Datum *invalues = tupleCastMap->invalues; - bool *inisnull = tupleCastMap->inisnull; - Datum *outvalues = tupleCastMap->outvalues; - bool *outisnull = tupleCastMap->outisnull; - int i; - - /* - * Extract all the values of the old tuple, offsetting the arrays so that - * invalues[0] is left NULL and invalues[1] is the first source attribute; - * this exactly matches the numbering convention in attrMap. - */ - heap_deform_tuple(tuple, tupleCastMap->indesc, invalues + 1, inisnull + 1); - - /* - * Transpose into proper fields of the new tuple. - */ - Assert(attrMap->maplen == tupleCastMap->outdesc->natts); - for (i = 0; i < attrMap->maplen; i++) - { - int j = attrMap->attnums[i]; - Form_pg_attribute outatt = TupleDescAttr(tupleCastMap->outdesc, i); - Form_pg_attribute inatt = TupleDescAttr(tupleCastMap->indesc, i); - - outtypeid = outatt->atttypid; - outtypemod = outatt->atttypmod; - intypeid = inatt->atttypid; - inttypemod = inatt->atttypmod; - - outisnull[i] = inisnull[j]; - outvalues[i] = exec_cast_value(get_current_tsql_estate(), - invalues[j], &outisnull[i], - intypeid, inttypemod, - outtypeid, outtypemod - ); - } - // now form the new tuple - return heap_form_tuple(tupleCastMap->outdesc, outvalues, outisnull); -} - int exec_stmt_insert_bulk(PLtsql_execstate *estate, PLtsql_stmt_insert_bulk *stmt) { diff --git a/contrib/babelfishpg_tsql/src/pl_exec.c b/contrib/babelfishpg_tsql/src/pl_exec.c index 95551b94e2..71f1deb51e 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec.c +++ b/contrib/babelfishpg_tsql/src/pl_exec.c @@ -429,6 +429,9 @@ static Datum exec_cast_value(PLtsql_execstate *estate, Datum value, bool *isnull, Oid valtype, int32 valtypmod, Oid reqtype, int32 reqtypmod); +Datum pltsql_exec_tsql_cast_value(Datum value, bool *isnull, + Oid valtype, int32 valtypmod, + Oid reqtype, int32 reqtypmod); static pltsql_CastHashEntry *get_cast_hashentry(PLtsql_execstate *estate, Oid srctype, int32 srctypmod, Oid dsttype, int32 dsttypmod); @@ -10384,3 +10387,13 @@ get_original_query_string(void) { return original_query_string; } + +Datum pltsql_exec_tsql_cast_value(Datum value, bool *isnull, + Oid valtype, int32 valtypmod, + Oid reqtype, int32 reqtypmod) +{ + return exec_cast_value(get_current_tsql_estate(), + value, isnull, + valtype, valtypmod, + reqtype, reqtypmod); +}