Skip to content

Commit 84d3a71

Browse files
Addressed PR comments and fixed regression test failures
Task: BABEL-6037 Signed-off-by: Manisha Deshpande <mmdeshp@amazon.com>
1 parent dd6e334 commit 84d3a71

File tree

12 files changed

+382
-61
lines changed

12 files changed

+382
-61
lines changed

contrib/babelfishpg_tsql/sql/ownership.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ CREATE TABLE sys.babelfish_function_ext (
4141
create_date SYS.DATETIME NOT NULL,
4242
modify_date SYS.DATETIME NOT NULL,
4343
definition sys.NTEXT DEFAULT NULL,
44-
antlr_parse_tree_text TEXT DEFAULT NULL, -- Native PG nodeToString() serialized parse tree [TODO: convert to BINARY TEXT]
45-
antlr_parse_tree_datums TEXT DEFAULT NULL, -- Native PG nodeToString() serialized datums array [TODO: convert to BINARY TEXT]
44+
antlr_parse_tree_text TEXT DEFAULT NULL, -- Native PG nodeToString() serialized parse tree
45+
antlr_parse_tree_datums TEXT DEFAULT NULL, -- Native PG nodeToString() serialized datums array
4646
antlr_parse_tree_modify_date SYS.DATETIME DEFAULT NULL,
4747
antlr_parse_tree_bbf_version TEXT DEFAULT NULL,
4848
PRIMARY KEY(funcname, nspname, funcsignature)

contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--5.5.0--5.6.0.sql

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -232,28 +232,11 @@ $$
232232
$$;
233233

234234
-- BABELFISH_FUNCTION_EXT
235-
DROP TABLE IF EXISTS sys.babelfish_function_ext;
236-
237-
CREATE TABLE sys.babelfish_function_ext (
238-
nspname NAME NOT NULL,
239-
funcname NAME NOT NULL,
240-
orig_name sys.NVARCHAR(128), -- original input name of users
241-
funcsignature TEXT NOT NULL COLLATE "C",
242-
default_positions TEXT COLLATE "C",
243-
flag_validity BIGINT,
244-
flag_values BIGINT,
245-
create_date SYS.DATETIME NOT NULL,
246-
modify_date SYS.DATETIME NOT NULL,
247-
definition sys.NTEXT DEFAULT NULL,
248-
antlr_parse_tree_text TEXT DEFAULT NULL, -- Native PG nodeToString() serialized parse tree
249-
antlr_parse_tree_datums TEXT DEFAULT NULL, -- Native PG nodeToString() serialized datums array
250-
antlr_parse_tree_modify_date SYS.DATETIME DEFAULT NULL,
251-
antlr_parse_tree_bbf_version TEXT DEFAULT NULL,
252-
PRIMARY KEY(funcname, nspname, funcsignature)
253-
);
254-
GRANT SELECT ON sys.babelfish_function_ext TO PUBLIC;
235+
ALTER TABLE sys.babelfish_function_ext ADD COLUMN IF NOT EXISTS antlr_parse_tree_text TEXT DEFAULT NULL;
236+
ALTER TABLE sys.babelfish_function_ext ADD COLUMN IF NOT EXISTS antlr_parse_tree_datums TEXT DEFAULT NULL;
237+
ALTER TABLE sys.babelfish_function_ext ADD COLUMN IF NOT EXISTS antlr_parse_tree_modify_date SYS.DATETIME DEFAULT NULL;
238+
ALTER TABLE sys.babelfish_function_ext ADD COLUMN IF NOT EXISTS antlr_parse_tree_bbf_version TEXT DEFAULT NULL;
255239

256-
SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_function_ext', '');
257240
-- Please add your SQLs here
258241

259242
-- Deprecate and drop old aggregates first (they depend on the function)

contrib/babelfishpg_tsql/src/guc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ char *pltsql_host_service_pack_level = NULL;
7272

7373
bool pltsql_enable_create_alter_view_from_pg = false;
7474
bool pltsql_enable_alter_owner_from_pg = false;
75-
bool pltsql_enable_procedure_parse_cache = false;
75+
bool pltsql_enable_routine_parse_cache = false;
7676

7777
static const struct config_enum_entry explain_format_options[] = {
7878
{"text", EXPLAIN_FORMAT_TEXT, false},
@@ -1144,10 +1144,10 @@ define_custom_variables(void)
11441144
/*
11451145
* Enable/disable procedure ANTLR parse result caching for cross-session (sys.babelfish_function_ext->antlr_parse_tree) performance optimization.
11461146
*/
1147-
DefineCustomBoolVariable("babelfishpg_tsql.enable_procedure_parse_cache",
1147+
DefineCustomBoolVariable("babelfishpg_tsql.enable_routine_parse_cache",
11481148
gettext_noop("Enables caching of ANTLR parse results for stored procedures across sessions"),
11491149
NULL,
1150-
&pltsql_enable_procedure_parse_cache,
1150+
&pltsql_enable_routine_parse_cache,
11511151
false,
11521152
PGC_USERSET,
11531153
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_DISALLOW_IN_AUTO_FILE,

contrib/babelfishpg_tsql/src/guc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ typedef enum IsolationOptions
1818
extern bool pltsql_fmtonly;
1919
extern bool pltsql_enable_create_alter_view_from_pg;
2020
extern bool pltsql_enable_alter_owner_from_pg;
21-
extern bool pltsql_enable_procedure_parse_cache;
21+
extern bool pltsql_enable_routine_parse_cache;
2222
extern bool pltsql_enable_linked_servers;
2323
extern bool pltsql_enable_ownership_chaining;
2424
extern bool pltsql_allow_windows_login;

contrib/babelfishpg_tsql/src/hooks.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4335,7 +4335,7 @@ pltsql_store_func_default_positions(ObjectAddress address, List *parameters, con
43354335
PLtsql_function *function = NULL;
43364336

43374337
/* Check if procedure parse caching is enabled via GUC */
4338-
if (!pltsql_enable_procedure_parse_cache)
4338+
if (!pltsql_enable_routine_parse_cache)
43394339
{
43404340
/* GUC disabled - skip cache storage, set columns to NULL */
43414341
elog(DEBUG1, "Parse result caching skipped for function %u: GUC disabled",
@@ -4566,11 +4566,12 @@ pltsql_update_func_cache_entry(HeapTuple proctup, PLtsql_function *function)
45664566
bool new_record_nulls[BBF_FUNCTION_EXT_NUM_COLS];
45674567
bool new_record_replaces[BBF_FUNCTION_EXT_NUM_COLS];
45684568
Datum modify_date;
4569+
bool snapshot_pushed = false;
45694570

45704571
if (babelfish_dump_restore)
45714572
return;
45724573

4573-
if (!pltsql_enable_procedure_parse_cache)
4574+
if (!pltsql_enable_routine_parse_cache)
45744575
return;
45754576

45764577
if (!OidIsValid(get_bbf_function_ext_idx_oid()))
@@ -4580,6 +4581,17 @@ pltsql_update_func_cache_entry(HeapTuple proctup, PLtsql_function *function)
45804581
if (!HeapTupleIsValid(oldtup))
45814582
return;
45824583

4584+
/*
4585+
* CatalogTupleUpdate requires an active snapshot. During after-trigger
4586+
* execution the snapshot may already have been popped, so push one if
4587+
* needed. This mirrors the pattern at pltsql_store_func_default_positions.
4588+
*/
4589+
if (!ActiveSnapshotSet())
4590+
{
4591+
PushActiveSnapshot(GetTransactionSnapshot());
4592+
snapshot_pushed = true;
4593+
}
4594+
45834595
rel = table_open(get_bbf_function_ext_oid(), RowExclusiveLock);
45844596
dsc = RelationGetDescr(rel);
45854597

@@ -4604,6 +4616,9 @@ pltsql_update_func_cache_entry(HeapTuple proctup, PLtsql_function *function)
46044616
heap_freetuple(oldtup);
46054617
heap_freetuple(newtup);
46064618
table_close(rel, RowExclusiveLock);
4619+
4620+
if (snapshot_pushed)
4621+
PopActiveSnapshot();
46074622
}
46084623

46094624
/*
@@ -4615,7 +4630,7 @@ pltsql_update_func_cache_entry(HeapTuple proctup, PLtsql_function *function)
46154630
* expensive ANTLR parsing entirely for first time execution of routines in new sessions.
46164631
*
46174632
* Validation checks (all must pass for a cache hit):
4618-
* 1. GUC babelfishpg_tsql.enable_procedure_parse_cache is enabled
4633+
* 1. GUC babelfishpg_tsql.enable_routine_parse_cache is enabled
46194634
* 2. bbf_version matches current BABELFISH_VERSION_STR (detects MVU)
46204635
* 3. antlr_parse_tree_modify_date >= modify_date (detects guc-disabled ALTER after caching)
46214636
* 4. antlr_parse_tree_text is not NULL and deserializes successfully
@@ -4641,9 +4656,9 @@ pltsql_restore_func_parse_result(HeapTuple proctup)
46414656
return NULL;
46424657

46434658
/* Check if procedure parse caching is enabled via GUC */
4644-
if (!pltsql_enable_procedure_parse_cache)
4659+
if (!pltsql_enable_routine_parse_cache)
46454660
{
4646-
elog(DEBUG1, "Parse result cache retrieval skipped: GUC babelfishpg_tsql.enable_procedure_parse_cache is disabled");
4661+
elog(DEBUG1, "Parse result cache retrieval skipped: GUC babelfishpg_tsql.enable_routine_parse_cache is disabled");
46474662
return NULL;
46484663
}
46494664

contrib/babelfishpg_tsql/src/pl_comp.c

Lines changed: 110 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,8 @@ do_compile(FunctionCallInfo fcinfo,
930930

931931
if (cached_result)
932932
{
933+
int ci;
934+
933935
elog(LOG, "do_compile: Using cached parse result (ndatums=%d), skipping ANTLR parsing", cached_result->ndatums);
934936
pltsql_parse_result = cached_result->parse_tree;
935937

@@ -939,12 +941,110 @@ do_compile(FunctionCallInfo fcinfo,
939941

940942
/* Mark that this function was loaded from persistent cache */
941943
function->from_cache = true;
942-
944+
945+
/*
946+
* Re-derive found_varno and fetch_status_varno from cached
947+
* datums by scanning refnames.
948+
*
949+
* The cache was serialized from the validator-compiled function
950+
* which may have a different datum layout than what do_compile
951+
* built above (e.g., for ITVFs the validator serializes BEFORE
952+
* pg_proc is updated with TABLE-mode columns, so the cache has
953+
* fewer datums than the runtime parameter loop creates).
954+
* Scanning by refname makes us layout-independent.
955+
*
956+
* in_arg_varnos is NOT re-derived here — the parameter loop
957+
* above already set it correctly from pg_proc metadata, and
958+
* scanning '@'-prefixed refnames would also match local
959+
* variables, overflowing the pronargs-sized buffer.
960+
*/
961+
function->found_varno = -1;
962+
function->fetch_status_varno = -1;
963+
964+
for (ci = 0; ci < cached_result->ndatums; ci++)
965+
{
966+
PLtsql_datum *d = cached_result->datums[ci];
967+
968+
if (d->dtype == PLTSQL_DTYPE_VAR)
969+
{
970+
PLtsql_var *v = (PLtsql_var *) d;
971+
972+
if (v->refname && strcmp(v->refname, "found") == 0)
973+
function->found_varno = d->dno;
974+
else if (v->refname && strcmp(v->refname, "@@fetch_status") == 0)
975+
function->fetch_status_varno = d->dno;
976+
}
977+
}
978+
979+
/*
980+
* Do NOT re-derive in_arg_varnos from cached datums.
981+
* The parameter loop above already populated in_arg_varnos
982+
* correctly from pg_proc metadata. Re-scanning cached datums
983+
* for '@'-prefixed refnames would also match local variables
984+
* (e.g. @sql, @precision), overflowing the in_arg_varnos
985+
* buffer which is sized for pronargs only.
986+
*/
987+
988+
Assert(function->found_varno >= 0);
989+
Assert(function->fetch_status_varno >= 0);
990+
991+
/*
992+
* Re-derive out_param_varno from cached datums.
993+
*
994+
* The validator already built the OUT row (PLtsql_row) and it was
995+
* serialized into the cache. The cached parse tree's RETURN nodes
996+
* reference it by dno. We just need to find it and set
997+
* function->out_param_varno so the runtime can use it.
998+
*
999+
* For multiple OUT args (or procedure with any OUT): find the
1000+
* PLtsql_row datum in the cache, then rebuild its rowtupdesc
1001+
* (which is not serialized — marked read_write_ignore).
1002+
* For single OUT arg (non-procedure function): point directly
1003+
* to the matching cached datum using the dno from the parameter
1004+
* loop (which assigned the same sequential dnos as the validator).
1005+
*/
1006+
if (num_out_args > 1 ||
1007+
(num_out_args == 1 && function->fn_prokind == PROKIND_PROCEDURE))
1008+
{
1009+
for (ci = 0; ci < cached_result->ndatums; ci++)
1010+
{
1011+
if (cached_result->datums[ci]->dtype == PLTSQL_DTYPE_ROW)
1012+
{
1013+
PLtsql_row *row = (PLtsql_row *) cached_result->datums[ci];
1014+
int fi;
1015+
1016+
function->out_param_varno = row->dno;
1017+
1018+
/*
1019+
* Rebuild rowtupdesc — it is NULL after deserialization
1020+
* (TupleDesc is not serializable). Walk the ROW's varnos
1021+
* to find each member VAR's type info, same as
1022+
* build_row_from_vars does.
1023+
*/
1024+
row->rowtupdesc = CreateTemplateTupleDesc(row->nfields);
1025+
for (fi = 0; fi < row->nfields; fi++)
1026+
{
1027+
PLtsql_var *fvar = (PLtsql_var *) cached_result->datums[row->varnos[fi]];
1028+
1029+
TupleDescInitEntry(row->rowtupdesc, fi + 1,
1030+
row->fieldnames[fi],
1031+
fvar->datatype->typoid,
1032+
fvar->datatype->atttypmod,
1033+
0);
1034+
TupleDescInitEntryCollation(row->rowtupdesc, fi + 1,
1035+
fvar->datatype->collation);
1036+
}
1037+
break;
1038+
}
1039+
}
1040+
}
1041+
else if (num_out_args == 1)
1042+
function->out_param_varno = out_arg_variables[0]->dno;
1043+
9431044
/* Free the wrapper structure (but not the data we transferred) */
9441045
pfree(cached_result);
9451046

9461047
parse_rc = 0;
947-
// [TODO}: uncomment to execute deserialized parse result
9481048
goto skip_antlr_parsing;
9491049
}
9501050
}
@@ -976,8 +1076,9 @@ do_compile(FunctionCallInfo fcinfo,
9761076
/*
9771077
* Multi-Statement Table-Valued Function: 1) Add a declare table statement
9781078
* to the beginning 2) Add a return table statement to the end
1079+
* Skip when restoring from cache — the cached parse tree already contains these.
9791080
*/
980-
if (function->is_mstvf)
1081+
if (function->is_mstvf && !function->from_cache)
9811082
{
9821083
/*
9831084
* ANTLR parser would return a stmt list like INIT->BLOCK, where BLOCK
@@ -994,9 +1095,11 @@ do_compile(FunctionCallInfo fcinfo,
9941095
* control to fall off the end without an explicit RETURN statement. The
9951096
* easiest way to implement this is to add a RETURN statement to the end
9961097
* of the statement list during parsing.
1098+
* Skip when restoring from cache — the cached parse tree already has the dummy return.
9971099
*/
998-
if (num_out_args > 0 || function->fn_rettype == VOIDOID ||
999-
function->fn_retset)
1100+
if (!function->from_cache &&
1101+
(num_out_args > 0 || function->fn_rettype == VOIDOID ||
1102+
function->fn_retset))
10001103
add_dummy_return(function);
10011104

10021105
/*
@@ -1031,7 +1134,7 @@ do_compile(FunctionCallInfo fcinfo,
10311134
* handles cache writes for DDL. Calling both in the same transaction causes
10321135
* "tuple already updated by self" errors.
10331136
*/
1034-
if (!forValidator && pltsql_enable_procedure_parse_cache && !function->from_cache)
1137+
if (!forValidator && pltsql_enable_routine_parse_cache && !function->from_cache)
10351138
pltsql_update_func_cache_entry(procTup, function);
10361139

10371140
/* Debug dump for completed functions */
@@ -3220,7 +3323,7 @@ pltsql_HashTableLookup(PLtsql_func_hashkey *func_key)
32203323
if (hentry)
32213324
{
32223325
/* If GUC is disabled and function was loaded from cache, return NULL to force re-parse */
3223-
if (!pltsql_enable_procedure_parse_cache && hentry->function->from_cache)
3326+
if (!pltsql_enable_routine_parse_cache && hentry->function->from_cache)
32243327
{
32253328
elog(DEBUG1, "pltsql_HashTableLookup: GUC disabled, invalidating cached function %u", (unsigned int) func_key->funcOid);
32263329
return NULL;

test/JDBC/expected/BABEL-6037-vu-cleanup.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,17 @@ GO
5353
DROP TABLE IF EXISTS dbo.dep_test_table;
5454
GO
5555

56+
-- Drop Test 11: Procedure with single OUT parameter
57+
DROP PROCEDURE IF EXISTS dbo.babel_6037_out_single;
58+
GO
59+
60+
-- Drop Test 12: Procedure with multiple OUT parameters
61+
DROP PROCEDURE IF EXISTS dbo.babel_6037_out_multi;
62+
GO
63+
64+
-- Drop Test 13: MSTVF
65+
DROP FUNCTION IF EXISTS dbo.babel_6037_mstvf;
66+
GO
67+
5668
PRINT 'Cleanup completed successfully';
5769
GO

test/JDBC/expected/BABEL-6037-vu-prepare.out

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
-- BABEL-6037 POC Test - Prepare
33
-- Creates test procedures for parse tree serialization/deserialization testing
4-
SELECT set_config('babelfishpg_tsql.enable_procedure_parse_cache', 'on', false);
4+
SELECT set_config('babelfishpg_tsql.enable_routine_parse_cache', 'on', false);
55
GO
66
~~START~~
77
text
@@ -214,7 +214,48 @@ BEGIN
214214
END;
215215
GO
216216

217-
SELECT set_config('babelfishpg_tsql.enable_procedure_parse_cache', 'off', false);
217+
-- Test 11: Procedure with single OUT parameter
218+
-- Tests out_param_varno re-derivation for single OUT arg on a procedure
219+
-- (validator builds PLtsql_row, serialized into cache).
220+
CREATE PROCEDURE dbo.babel_6037_out_single
221+
@in_val INT,
222+
@out_val INT OUT
223+
AS
224+
BEGIN
225+
SET @out_val = @in_val * 10;
226+
END;
227+
GO
228+
229+
-- Test 12: Procedure with multiple OUT parameters
230+
-- For procedures with >1 OUT args, the validator builds a PLtsql_row datum
231+
-- that gets serialized into the cache. The cache-hit path must find it
232+
-- and set function->out_param_varno.
233+
CREATE PROCEDURE dbo.babel_6037_out_multi
234+
@in_val INT,
235+
@out_val INT OUT,
236+
@out_msg VARCHAR(100) OUT
237+
AS
238+
BEGIN
239+
SET @out_val = @in_val * 2;
240+
SET @out_msg = 'Result: ' + CAST(@out_val AS VARCHAR);
241+
END;
242+
GO
243+
244+
-- Test 13: Multi-statement table-valued function (MSTVF)
245+
-- MSTVFs use out_param_varno at runtime (pl_exec-2.c:1445) to build the
246+
-- result table. The cache must preserve the ROW datum so the runtime can
247+
-- find it.
248+
CREATE FUNCTION dbo.babel_6037_mstvf(@in_val INT)
249+
RETURNS @result TABLE (id INT, val VARCHAR(50))
250+
AS
251+
BEGIN
252+
INSERT INTO @result VALUES (@in_val, 'row1');
253+
INSERT INTO @result VALUES (@in_val + 1, 'row2');
254+
RETURN;
255+
END;
256+
GO
257+
258+
SELECT set_config('babelfishpg_tsql.enable_routine_parse_cache', 'off', false);
218259
GO
219260
~~START~~
220261
text

0 commit comments

Comments
 (0)