Conversation
If the declaration of the referenced item changes, then the declaration of the referencing item changes too. The referencing item inherits data type, data size, constraints (unless the referenced item is a column) of the referenced item. Function and prorcedures argument datatype can be %ROWTYPE, and funtion returned datatype can be %ROWTYPE.
WalkthroughAdds Oracle-style %TYPE/%ROWTYPE handling across parser, PL/iSQL engine, dependency tracking, catalogs, and sysviews; introduces new RAW_PARSE_PLISQL modes, DEPENDENCY_TYPE, pg_proc.prostatus, inner-subtransaction PL/iSQL validation semantics, expanded metadata functions/views, and comprehensive tests. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as DDL (ALTER/RENAME)
participant AT as tablecmds
participant Dep as dependency.c
participant Cat as pg_depend/pg_proc
participant Cache as PackageCache
User->>AT: perform DDL on relation/column
AT->>Dep: scan pg_depend, collect ObjectFunOrPkg[]
Dep->>Dep: delete type deps, sort & dedupe collected OIDs
alt function
Dep->>Cat: set pg_proc.prostatus = PROSTATUS_NA/INVALID
else package spec
Dep->>Cache: mark PACKAGE_SET_SPECIFICATION_UPDATE_FLAG
else package body
Dep->>Cache: mark PACKAGE_SET_BODY_UPDATE_FLAG
end
sequenceDiagram
autonumber
participant Dev as CREATE/ALTER FUNCTION (PL/iSQL)
participant FC as functioncmds
participant Proc as pg_proc
participant Xact as inner subtransaction
participant Val as PL/iSQL validator
Dev->>FC: create/update function
FC->>Proc: insert/update pg_proc (prostatus set per mode)
alt check_function_bodies = true
FC->>Xact: begin inner subtransaction
Xact->>Val: validate function body
alt success
Val-->>Xact: OK
Xact->>Xact: commit
Proc->>pg_proc: prostatus = PROSTATUS_VALID
else failure
Val-->>Xact: ERROR
Xact->>Xact: rollback
Proc->>pg_proc: update prostatus = PROSTATUS_INVALID
end
else skip validation
Proc->>pg_proc: prostatus = PROSTATUS_NA
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 44
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (16)
src/include/parser/parse_package.h (1)
40-48: Initialize all PkgType fields or switch to palloc0
parse_package_typecallspalloc(sizeof(PkgType))but only setspkgoid,basetypid,item,value, andbasetypmod, leavingpkgname_startlocuninitialized. Change the allocation topalloc0(sizeof(PkgType))or addpkgtype->pkgname_startloc = -1;before returning. [src/backend/parser/parse_package.c:195]
src/backend/utils/cache/packagecache.c (1)
410-451: Memory-context misuse: use free_parsestate() and avoid piecemeal pfree()Both set_pkginfo_from_funcexpr() and set_pkgtype_from_funcexpr() allocate parse structures and derived nodes. Using pfree() on ParseState and other nodes is unsafe and leaks the arena. Replace pfree(pstate) with free_parsestate(pstate), and avoid manually freeing nodes/lists allocated under that context.
Apply (illustrative):
- list_free(fn->funcname); - pfree(fn); - pfree(pstate); - pfree(funcexpr); + /* Nodes were allocated under pstate's context. Free the whole arena. */ + free_parsestate(pstate);Also applies to: 458-506
src/backend/utils/fmgr/funcapi.c (1)
2039-2071: Avoid stringToNode for type names; use typeStringToTypeName and handle unresolved types explicitly.proargtypenames elements are text; parsing with stringToNode assumes nodeToString data. Prefer the parser’s typeName parser. Also, handle the “not found” branch explicitly for clarity.
Apply:
- TypeName *tname; + TypeName *tname; PkgType *pkgtype; - tname = (TypeName *) stringToNode(argtypenames[i]); + tname = typeStringToTypeName(argtypenames[i], NULL); pkgtype = LookupPkgTypeByTypename(tname->names, false); if (pkgtype != NULL) { outargtypes[numoutargs] = pkgtype->basetypid; pfree(pkgtype); } else { - Type typtup; - - typtup = LookupOraTypeName(NULL, tname, NULL, false); + Type typtup = LookupOraTypeName(NULL, tname, NULL, false); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) { pfree(tname); elog(ERROR, "package, relation or view does not exist"); } outargtypes[numoutargs] = typeTypeId(typtup); ReleaseSysCache(typtup); } + /* else: leave original Oid as-is */ } pfree(tname);src/backend/commands/functioncmds.c (1)
1866-1891: check_function_bodies not restored on success path.You force
check_function_bodies = trueand only restore it in the PG_CATCH branch, leaking the setting for the session on success.Restore after PG_END_TRY:
- PG_TRY(); + PG_TRY(); { ... } PG_CATCH(); { check_function_bodies = saved_check_function_bodies; ... } PG_END_TRY(); + /* success path: restore */ + check_function_bodies = saved_check_function_bodies;Also applies to: 1990-1996
src/backend/parser/parse_utilcmd.c (1)
728-736: Add regression tests for SERIAL and type guards
The%ROWTYPEexclusion is correct; add tests covering:
CREATE TABLE t(a serial)still creates the sequence/defaultCREATE TABLE t(a some_tbl%TYPE)is not treated as SERIAL- Using
%ROWTYPEin a column type position errors as expectedsrc/backend/parser/parse_package.c (1)
138-155: Database.schema.package resolution guard is invertedFor database.schema.package, the length check should be “== 3”. Using “!= 3” prevents valid 3-part names from resolving.
- if (list_size != 3) + if (list_size == 3) { tmp = lappend(list_make1(lsecond(names)), lthird(names)); pkgoid = LookupPackageByNames(tmp, true); if (OidIsValid(pkgoid)) { *pkey = pkgoid; *pkgname_startloc = 3; list_free(tmp); return true; } list_free(tmp); return false; }src/backend/catalog/namespace.c (3)
1554-1589: Fix index mix-up (i vs j) and prevent out-of-bounds access on argtypenames/argnumbers.Inside the named-args reorder loop, the code uses i (outer loop var) instead of j (current arg index), corrupting newResult->args[] and indexing argtypenames/argnumbers incorrectly. Also, argnumbers[j] can exceed nelems, risking OOB reads. Patch below fixes both.
Apply this diff:
- for (int j = 0; j < pronargs; j++) + for (int j = 0; j < pronargs; j++) { newResult->args[j] = proargtypes[argnumbers[j]]; - if (argtypenames != NULL && strcmp(argtypenames[argnumbers[i]], "") != 0) + if (argtypenames != NULL && + argnumbers[j] < nelems && + argtypenames[argnumbers[j]][0] != '\0') { TypeName *tname; PkgType *pkgtype; - tname = (TypeName *) stringToNode(argtypenames[argnumbers[i]]); + tname = (TypeName *) stringToNode(argtypenames[argnumbers[j]]); pkgtype = LookupPkgTypeByTypename(tname->names, false); if (pkgtype != NULL) { - newResult->args[i] = pkgtype->basetypid; + newResult->args[j] = pkgtype->basetypid; pfree(pkgtype); } else { Type typtup; typtup = LookupOraTypeName(NULL, tname, NULL, false); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("argument type %s is invalid", TypeNameToString(tname)))); - newResult->args[i] = typeTypeId(typtup); + newResult->args[j] = typeTypeId(typtup); ReleaseSysCache(typtup); } } pfree(tname); } }
1535-1546: Plug memory leaks: free elems and the per-element C-strings from protypenames.
- deconstruct_array() allocates elems; it should be pfree’d after extracting C-strings.
- TextDatumGetCString() allocations for each argtypenames[i] must be freed before freeing the array.
Apply this diff:
deconstruct_array(DatumGetArrayTypeP(protypenames), TEXTOID, -1, false, 'i', &elems, NULL, &nelems); argtypenames = (char **) palloc(sizeof(char *) * nelems); for (i = 0; i < nelems; i++) argtypenames[i] = TextDatumGetCString(elems[i]); + pfree(elems);- if (argtypenames != NULL) - pfree(argtypenames); + if (argtypenames != NULL) + { + for (int k = 0; k < nelems; k++) + if (argtypenames[k]) + pfree(argtypenames[k]); + pfree(argtypenames); + }Also applies to: 1642-1644
1599-1636: Guard positional-args alias resolution with bounds checks to avoid OOB.Same OOB risk exists in the positional path; also prefer a cheap empty-string check.
Apply this diff:
- if (strcmp(argtypenames[j], "") != 0) + if (j < nelems && argtypenames[j][0] != '\0') { TypeName *tname; PkgType *pkgtype; tname = (TypeName *) stringToNode(argtypenames[j]); pkgtype = LookupPkgTypeByTypename(tname->names, false); if (pkgtype != NULL) { newResult->args[j] = pkgtype->basetypid; pfree(pkgtype); } else { Type typtup; typtup = LookupOraTypeName(NULL, tname, NULL, true);src/backend/utils/cache/lsyscache.c (1)
1759-1818: Refactor error reporting for missing package/type
Replace everyelog(ERROR, "package, relation or view does not exist");with
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("package, relation, or view does not exist")));to attach the proper SQLSTATE and include the Oxford comma. Apply this change in:
- src/backend/utils/cache/lsyscache.c (in get_func_real_rettype, repl_func_real_argtype, get_func_real_allargtype)
- src/pl/plisql/src/pl_comp.c
- src/backend/utils/fmgr/funcapi.c
Representative diff:
- elog(ERROR, "package, relation or view does not exist"); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("package, relation, or view does not exist")));src/backend/catalog/pg_proc.c (1)
762-799: Double GUC cleanup on error path; call AtEOXact_GUC only onceIn the PG_CATCH block you call AtEOXact_GUC(true, save_nestlevel), and you call it again unconditionally after PG_END_TRY. On the error path this executes twice at the same nest level, which can corrupt GUC nesting state.
Minimal fix: null out set_items after cleaning up in the catch so the final block is skipped on the error path.
PG_CATCH(); { if (set_items) { AtEOXact_GUC(true, save_nestlevel); + /* Prevent double cleanup after PG_END_TRY */ + set_items = NULL; }Optionally assert save_nestlevel > 0 before the final AtEOXact_GUC to document intent.
Also applies to: 800-821, 822-879, 881-883
src/backend/oracle_parser/ora_gram.y (1)
22268-22290: determineLanguage must return the updated List* and callers must capture it
determineLanguage currently takes its List* parameter by value, so any lappend result is lost when the function returns void—meaning the default “plisql” option is never applied. Change:
The prototype at line 211 in src/backend/oracle_parser/ora_gram.y from
static void determineLanguage(List *options);
to
static List *determineLanguage(List *options);The definition at line 22269 from
static void determineLanguage(List *options)
to
static List *determineLanguage(List *options)
addreturn options;before the closing}.And update every call site in that file (at lines 8753, 8774, 8826 to assign n->options, and at 10190, 10197, 10204, 10212 to assign n->args) from:
determineLanguage(n->options);to
n->options = determineLanguage(n->options);or, for DoStmt branches:
n->args = determineLanguage(n->args);src/pl/plisql/src/pl_comp.c (2)
446-459: Leaks: arg type/ret type name strings from get_func_typename_info are never freed (and later freed shallow-only)
- The array of C-strings returned for argtypenames is not freed in this first usage, and in the later post-compile pass you free only the array pointer, not each string. rettypename is also leaked in this first block.
Apply:
@@ - get_func_typename_info(procTup, - &argtypenames, &rettypename); + get_func_typename_info(procTup, &argtypenames, &rettypename); @@ for (i = 0; i < numargs; i++) { ... } + + /* Free argtypenames deep here since we're done with param dtypes */ + if (argtypenames) + { + for (i = 0; i < numargs; i++) + if (argtypenames[i] && argtypenames[i][0] != '\0') + pfree(argtypenames[i]); + pfree(argtypenames); + argtypenames = NULL; + } @@ - if (rettypename != NULL) + if (rettypename != NULL) { TypeName *tname; tname = (TypeName *) stringToNode(rettypename); ... pfree(tname); + + /* release rettypename buffer after use */ + pfree(rettypename); + rettypename = NULL; }Also applies to: 481-514, 595-633
3366-3429: Leaks in compute_function_hashkey: argtypenames and elems
- argtypenames strings and container aren’t freed.
- elems from deconstruct_array isn’t freed.
Apply:
@@ - for (i = 0; i < nelems; i++) - argtypenames[i] = TextDatumGetCString(elems[i]); + for (i = 0; i < nelems; i++) + argtypenames[i] = TextDatumGetCString(elems[i]); @@ - if (argtypenames != NULL) + if (argtypenames != NULL) { for (i = 0; i < procStruct->pronargs; i++) { ... } + /* free deep copies and elems from deconstruct_array */ + for (i = 0; i < nelems; i++) + if (argtypenames[i]) + pfree(argtypenames[i]); + pfree(argtypenames); + if (!isNull && elems) + pfree(elems); } else memcpy(hashkey->argtypes, procStruct->proargtypes.values, procStruct->pronargs * sizeof(Oid));contrib/ivorysql_ora/src/sysview/sysview--1.0.sql (1)
365-372: LINE now mismatches TEXT source (off-by-one/incorrect row mapping). Switch to WITH ORDINALITY on the same array you UNNEST.ALL_SOURCE and USER_SOURCE compute LINE from PROSRC via _ORA_SRC_COUNT but TEXT from pg_get_functiondef(). If the two differ in line-breaks, rows are wrong. Use UNNEST(... WITH ORDINALITY) to derive both LINE and TEXT from the same array.
Apply this diff in both views:
- GENERATE_SERIES(1::INTEGER, SYS._ORA_SRC_COUNT(P.OID))::NUMBER AS LINE, - UNNEST(STRING_TO_ARRAY(substring(pg_get_functiondef(P.OID), length('CREATE OR REPLACE ') + 1), CHR(10)))::VARCHAR2(4000) AS TEXT, + t.ord::NUMBER AS LINE, + t.txt::VARCHAR2(4000) AS TEXT, ... - FROM - PG_PROC AS P - LEFT JOIN PG_TRIGGER AS T - ON P.OID = T.TGFOID + FROM + PG_PROC AS P + LEFT JOIN PG_TRIGGER AS T + ON P.OID = T.TGFOID + CROSS JOIN LATERAL UNNEST( + STRING_TO_ARRAY( + substring(pg_get_functiondef(P.OID), length('CREATE OR REPLACE ') + 1), + CHR(10) + ) + ) WITH ORDINALITY AS t(txt, ord)Also applies to: 394-401
src/pl/plisql/src/pl_package.c (1)
2053-2056: Stack underflow bug: post-decrement check is wrong. Use pre-decrement (or split) before indexing.As written, level==0 will decrement to -1 and proceed.
- if (plisql_curr_global_proper_level-- < 0) + if (--plisql_curr_global_proper_level < 0) elog(ERROR, "compile proper stack wrong");
🧹 Nitpick comments (43)
src/include/parser/parse_package.h (1)
44-44: Fix comment wording and document sentinel value.“mode” → “modifier”; also clarify that -1 means “typmod not specified.”
- int32 basetypmod; /* base of type mode */ + int32 basetypmod; /* base type modifier (typmod); -1 if not specified */src/oracle_test/regress/sql/ora_package.sql (1)
2002-2006: Spec’s return type vs rowtype: confirm intent or align for clarity.Spec returns mds while arguments/locals use mds%rowtype. If not intentionally mixing forms, consider using mds%rowtype consistently to avoid confusion.
- function test(id integer, var2 mds%rowtype) return mds; + function test(id integer, var2 mds%rowtype) return mds%rowtype;And in the body:
- function test(id integer, var2 mds%rowtype) return mds is + function test(id integer, var2 mds%rowtype) return mds%rowtype issrc/pl/plisql/src/sql/plisql_nested_subproc2.sql (1)
3909-3919: Rename to test_f2 is fine; add cleanup to future-proof the test.If “pipelined” ever succeeds, the function will linger. Add a defensive drop after the block.
end; / +drop function if exists test_f2(integer); +src/backend/utils/cache/packagecache.c (1)
599-619: Fix typos in SYS view mapping comment
- “DATA_SCALERS” → “DATA_SCALES”
- Minor copyedit for consistency.
Apply:
- * data_scales is used for DATA_SCALERS. + * data_scales is used for DATA_SCALES.src/backend/utils/fmgr/funcapi.c (4)
2218-2251: Duplicate Ora-type resolution logic: factor into a small helper to reduce risk and drift.The block mirrors the earlier function; centralize into a static helper to keep behavior consistent.
Example (outside selected range):
static void resolve_outargtype_from_typename(char *typename_txt, Oid *out_oid);Then call it in both places.
2729-2735: Minor: variable name typo (prostaus_datum).Rename to prostatus_datum for clarity. No functional impact.
- Datum prostaus_datum; + Datum prostatus_datum; ... - prostaus_datum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prostatus, + prostatus_datum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prostatus, &isNull); ... - result = DatumGetChar(prostaus_datum); + result = DatumGetChar(prostatus_datum);
2759-2784: Skip unnecessary writes and document NA/INVALID behavior.
- If the current prostatus equals the requested value, return early to avoid catalog churn.
- The NA/INVALID early-return is surprising; add a comment explaining why refusing changes is desired (or relax it if not).
- ori_prostatus = get_func_prostatus(oldprocTup); - if (ori_prostatus == PROSTATUS_INVALID || - ori_prostatus == PROSTATUS_NA) + ori_prostatus = get_func_prostatus(oldprocTup); + /* Do not update if value is unchanged */ + if (ori_prostatus == prostatus) + { + ReleaseSysCache(oldprocTup); + return; + } + /* Disallow transitions from INVALID/NA (by design) */ + if (ori_prostatus == PROSTATUS_INVALID || + ori_prostatus == PROSTATUS_NA) { ReleaseSysCache(oldprocTup); return; }
2039-2071: Error message accuracy.“package, relation or view does not exist” may be misleading if a type is missing. Consider “type does not exist” or including the resolved name for better diagnostics.
Also applies to: 2218-2251
src/pl/plisql/src/sql/plisql_type_rowtype.sql (2)
2-2: Typo in header comment.“and and” duplicated.
--- -- Tests for PL/iSQL %TYPE and and %ROWTYPE Attributes +++ -- Tests for PL/iSQL %TYPE and %ROWTYPE Attributes
1218-1221: Suspicious assignment of rowtype to scalar.
r.phone_number := var1;assigns a composite to a scalar field. If the intent is to test invalidation, please add a comment and-- failedmarker; otherwise usevar1.phone_number.- r.phone_number := var1; + r.phone_number := var1.phone_number;src/include/parser/parse_target.h (1)
58-62: Make prototypes consistent (add extern)
These two new declarations are missing extern unlike others in this header. Align for consistency.-List *transformRowExpression(ParseState *pstate, List *exprlist, - ParseExprKind exprKind, bool allowDefault); -List *transformRowTargetList(ParseState *pstate, List *targetlist, - ParseExprKind exprKind); +extern List *transformRowExpression(ParseState *pstate, List *exprlist, + ParseExprKind exprKind, bool allowDefault); +extern List *transformRowTargetList(ParseState *pstate, List *targetlist, + ParseExprKind exprKind);src/pl/plisql/src/pl_package.h (1)
157-161: Provide an ABI-compatibility wrapper forplisql_package_parse
No internal callers exist beyond its own implementation; add a shim matching the original signature to preserve external ABI.src/backend/parser/gram.y (1)
919-958: Optional: fail fast if PL/iSQL tokens ever reach the core parser.If there’s any chance the core path could inject MODE_PLISQL_* by mistake, add explicit reject branches in parse_toplevel that raise a clear error (mirrors how we gate other modes). This avoids confusing error cascades.
If desired, add guarded branches like:
parse_toplevel: stmtmulti ... | MODE_PLPGSQL_ASSIGN3 PLAssignStmt { PLAssignStmt *n = (PLAssignStmt *) $2; n->nnames = 3; pg_yyget_extra(yyscanner)->parsetree = list_make1(makeRawStmt((Node *) n, @2)); } + | MODE_PLISQL_EXPR /* not supported in core parser */ + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("PL/iSQL expressions are not supported in PostgreSQL grammar"))); + } + | MODE_PLISQL_ASSIGN1 + | MODE_PLISQL_ASSIGN2 + | MODE_PLISQL_ASSIGN3 + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("PL/iSQL assignments are not supported in PostgreSQL grammar"))); + } ;src/include/parser/parse_type.h (1)
63-67: Add brief docs and keep prototype grouping consistentConsider adding short comments mirroring LookupTypeName/Extended docs and grouping these Ora variants adjacent to them for discoverability. No functional issues spotted.
src/include/catalog/pg_proc.h (1)
202-216: Fix typos in prostatus comments“validate te procedure” → “validate the procedure” (both occurrences).
- * validate te procedure, otherwise failed to + * validate the procedure, otherwise failed tosrc/backend/parser/parse_type.c (2)
30-34: Remove unused includescatalog/pg_class.h and access/table.h aren’t used in this file; consider dropping them.
-#include "catalog/pg_class.h" -#include "access/table.h"
909-1084: Avoid duplicating LookupTypeNameExtended logicLookupOraTypeName(Extended) largely duplicate LookupTypeName(Extended) minus the %TYPE NOTICE. Consider a shared static helper with a “emit_pct_notice” flag to reduce drift.
src/backend/utils/adt/ruleutils.c (4)
79-79: Consider guarding Oracle-specific includeIf this file is compiled in non-Oracle modes, unguarded inclusion of parser/parse_package.h could break those builds. If IvorySQL guarantees availability, ignore; otherwise wrap with a feature guard.
-#include "parser/parse_package.h" +#ifdef USE_ORACLE_COMPAT +#include "parser/parse_package.h" +#endif
14479-14482: Prefer SQL NULLs over the literal "NULL" placeholdersEmitting text "NULL" makes downstream consumers do string checks instead of using IS NULL. Unless views are explicitly depending on this sentinel, return proper NULLs.
- values[2] = CStringGetTextDatum("_NULL_"); - values[3] = CStringGetTextDatum("_NULL_"); - values[4] = CStringGetTextDatum("_NULL_"); + nulls[2] = true; + nulls[3] = true; + nulls[4] = true;If SYS views depend on the sentinel, keep as-is; otherwise this is a cleaner contract.
14260-14263: Optionally finalize tuplestore writingNot required, but calling tuplestore_donestoring improves clarity and mirrors surrounding code patterns.
- /* clean up and return the generated tuplestore */ - //tuplestore_donestoring(tupstore); + /* clean up and return the generated tuplestore */ + tuplestore_donestoring(tupstore);
14388-14427: Avoid shadowing ‘relid’ for readabilityThe inner declaration of relid masks the outer relid. Use a distinct name.
- Oid relid; + Oid relid_local; ... - relid_local = RangeVarGetRelid(rel, NoLock, true); - attnum = get_attnum(relid_local, field); + relid_local = RangeVarGetRelid(rel, NoLock, true); + attnum = get_attnum(relid_local, field);src/backend/parser/parse_target.c (2)
2146-2166: Add errposition and use precise message for indirection star.Missing
parser_errpositionin theA_Indirectionbranch. Also prefer consistent wording (“row expansion via "*" is not supported here”), matching existing messages.- ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unsupported syntax"))); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("row expansion via \"*\" is not supported here"), + parser_errposition(pstate, exprLocation((Node *) ind)));
2173-2179: Include errposition for DEFAULT rejection; clarify text.Current error lacks cursor position.
- if (IsA(e, SetToDefault)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unsupported syntax"))); + if (IsA(e, SetToDefault)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("DEFAULT is not allowed in row target list"), + parser_errposition(pstate, exprLocation(e))));src/backend/catalog/dependency.c (2)
2723-2735: Comparator scope and semantics.The comparator only sorts by
objectId(descending). Given OIDs are cluster-global, dedup onobjectIdviaquniqueis safe. Considerstaticlinkage if not used outside this TU (unless declared in a header intentionally).
92-95: Remove duplicate and unused includes in dependency.cDrop the second
#include "funcapi.h"(already at line 82) and remove the unused#include "commands/tablecmds.h":src/backend/catalog/dependency.c -#include "commands/tablecmds.h" -#include "funcapi.h"src/backend/catalog/pg_proc.c (4)
598-609: Unify PL/iSQL language checks; avoid syscache lookups in hot pathsYou compare the function language to get_language_oid("plisql", true) here, but elsewhere in this file you use LANG_PLISQL_OID. Prefer the constant for consistency and to avoid repeated syscache hits.
Apply this diff in both blocks:
- if (ORA_PARSER == compatible_db && - languageObjectId == get_language_oid("plisql", true)) + if (ORA_PARSER == compatible_db && + languageObjectId == LANG_PLISQL_OID)Also applies to: 643-655
685-693: Skipping return-type dependency for composite types can break invalidationYou skip TypeRelationId dependency when PL/iSQL returns a composite type in Oracle mode. Ensure an equivalent dependency is recorded via the new dependency kind so DDL on the underlying relation properly invalidates the function. Otherwise, callers could observe stale prostatus or fail at runtime after ALTER TABLE.
If this is handled elsewhere, point to the object kind used and its invalidation path. If not, consider recording a weaker dependency (e.g., DEPENDENCY_NORMAL on the type oid) behind a feature flag until the new mechanism is fully wired.
704-717: Parameter-type transform dependencies gated by composite-type skipTransforms for composite parameter types are skipped together with the type dependency. If a transform exists for a composite, this will silently bypass it. Confirm that’s intended (likely fine given composite transforms are uncommon) or add a comment explaining why transforms are intentionally suppressed here.
854-857: Use errdetail instead of errhint for the validator’s error textThe caught validator message is not a hint. Prefer errdetail("%s", edata->message) to place it correctly in the report and avoid hint misuse.
- ereport(WARNING, - (errmsg("compilation error"), - errhint("%s", edata->message), - errposition(edata->cursorpos))); + ereport(WARNING, + (errmsg("compilation error"), + errdetail("%s", edata->message), + errposition(edata->cursorpos)));src/backend/parser/analyze.c (1)
2628-2639: Use ereport() and add error code/position for ROWID restrictionelog lacks SQLSTATE and cursor position. Use ereport with a syntax or invalid-column reference code.
- if (strcmp(origTarget->name, "rowid") == 0) - elog(ERROR, "cannot specify system column name \"%s\"", origTarget->name); + if (strcmp(origTarget->name, "rowid") == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("cannot specify system column name \"%s\"", origTarget->name), + parser_errposition(pstate, origTarget->location)));src/backend/oracle_parser/ora_gram.y (1)
14679-14688: VALUES as single-row: broaden or guard the grammar.
- Using columnref prohibits “VALUES (func())” or “VALUES CURRENT_DATE” from being treated as the row form. If that’s intentional, OK. If not, consider accepting a_expr and gate in transform.
- Columnref here yields a ColumnRefOrFuncCall node in this grammar; ensure transformValuesClause expects that node shape.
Option A (broaden):
-values_clause_no_parens: - VALUES columnref +values_clause_no_parens: + VALUES a_expr { SelectStmt *n = makeNode(SelectStmt); - n->valuesLists = list_make1(list_make1($2)); + n->valuesLists = list_make1(list_make1($2)); n->valuesIsrow = true; $$ = (Node *) n; }Add a unit test covering:
- VALUES some_rowvar
- VALUES CURRENT_DATE
- VALUES (col1, col2) still parsed by values_clause
src/pl/plisql/src/pl_comp.c (4)
3390-3411: Error messaging: make LookupOraTypeName failure actionableelog(ERROR, "package, relation or view does not exist") is misleading here; the failure is about resolving a type name.
Use a typed error with the offending name:
- elog(ERROR, "package, relation or view does not exist"); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + TypeNameToString(tname))));
3795-3887: plisql_free_function: avoid ERRORs and ensure hashkey parity
- This helper should not ERROR on unsupported pseudotypes; it’s a cache eviction utility. Return quietly instead.
- Consider initializing fake_fcinfo with InitFunctionCallInfoData to avoid future API drift.
@@ - else if (proc->prorettype != RECORDOID && - proc->prorettype != VOIDOID && - !IsPolymorphicType(proc->prorettype)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("PL/iSQL functions cannot return type %s", - format_type_be(proc->prorettype)))); + else if (proc->prorettype != RECORDOID && + proc->prorettype != VOIDOID && + !IsPolymorphicType(proc->prorettype)) + PG_RETURN_VOID(); /* nothing to free for invalid signatures */If you prefer a no-op, simply return without emitting an error.
Would you like me to wire InitFunctionCallInfoData usage here?
1303-1305: Inline compile: clear list memory if ever populatedYou reset the globals, but if the list were somehow populated, we’d leak. Safe-guard with deep free.
- check_referenced_objects = false; - plisql_referenced_objects = NIL; + check_referenced_objects = false; + if (plisql_referenced_objects != NIL) + list_free_deep(plisql_referenced_objects); + plisql_referenced_objects = NIL;Also applies to: 1447-1449
547-571: Comment typos
- “allowned” -> “allowed”
- “alos” -> “also”
- “ts return type” -> “its return type”
I can batch-fix these across the file.
Also applies to: 699-707
src/backend/commands/tablecmds.c (3)
14974-14976: Remove unused variable.ObjectAddress address is never used and will trigger -Wunused-variable in many builds.
- ObjectAddress address;
15129-15209: Deduplicate invalidation logic into a helper to avoid drift.This switch is duplicated later (Lines 22687-22744). Extract to a static helper (e.g., invalidate_funpkg_array(ObjectFunOrPkg*, int)). Also fix the “funciton” typo in the nearby comment.
15446-15490: Strictness of “unexpected object depending on column”.In ORA mode, only DEPENDENCY_TYPE is expected, but consider downgrading to WARNING or skipping instead of ERROR to avoid breaking on unforeseen deps.
contrib/ivorysql_ora/src/sysview/sysview--1.0.sql (4)
464-467: Case-insensitive TYPE_OBJECT_TYPE check can be simplified and made consistent.Use UPPER(..) IN (...) instead of four equality checks.
- WHEN ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'table' OR ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'TABLE' - OR ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'view' OR ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'VIEW' + WHEN UPPER(ARG.TYPE_OBJECT_TYPE::VARCHAR2) IN ('TABLE','VIEW') THEN 'PL/SQL RECORD'
544-548: Avoid sentinel 'NULL' string checks; use NULLIF/COALESCE to express absence.This reduces string-logic and edge cases.
- CASE - WHEN ARG.TYPE_NAME != '_NULL_' - THEN SYS.ORA_CASE_TRANS(ARG.TYPE_NAME::VARCHAR2)::VARCHAR2(128) - ELSE - NULL - END::VARCHAR2(128) AS TYPE_NAME, + NULLIF(SYS.ORA_CASE_TRANS(ARG.TYPE_NAME::VARCHAR2), '_NULL_')::VARCHAR2(128) AS TYPE_NAME, - CASE - WHEN ARG.TYPE_SUBNAME != '_NULL_' - THEN SYS.ORA_CASE_TRANS(ARG.TYPE_SUBNAME::VARCHAR2)::VARCHAR2(128) - ELSE - NULL - END::VARCHAR2(128) AS TYPE_SUBNAME, + NULLIF(SYS.ORA_CASE_TRANS(ARG.TYPE_SUBNAME::VARCHAR2), '_NULL_')::VARCHAR2(128) AS TYPE_SUBNAME, - CASE - WHEN ARG.TYPE_OBJECT_TYPE != '_NULL_' - THEN SYS.ORA_CASE_TRANS(ARG.TYPE_OBJECT_TYPE::VARCHAR2)::VARCHAR2(128) - ELSE - NULL - END::VARCHAR2(128) AS TYPE_OBJECT_TYPE, + NULLIF(SYS.ORA_CASE_TRANS(ARG.TYPE_OBJECT_TYPE::VARCHAR2), '_NULL_')::VARCHAR2(128) AS TYPE_OBJECT_TYPE,Also applies to: 550-554, 557-561
679-682: Duplicate case-insensitive checks (ALL_ARGUMENTS) — same simplification as DBA_ARGUMENTS.- WHEN ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'table' OR ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'TABLE' - OR ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'view' OR ARG.TYPE_OBJECT_TYPE::VARCHAR2 = 'VIEW' + WHEN UPPER(ARG.TYPE_OBJECT_TYPE::VARCHAR2) IN ('TABLE','VIEW') THEN 'PL/SQL RECORD'
759-763: Prefer NULLIF over sentinel comparison in ALL_ARGUMENTS too.- CASE - WHEN ARG.TYPE_NAME != '_NULL_' - THEN SYS.ORA_CASE_TRANS(ARG.TYPE_NAME::VARCHAR2)::VARCHAR2(128) - ELSE - NULL - END::VARCHAR2(128) AS TYPE_NAME, + NULLIF(SYS.ORA_CASE_TRANS(ARG.TYPE_NAME::VARCHAR2), '_NULL_')::VARCHAR2(128) AS TYPE_NAME, - CASE - WHEN ARG.TYPE_SUBNAME != '_NULL_' - THEN SYS.ORA_CASE_TRANS(ARG.TYPE_SUBNAME::VARCHAR2)::VARCHAR2(128) - ELSE - NULL - END::VARCHAR2(128) AS TYPE_SUBNAME, + NULLIF(SYS.ORA_CASE_TRANS(ARG.TYPE_SUBNAME::VARCHAR2), '_NULL_')::VARCHAR2(128) AS TYPE_SUBNAME, - CASE - WHEN ARG.TYPE_OBJECT_TYPE != '_NULL_' - THEN SYS.ORA_CASE_TRANS(ARG.TYPE_OBJECT_TYPE::VARCHAR2)::VARCHAR2(128) - ELSE - NULL - END::VARCHAR2(128) AS TYPE_OBJECT_TYPE, + NULLIF(SYS.ORA_CASE_TRANS(ARG.TYPE_OBJECT_TYPE::VARCHAR2), '_NULL_')::VARCHAR2(128) AS TYPE_OBJECT_TYPE,Also applies to: 765-769, 772-776
src/pl/plisql/src/pl_package.c (1)
910-997: Optional: eliminate per-object scans with a single pass + de-dup in-memory.If many referenced objects exist, scanning pg_depend per-object is O(N) index probes. Consider collecting all candidate (refclassid, refobjid, refobjsubid) into a temp hash and then a single syscache scan per (refclassid, refobjid) to mark duplicates.
Also applies to: 1206-1285
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (11)
contrib/ivorysql_ora/expected/ora_sysview.outis excluded by!**/*.outsrc/include/catalog/pg_proc.datis excluded by!**/*.datsrc/oracle_test/regress/expected/ora_package.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_array.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_call.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_control.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_nested_subproc.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_nested_subproc2.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_out_parameter.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_record.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_type_rowtype.outis excluded by!**/*.out
📒 Files selected for processing (43)
contrib/ivorysql_ora/src/sysview/sysview--1.0.sql(14 hunks)src/backend/catalog/dependency.c(9 hunks)src/backend/catalog/namespace.c(3 hunks)src/backend/catalog/pg_proc.c(6 hunks)src/backend/commands/functioncmds.c(9 hunks)src/backend/commands/tablecmds.c(14 hunks)src/backend/executor/spi.c(1 hunks)src/backend/oracle_parser/liboracle_parser.c(1 hunks)src/backend/oracle_parser/ora_gram.y(7 hunks)src/backend/parser/analyze.c(4 hunks)src/backend/parser/gram.y(1 hunks)src/backend/parser/parse_func.c(4 hunks)src/backend/parser/parse_package.c(6 hunks)src/backend/parser/parse_target.c(1 hunks)src/backend/parser/parse_type.c(5 hunks)src/backend/parser/parse_utilcmd.c(1 hunks)src/backend/parser/parser.c(1 hunks)src/backend/utils/adt/ruleutils.c(3 hunks)src/backend/utils/cache/lsyscache.c(5 hunks)src/backend/utils/cache/packagecache.c(2 hunks)src/backend/utils/fmgr/funcapi.c(4 hunks)src/include/catalog/dependency.h(3 hunks)src/include/catalog/pg_proc.h(2 hunks)src/include/commands/tablecmds.h(1 hunks)src/include/funcapi.h(3 hunks)src/include/nodes/parsenodes.h(3 hunks)src/include/oracle_parser/ora_kwlist.h(1 hunks)src/include/parser/parse_package.h(1 hunks)src/include/parser/parse_target.h(1 hunks)src/include/parser/parse_type.h(1 hunks)src/include/parser/parser.h(1 hunks)src/oracle_test/regress/sql/ora_package.sql(1 hunks)src/pl/plisql/src/Makefile(1 hunks)src/pl/plisql/src/pl_comp.c(23 hunks)src/pl/plisql/src/pl_gram.y(11 hunks)src/pl/plisql/src/pl_handler.c(1 hunks)src/pl/plisql/src/pl_package.c(17 hunks)src/pl/plisql/src/pl_package.h(2 hunks)src/pl/plisql/src/pl_subproc_function.c(3 hunks)src/pl/plisql/src/plisql.h(3 hunks)src/pl/plisql/src/sql/plisql_nested_subproc2.sql(1 hunks)src/pl/plisql/src/sql/plisql_out_parameter.sql(1 hunks)src/pl/plisql/src/sql/plisql_type_rowtype.sql(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (24)
src/backend/parser/parser.c (1)
src/backend/utils/error/elog.c (2)
errcode(852-863)errmsg(1069-1085)
src/include/commands/tablecmds.h (1)
src/backend/commands/tablecmds.c (2)
check_function_depend_on_relation(22490-22522)check_function_depend_on_relation_column(22530-22566)
src/include/parser/parse_type.h (1)
src/backend/parser/parse_type.c (2)
LookupOraTypeName(912-918)LookupOraTypeNameExtended(924-1084)
src/pl/plisql/src/pl_handler.c (2)
src/pl/plisql/src/pl_comp.c (1)
plisql_compile(136-262)src/backend/executor/spi.c (2)
SPI_finish(187-221)SPI_result_code_string(2002-2075)
src/backend/parser/parse_type.c (4)
src/backend/catalog/namespace.c (3)
DeconstructQualifiedName(3542-3584)NameListToString(3835-3860)TypenameGetTypidExtended(1103-1127)src/backend/nodes/makefuncs.c (1)
makeRangeVar(473-487)src/backend/utils/cache/lsyscache.c (4)
get_rel_type_id(2339-2356)get_attnum(898-915)get_atttype(953-972)get_array_type(3212-3225)src/common/stringinfo.c (1)
appendStringInfoString(229-233)
src/pl/plisql/src/plisql.h (1)
src/pl/plisql/src/pl_comp.c (1)
plisql_free_function(3799-3887)
src/backend/catalog/namespace.c (3)
src/backend/parser/parse_type.c (3)
LookupOraTypeName(912-918)TypeNameToString(511-519)typeTypeId(675-681)src/include/access/htup_details.h (1)
GETSTRUCT(751-755)src/backend/utils/error/elog.c (2)
errcode(852-863)errmsg(1069-1085)
src/backend/utils/cache/lsyscache.c (4)
src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/backend/parser/parse_type.c (2)
LookupOraTypeName(912-918)typeTypeId(675-681)src/include/access/htup_details.h (1)
GETSTRUCT(751-755)src/backend/utils/mmgr/mcxt.c (1)
pfree(1523-1537)
src/include/catalog/dependency.h (1)
src/backend/catalog/dependency.c (1)
object_funpkgoid_comparator(2723-2735)
src/backend/utils/cache/packagecache.c (1)
src/backend/access/common/tupdesc.c (2)
CreateTemplateTupleDesc(163-201)TupleDescInitEntry(809-879)
src/include/parser/parse_target.h (1)
src/backend/parser/parse_target.c (2)
transformRowExpression(2054-2121)transformRowTargetList(2128-2191)
src/backend/catalog/pg_proc.c (8)
src/backend/commands/proclang.c (1)
get_language_oid(225-237)src/backend/utils/cache/lsyscache.c (1)
get_typtype(2990-3007)src/backend/commands/functioncmds.c (1)
get_transform_oid(2734-2749)src/backend/access/transam/xact.c (3)
BeginInternalSubTransaction(4694-4759)ReleaseCurrentSubTransaction(4768-4787)RollbackAndReleaseCurrentSubTransaction(4796-4853)src/include/utils/palloc.h (1)
MemoryContextSwitchTo(123-130)src/backend/utils/error/elog.c (3)
CopyErrorData(1766-1830)FlushErrorState(1887-1900)errmsg(1069-1085)src/backend/access/common/heaptuple.c (1)
heap_modify_tuple(1248-1302)src/backend/catalog/indexing.c (1)
CatalogTupleUpdate(312-326)
src/backend/utils/fmgr/funcapi.c (6)
src/backend/parser/parse_type.c (2)
LookupOraTypeName(912-918)typeTypeId(675-681)src/include/access/htup_details.h (1)
GETSTRUCT(751-755)src/backend/utils/mmgr/mcxt.c (1)
pfree(1523-1537)src/include/postgres.h (3)
DatumGetChar(116-120)ObjectIdGetDatum(256-260)CharGetDatum(126-130)src/backend/access/common/heaptuple.c (1)
heap_modify_tuple(1248-1302)src/backend/catalog/indexing.c (1)
CatalogTupleUpdate(312-326)
src/backend/parser/analyze.c (5)
src/backend/parser/parse_target.c (5)
transformRowExpression(2054-2121)transformExpressionList(220-278)transformRowTargetList(2128-2191)updateTargetListEntry(622-647)transformTargetList(121-204)src/backend/parser/parse_node.c (1)
parser_errposition(108-123)src/include/access/tupdesc.h (1)
TupleDescAttr(154-160)src/backend/parser/parse_relation.c (2)
attnameAttNum(3600-3621)attnumTypeId(3672-3685)src/backend/nodes/nodeFuncs.c (1)
exprType(44-316)
src/backend/parser/parse_target.c (4)
src/include/nodes/pg_list.h (2)
list_length(151-155)list_nth(298-303)src/backend/parser/parse_node.c (1)
parser_errposition(108-123)src/backend/nodes/nodeFuncs.c (1)
exprLocation(1410-1844)src/backend/parser/parse_expr.c (1)
transformExpr(141-157)
src/backend/commands/functioncmds.c (13)
src/backend/parser/parse_type.c (1)
typeTypeId(675-681)src/backend/nodes/outfuncs.c (1)
nodeToString(796-800)src/backend/nodes/makefuncs.c (1)
makeRangeVar(473-487)src/include/nodes/pg_list.h (1)
list_length(151-155)src/backend/utils/error/elog.c (5)
errcode(852-863)errmsg(1069-1085)CopyErrorData(1766-1830)FlushErrorState(1887-1900)errposition(1466-1477)src/backend/catalog/namespace.c (2)
NameListToString(3835-3860)DeconstructQualifiedName(3542-3584)src/backend/parser/parse_node.c (1)
parser_errposition(108-123)src/backend/access/transam/xact.c (3)
BeginInternalSubTransaction(4694-4759)ReleaseCurrentSubTransaction(4768-4787)RollbackAndReleaseCurrentSubTransaction(4796-4853)src/include/utils/palloc.h (1)
MemoryContextSwitchTo(123-130)src/backend/utils/cache/lsyscache.c (3)
get_func_real_rettype(1765-1818)repl_func_real_argtype(1825-1930)get_func_real_allargtype(1935-2026)src/backend/access/common/heaptuple.c (2)
heap_modify_tuple(1248-1302)heap_freetuple(1473-1477)src/backend/catalog/indexing.c (1)
CatalogTupleUpdate(312-326)src/backend/catalog/pg_depend.c (1)
recordDependencyOnCurrentExtension(192-243)
src/pl/plisql/src/pl_package.c (6)
src/backend/catalog/dependency.c (4)
new_object_addresses(2743-2757)add_exact_object_address(2789-2807)record_object_address_dependencies(2998-3007)free_object_addresses(3029-3036)src/include/postgres.h (4)
ObjectIdGetDatum(256-260)Int32GetDatum(216-220)CharGetDatum(126-130)PointerGetDatum(326-330)src/backend/access/index/genam.c (3)
systable_beginscan(404-497)systable_getnext(530-574)systable_endscan(619-647)src/include/access/htup_details.h (1)
GETSTRUCT(751-755)src/backend/utils/cache/lsyscache.c (1)
type_is_rowtype(3016-3033)src/backend/utils/adt/arrayfuncs.c (1)
construct_array(3380-3393)
src/backend/utils/adt/ruleutils.c (12)
src/include/utils/palloc.h (1)
MemoryContextSwitchTo(123-130)src/backend/access/common/tupdesc.c (2)
CreateTemplateTupleDesc(163-201)TupleDescInitEntry(809-879)src/backend/utils/sort/tuplestore.c (2)
tuplestore_begin_heap(329-350)tuplestore_putvalues(783-796)src/backend/access/index/genam.c (3)
systable_beginscan(404-497)systable_getnext(530-574)systable_endscan(619-647)src/backend/utils/fmgr/funcapi.c (1)
get_func_typename_info(1889-1933)src/backend/utils/mmgr/mcxt.c (2)
pfree(1523-1537)pstrdup(1698-1702)src/backend/catalog/namespace.c (2)
DeconstructQualifiedName(3542-3584)NameListToString(3835-3860)src/backend/nodes/makefuncs.c (1)
makeRangeVar(473-487)src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/backend/utils/cache/lsyscache.c (4)
type_is_rowtype(3016-3033)get_rel_name(2288-2305)get_atttypetypmodcoll(983-1002)get_rel_relkind(2363-2380)src/backend/parser/parse_type.c (1)
typeidTypeRelid(753-767)src/backend/utils/adt/regproc.c (1)
regtypeout(1280-1323)
src/backend/commands/tablecmds.c (7)
src/backend/utils/mmgr/mcxt.c (3)
palloc(1316-1344)pfree(1523-1537)repalloc(1543-1577)src/backend/catalog/dependency.c (1)
object_funpkgoid_comparator(2723-2735)src/backend/utils/cache/packagecache.c (4)
plisql_internel_funcs_init(393-404)PackageCacheIsValid(87-123)PackageCacheLookup(70-81)PackageHandle(143-149)src/backend/utils/fmgr/funcapi.c (1)
change_func_prostatus(2743-2784)src/include/access/htup_details.h (1)
GETSTRUCT(751-755)src/backend/access/index/genam.c (3)
systable_beginscan(404-497)systable_endscan(619-647)systable_getnext(530-574)src/backend/catalog/indexing.c (1)
CatalogTupleDelete(364-368)
src/backend/parser/parse_func.c (4)
src/include/postgres.h (1)
ObjectIdGetDatum(256-260)src/include/access/htup_details.h (1)
GETSTRUCT(751-755)src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/backend/utils/mmgr/mcxt.c (1)
pfree(1523-1537)
src/backend/catalog/dependency.c (4)
src/backend/utils/mmgr/mcxt.c (3)
palloc(1316-1344)repalloc(1543-1577)pfree(1523-1537)src/backend/catalog/indexing.c (1)
CatalogTupleDelete(364-368)src/backend/utils/cache/packagecache.c (4)
plisql_internel_funcs_init(393-404)PackageCacheIsValid(87-123)PackageCacheLookup(70-81)PackageHandle(143-149)src/backend/utils/fmgr/funcapi.c (1)
change_func_prostatus(2743-2784)
src/pl/plisql/src/pl_comp.c (8)
src/pl/plisql/src/pl_package.c (1)
plisql_parse_package_type(2231-2315)src/backend/parser/parse_type.c (3)
LookupOraTypeName(912-918)TypeNameToString(511-519)typeTypeId(675-681)src/backend/utils/fmgr/funcapi.c (1)
get_func_typename_info(1889-1933)src/backend/catalog/dependency.c (4)
new_object_addresses(2743-2757)add_exact_object_address(2789-2807)record_object_address_dependencies(2998-3007)free_object_addresses(3029-3036)src/backend/access/index/genam.c (3)
systable_beginscan(404-497)systable_getnext(530-574)systable_endscan(619-647)src/backend/nodes/makefuncs.c (3)
makeTypeNameFromNameList(531-541)makeTypeName(519-523)makeRangeVar(473-487)src/backend/utils/cache/lsyscache.c (2)
get_typtype(2990-3007)get_attnum(898-915)src/backend/catalog/namespace.c (2)
NameListToString(3835-3860)DeconstructQualifiedName(3542-3584)
src/include/funcapi.h (1)
src/backend/utils/fmgr/funcapi.c (2)
get_func_prostatus(2720-2738)change_func_prostatus(2743-2784)
src/pl/plisql/src/pl_subproc_function.c (3)
src/pl/plisql/src/pl_comp.c (1)
plisql_free_function(3799-3887)src/include/nodes/pg_list.h (1)
list_length(151-155)src/backend/parser/parser.c (1)
raw_parser(40-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: contrib_regression
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
| if (foundDep->deptype == DEPENDENCY_TYPE && | ||
| (object->classId == RelationRelationId || | ||
| object->classId == PackageRelationId || | ||
| object->classId == PackageBodyRelationId )) | ||
| { | ||
| if (foundDep->classid == 0) | ||
| continue; | ||
|
|
||
| switch ((&otherObject)->classId) | ||
| { | ||
| case ProcedureRelationId: | ||
|
|
||
| if (foundDep->deptype == DEPENDENCY_TYPE) | ||
| { | ||
| /* Add function Oid to the dependancy-oids list */ | ||
| if (numDependentFuncPkgOids >= maxDependentFuncPkgOids) | ||
| { | ||
| /* enlarge array if needed */ | ||
| maxDependentFuncPkgOids *= 2; | ||
| dependentFuncPkgOids = (ObjectFunOrPkg *) | ||
| repalloc(dependentFuncPkgOids, | ||
| maxDependentFuncPkgOids * sizeof(ObjectFunOrPkg)); | ||
| } | ||
|
|
||
| dependentFuncPkgOids[numDependentFuncPkgOids].objectId = foundDep->objid; | ||
| dependentFuncPkgOids[numDependentFuncPkgOids].flags = ProcedureRelationId; | ||
| numDependentFuncPkgOids++; | ||
|
|
||
| /* delete the tuple */ | ||
| CatalogTupleDelete(*depRel, &tup->t_self); | ||
| } | ||
| break; | ||
|
|
||
| case PackageRelationId: | ||
|
|
||
| if (foundDep->deptype == DEPENDENCY_TYPE) | ||
| { | ||
| /* Add package Oid to the dependancy-oids list */ | ||
| if (numDependentFuncPkgOids >= maxDependentFuncPkgOids) | ||
| { | ||
| /* enlarge array if needed */ | ||
| maxDependentFuncPkgOids *= 2; | ||
| dependentFuncPkgOids = (ObjectFunOrPkg *) | ||
| repalloc(dependentFuncPkgOids, | ||
| maxDependentFuncPkgOids * sizeof(ObjectFunOrPkg)); | ||
| } | ||
|
|
||
| dependentFuncPkgOids[numDependentFuncPkgOids].objectId = foundDep->objid; | ||
| dependentFuncPkgOids[numDependentFuncPkgOids].flags = PackageRelationId; | ||
| numDependentFuncPkgOids++; | ||
|
|
||
| /* delete the tuple */ | ||
| CatalogTupleDelete(*depRel, &tup->t_self); | ||
| } | ||
| break; | ||
| case PackageBodyRelationId: | ||
|
|
||
| if (foundDep->deptype == DEPENDENCY_TYPE) | ||
| { | ||
| /* Add package body Oid to the dependancy-oids list */ | ||
| if (numDependentFuncPkgOids >= maxDependentFuncPkgOids) | ||
| { | ||
| /* enlarge array if needed */ | ||
| maxDependentFuncPkgOids *= 2; | ||
| dependentFuncPkgOids = (ObjectFunOrPkg *) | ||
| repalloc(dependentFuncPkgOids, | ||
| maxDependentFuncPkgOids * sizeof(ObjectFunOrPkg)); | ||
| } | ||
|
|
||
| dependentFuncPkgOids[numDependentFuncPkgOids].objectId = foundDep->objid; | ||
| dependentFuncPkgOids[numDependentFuncPkgOids].flags = PackageBodyRelationId; | ||
| numDependentFuncPkgOids++; | ||
|
|
||
| /* delete the tuple */ | ||
| CatalogTupleDelete(*depRel, &tup->t_self); | ||
| } | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
|
|
||
| continue; | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Release deletion locks in DEPENDENCY_TYPE fast path to avoid lock leaks.
You AcquireDeletionLock(&otherObject, 0) earlier in the loop, then in the TYPE branch you continue after deleting the dep tuple. The lock is never released because these objects are not scheduled for deletion. This can hold AccessExclusiveLock on functions/packages until end of DROP, causing unnecessary blocking.
Apply:
case ProcedureRelationId:
...
- /* delete the tuple */
+ /* delete the tuple */
CatalogTupleDelete(*depRel, &tup->t_self);
+ /* not deleting the object; release the lock we acquired */
+ ReleaseDeletionLock(&otherObject);
break;
...
case PackageRelationId:
...
- CatalogTupleDelete(*depRel, &tup->t_self);
+ CatalogTupleDelete(*depRel, &tup->t_self);
+ ReleaseDeletionLock(&otherObject);
break;
...
case PackageBodyRelationId:
...
- CatalogTupleDelete(*depRel, &tup->t_self);
+ CatalogTupleDelete(*depRel, &tup->t_self);
+ ReleaseDeletionLock(&otherObject);
break;
...
}
continue;Also add a default path release if you add any new classes in future.
🏁 Script executed:
#!/bin/bash
# Show TYPE-branch locations and ensure ReleaseDeletionLock follows CatalogTupleDelete
rg -nC3 'DEPENDENCY_TYPE' src/backend/catalog/dependency.cLength of output: 1308
Release deletion locks in DEPENDENCY_TYPE fast path
After each CatalogTupleDelete in the ProcedureRelationId, PackageRelationId, and PackageBodyRelationId cases, call ReleaseDeletionLock(&otherObject) to match the earlier AcquireDeletionLock and prevent AccessExclusiveLocks from leaking; also add a default-case ReleaseDeletionLock for any future classes.
🤖 Prompt for AI Agents
In src/backend/catalog/dependency.c around lines 922 to 1004, the fast path
handling DEPENDENCY_TYPE deletes tuples for ProcedureRelationId,
PackageRelationId, and PackageBodyRelationId but does not release the deletion
lock; after each CatalogTupleDelete(&tup->t_self) call add a call to
ReleaseDeletionLock(&otherObject). Also ensure the switch has a default branch
that calls ReleaseDeletionLock(&otherObject) so any future classId values also
release the lock.
| /* | ||
| * Check if the datatype of function parameter is tablename.columnname%TYPE | ||
| */ | ||
| if (!has_pkg_type && | ||
| compatible_db == ORA_PARSER && | ||
| LANG_PLISQL_OID == languageOid && | ||
| t->pct_type) | ||
| { | ||
| RangeVar *rel = makeRangeVar(NULL, NULL, t->location); | ||
| Oid relid; | ||
|
|
||
| /* parse name list */ | ||
| switch (list_length(t->names)) | ||
| { | ||
| case 1: | ||
| ereport(ERROR, | ||
| (errcode(ERRCODE_SYNTAX_ERROR), | ||
| errmsg("wrong %%TYPE reference (too few dotted names): %s", | ||
| NameListToString(t->names)), | ||
| parser_errposition(pstate, t->location))); | ||
| break; | ||
| case 2: | ||
| rel->relname = strVal(linitial(t->names)); | ||
| break; | ||
| case 3: | ||
| rel->schemaname = strVal(linitial(t->names)); | ||
| rel->relname = strVal(lsecond(t->names)); | ||
| break; | ||
| case 4: | ||
| rel->catalogname = strVal(linitial(t->names)); | ||
| rel->schemaname = strVal(lsecond(t->names)); | ||
| rel->relname = strVal(lthird(t->names)); | ||
| break; | ||
| default: | ||
| ereport(ERROR, | ||
| (errcode(ERRCODE_SYNTAX_ERROR), | ||
| errmsg("wrong %%TYPE reference (too many dotted names): %s", | ||
| NameListToString(t->names)), | ||
| parser_errposition(pstate, t->location))); | ||
| break; | ||
| } | ||
|
|
||
| /* Get oid of the relation */ | ||
| relid = RangeVarGetRelid(rel, NoLock, false); | ||
| if (OidIsValid(relid)) | ||
| { | ||
| typeNames[i] = CStringGetTextDatum(nodeToString(t)); | ||
| has_rel_type = true; | ||
| } | ||
| } | ||
| else if (!has_pkg_type && | ||
| compatible_db == ORA_PARSER && | ||
| LANG_PLISQL_OID == languageOid && | ||
| t->row_type) | ||
| { | ||
| char *schema_name = NULL; | ||
| char *relname = NULL; | ||
| RangeVar *rel = NULL; | ||
| Oid relid; | ||
|
|
||
| DeconstructQualifiedName(t->names, &schema_name, &relname); | ||
| rel = makeRangeVar(schema_name, relname, t->location); | ||
|
|
||
| relid = RangeVarGetRelid(rel, NoLock, false); | ||
| if (OidIsValid(relid)) | ||
| { | ||
| typeNames[i] = CStringGetTextDatum(nodeToString(t)); | ||
| has_rel_type = true; | ||
| } | ||
| } |
There was a problem hiding this comment.
%TYPE/%ROWTYPE detection gated by has_pkg_type—later params skipped.
Using !has_pkg_type in the t->pct_type and t->row_type branches suppresses detection for later parameters once any earlier parameter used a package type. That yields incomplete parametertypeNames.
Use per-parameter checks; don’t gate on the global flag:
- if (!has_pkg_type &&
- compatible_db == ORA_PARSER &&
+ if (compatible_db == ORA_PARSER &&
LANG_PLISQL_OID == languageOid &&
t->pct_type)
{
...
- if (OidIsValid(relid))
+ if (OidIsValid(relid))
{
typeNames[i] = CStringGetTextDatum(nodeToString(t));
has_rel_type = true;
}
}
- else if (!has_pkg_type &&
- compatible_db == ORA_PARSER &&
+ else if (compatible_db == ORA_PARSER &&
LANG_PLISQL_OID == languageOid &&
t->row_type)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /* | |
| * Check if the datatype of function parameter is tablename.columnname%TYPE | |
| */ | |
| if (!has_pkg_type && | |
| compatible_db == ORA_PARSER && | |
| LANG_PLISQL_OID == languageOid && | |
| t->pct_type) | |
| { | |
| RangeVar *rel = makeRangeVar(NULL, NULL, t->location); | |
| Oid relid; | |
| /* parse name list */ | |
| switch (list_length(t->names)) | |
| { | |
| case 1: | |
| ereport(ERROR, | |
| (errcode(ERRCODE_SYNTAX_ERROR), | |
| errmsg("wrong %%TYPE reference (too few dotted names): %s", | |
| NameListToString(t->names)), | |
| parser_errposition(pstate, t->location))); | |
| break; | |
| case 2: | |
| rel->relname = strVal(linitial(t->names)); | |
| break; | |
| case 3: | |
| rel->schemaname = strVal(linitial(t->names)); | |
| rel->relname = strVal(lsecond(t->names)); | |
| break; | |
| case 4: | |
| rel->catalogname = strVal(linitial(t->names)); | |
| rel->schemaname = strVal(lsecond(t->names)); | |
| rel->relname = strVal(lthird(t->names)); | |
| break; | |
| default: | |
| ereport(ERROR, | |
| (errcode(ERRCODE_SYNTAX_ERROR), | |
| errmsg("wrong %%TYPE reference (too many dotted names): %s", | |
| NameListToString(t->names)), | |
| parser_errposition(pstate, t->location))); | |
| break; | |
| } | |
| /* Get oid of the relation */ | |
| relid = RangeVarGetRelid(rel, NoLock, false); | |
| if (OidIsValid(relid)) | |
| { | |
| typeNames[i] = CStringGetTextDatum(nodeToString(t)); | |
| has_rel_type = true; | |
| } | |
| } | |
| else if (!has_pkg_type && | |
| compatible_db == ORA_PARSER && | |
| LANG_PLISQL_OID == languageOid && | |
| t->row_type) | |
| { | |
| char *schema_name = NULL; | |
| char *relname = NULL; | |
| RangeVar *rel = NULL; | |
| Oid relid; | |
| DeconstructQualifiedName(t->names, &schema_name, &relname); | |
| rel = makeRangeVar(schema_name, relname, t->location); | |
| relid = RangeVarGetRelid(rel, NoLock, false); | |
| if (OidIsValid(relid)) | |
| { | |
| typeNames[i] = CStringGetTextDatum(nodeToString(t)); | |
| has_rel_type = true; | |
| } | |
| } | |
| /* | |
| * Check if the datatype of function parameter is tablename.columnname%TYPE | |
| */ | |
| if (compatible_db == ORA_PARSER && | |
| LANG_PLISQL_OID == languageOid && | |
| t->pct_type) | |
| { | |
| RangeVar *rel = makeRangeVar(NULL, NULL, t->location); | |
| Oid relid; | |
| /* parse name list */ | |
| switch (list_length(t->names)) | |
| { | |
| case 1: | |
| ereport(ERROR, | |
| (errcode(ERRCODE_SYNTAX_ERROR), | |
| errmsg("wrong %%TYPE reference (too few dotted names): %s", | |
| NameListToString(t->names)), | |
| parser_errposition(pstate, t->location))); | |
| break; | |
| case 2: | |
| rel->relname = strVal(linitial(t->names)); | |
| break; | |
| case 3: | |
| rel->schemaname = strVal(linitial(t->names)); | |
| rel->relname = strVal(lsecond(t->names)); | |
| break; | |
| case 4: | |
| rel->catalogname = strVal(linitial(t->names)); | |
| rel->schemaname = strVal(lsecond(t->names)); | |
| rel->relname = strVal(lthird(t->names)); | |
| break; | |
| default: | |
| ereport(ERROR, | |
| (errcode(ERRCODE_SYNTAX_ERROR), | |
| errmsg("wrong %%TYPE reference (too many dotted names): %s", | |
| NameListToString(t->names)), | |
| parser_errposition(pstate, t->location))); | |
| break; | |
| } | |
| /* Get oid of the relation */ | |
| relid = RangeVarGetRelid(rel, NoLock, false); | |
| if (OidIsValid(relid)) | |
| { | |
| typeNames[i] = CStringGetTextDatum(nodeToString(t)); | |
| has_rel_type = true; | |
| } | |
| } | |
| else if (compatible_db == ORA_PARSER && | |
| LANG_PLISQL_OID == languageOid && | |
| t->row_type) | |
| { | |
| char *schema_name = NULL; | |
| char *relname = NULL; | |
| RangeVar *rel = NULL; | |
| Oid relid; | |
| DeconstructQualifiedName(t->names, &schema_name, &relname); | |
| rel = makeRangeVar(schema_name, relname, t->location); | |
| relid = RangeVarGetRelid(rel, NoLock, false); | |
| if (OidIsValid(relid)) | |
| { | |
| typeNames[i] = CStringGetTextDatum(nodeToString(t)); | |
| has_rel_type = true; | |
| } | |
| } |
| #include "utils/guc.h" | ||
| #include "utils/ora_compatible.h" | ||
| #include "utils/packagecache.h" | ||
| #include "funcapi.h" | ||
| #include "catalog/pg_proc.h" | ||
| #include "catalog/pg_package_body.h" | ||
| #include "lib/qunique.h" | ||
| #include "catalog/pg_package_d.h" | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify headers and symbol exposure.
- Ensure ObjectFunOrPkg and object_funpkgoid_comparator are declared via catalog/dependency.h in this file’s includes; otherwise you’ll hit implicit declaration/unknown type at compile time.
- Consider guarding Oracle-only includes behind ORA_PARSER/compatible_db if this TU is built in non-Oracle configurations.
Run:
🏁 Script executed:
rg -nP 'object_funpkgoid_comparator|ObjectFunOrPkg' src/backend/commands/tablecmds.c src/include/catalog/dependency.hLength of output: 2564
Include missing dependency header and guard Oracle-only headers
- Insert
#include "catalog/dependency.h"
among the othercatalog/*.hincludes soObjectFunOrPkgandobject_funpkgoid_comparatorare declared. - Wrap Oracle-specific headers (
utils/ora_compatible.h,utils/packagecache.h,catalog/pg_package_body.h,catalog/pg_package_d.h) in#ifdef ORA_PARSER(or#if defined(ORA_PARSER) || defined(compatible_db)) to avoid build failures in non-Oracle configurations.
🤖 Prompt for AI Agents
In src/backend/commands/tablecmds.c around lines 113 to 121, add the missing
catalog dependency include and guard Oracle-only headers: insert #include
"catalog/dependency.h" alongside the other catalog/*.h includes so that
ObjectFunOrPkg and object_funpkgoid_comparator are declared, and wrap the
Oracle-specific includes (utils/ora_compatible.h, utils/packagecache.h,
catalog/pg_package_body.h, catalog/pg_package_d.h) with #ifdef ORA_PARSER (or
#if defined(ORA_PARSER) || defined(compatible_db)) / corresponding #endif blocks
so those headers are only included in Oracle builds to avoid non-Oracle build
failures.
| static List *GetParentedForeignKeyRefs(Relation partition); | ||
| static void ATDetachCheckNoForeignKeyRefs(Relation partition); | ||
| static char GetAttributeCompression(Oid atttypid, const char *compression); | ||
| static void check_function_dependency_internal(Relation depRel, SysScanDesc scan); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add a “delete_deps” switch to the helper.
Some callers state “don’t delete the dependency” while the helper currently deletes. Add a boolean to control deletion (see comments on Lines 4072-4078 and 4360-4366).
-static void check_function_dependency_internal(Relation depRel, SysScanDesc scan);
+static void check_function_dependency_internal(Relation depRel, SysScanDesc scan, bool delete_deps);🤖 Prompt for AI Agents
In src/backend/commands/tablecmds.c around line 750, the helper declaration
static void check_function_dependency_internal(Relation depRel, SysScanDesc
scan); must be extended to accept a boolean delete_deps flag so callers can opt
out of deletion; change the prototype to include a bool delete_deps parameter,
update the implementation to conditionally perform deletions only when
delete_deps is true, and update every caller (including those referenced around
lines 4072-4078 and 4360-4366) to pass true or false as appropriate to preserve
their current semantics.
| /* | ||
| * if there is any functions(procedures, packages) that depend on | ||
| * the relation and attnum, invalidate functions(procedures, packages), | ||
| * but don't delete the dependency. | ||
| */ | ||
| check_function_depend_on_relation_column(myrelid, attnum); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Behavior-comment mismatch: function deletes deps despite “don’t delete” note.
check_function_dependency_internal deletes pg_depend rows; the comment says not to. Either stop deleting for this path or fix the comment. Prefer making deletion optional.
See proposed API in Lines 750 and usage tweak in Lines 22517 and 22661 of this file.
🤖 Prompt for AI Agents
In src/backend/commands/tablecmds.c around lines 4072-4078, the comment says we
should invalidate dependent functions without deleting pg_depend rows, but
check_function_depend_on_relation_column currently deletes dependencies; make
deletion optional instead of changing the comment. Modify the API per the
proposed signature near line 750 to accept a boolean flag (e.g., delete_deps)
and update the internal implementation to skip deletions when that flag is
false; propagate the new flag in the two call sites near lines 22517 and 22661
to pass false for this path; ensure the comment at 4072-4078 remains accurate
after the change and add a short comment on the new flag semantics.
| PG_TRY(); | ||
| { | ||
| /* Test-compile the function */ | ||
| plisql_compile(fake_fcinfo, true); | ||
| } | ||
| PG_CATCH(); | ||
| { | ||
| /* | ||
| * Disconnect from SPI manager | ||
| */ | ||
| if ((rc = SPI_finish()) != SPI_OK_FINISH) | ||
| elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); | ||
|
|
||
| ReleaseSysCache(tuple); | ||
|
|
||
| PG_RE_THROW(); | ||
| } | ||
| PG_END_TRY(); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Don’t raise ERROR from cleanup in PG_CATCH; it masks the original validation error
Inside the catch block, throwing on SPI_finish failure will override the real compile-time error and skip ReleaseSysCache/PG_RE_THROW. Cleanup paths in PG_CATCH must not ERROR. Log a warning (or ignore) instead.
Apply this minimal diff:
@@
- /*
- * Disconnect from SPI manager
- */
- if ((rc = SPI_finish()) != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
+ /*
+ * Disconnect from SPI manager. Do not ERROR here or we’ll mask the
+ * original validation failure.
+ */
+ {
+ int rc2 = SPI_finish();
+ if (rc2 != SPI_OK_FINISH)
+ ereport(WARNING,
+ (errmsg("SPI_finish during validator cleanup failed: %s",
+ SPI_result_code_string(rc2))));
+ }Optional: You can instead use PG_FINALLY for unified cleanup and drop the later SPI_finish/ReleaseSysCache calls, which removes duplication.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| PG_TRY(); | |
| { | |
| /* Test-compile the function */ | |
| plisql_compile(fake_fcinfo, true); | |
| } | |
| PG_CATCH(); | |
| { | |
| /* | |
| * Disconnect from SPI manager | |
| */ | |
| if ((rc = SPI_finish()) != SPI_OK_FINISH) | |
| elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); | |
| ReleaseSysCache(tuple); | |
| PG_RE_THROW(); | |
| } | |
| PG_END_TRY(); | |
| PG_TRY(); | |
| { | |
| /* Test-compile the function */ | |
| plisql_compile(fake_fcinfo, true); | |
| } | |
| PG_CATCH(); | |
| { | |
| /* | |
| * Disconnect from SPI manager. Do not ERROR here or we’ll mask the | |
| * original validation failure. | |
| */ | |
| { | |
| int rc2 = SPI_finish(); | |
| if (rc2 != SPI_OK_FINISH) | |
| ereport(WARNING, | |
| (errmsg("SPI_finish during validator cleanup failed: %s", | |
| SPI_result_code_string(rc2)))); | |
| } | |
| ReleaseSysCache(tuple); | |
| PG_RE_THROW(); | |
| } | |
| PG_END_TRY(); |
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_handler.c around lines 704 to 722, the PG_CATCH block
currently calls elog(ERROR) if SPI_finish() fails which will override the
original compile/validation error and prevent ReleaseSysCache/PG_RE_THROW;
change the cleanup to never raise ERROR: call SPI_finish() but if it returns a
failure log a WARNING (or ignore the error) instead of elog(ERROR), always call
ReleaseSysCache(tuple) and then PG_RE_THROW(); alternatively move SPI_finish()
and ReleaseSysCache(tuple) into a PG_FINALLY block so cleanup runs
unconditionally and remove the duplicated cleanup code.
| depRel = table_open(DependRelationId, RowExclusiveLock); | ||
|
|
||
| foreach(x, plisql_referenced_objects) | ||
| { | ||
| ObjectAddress *obj = (ObjectAddress *) lfirst(x); | ||
| ObjectAddress referenced; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use AccessShareLock for pg_depend scan; RowExclusiveLock is unnecessary and harms concurrency.
You only read pg_depend to detect duplicates; lower the lock level.
- depRel = table_open(DependRelationId, RowExclusiveLock);
+ depRel = table_open(DependRelationId, AccessShareLock);
...
- table_close(depRel, RowExclusiveLock);
+ table_close(depRel, AccessShareLock);Also applies to: 992-997
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_package.c around lines 929-934 (and also at 992-997),
the code opens pg_depend with RowExclusiveLock even though it only needs to read
the relation to detect duplicates; change the lock to AccessShareLock to avoid
unnecessary write-level locking and reduce contention. Replace the
RowExclusiveLock argument to table_open(DependRelationId, ...) with
AccessShareLock in both locations, and ensure any subsequent operations treat
the relation as read-only (no attempts to modify tuples under that lock).
|
|
||
| addrs = new_object_addresses(); | ||
| ObjectAddressSet(myself, PackageRelationId, pkgStruct->oid); | ||
| depRel = table_open(DependRelationId, RowExclusiveLock); | ||
|
|
||
| foreach(x, plisql_referenced_objects) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Same: downgrade lock for package spec duplicate scan.
- depRel = table_open(DependRelationId, RowExclusiveLock);
+ depRel = table_open(DependRelationId, AccessShareLock);
...
- table_close(depRel, RowExclusiveLock);
+ table_close(depRel, AccessShareLock);Also applies to: 1277-1283
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_package.c around lines 1213-1218 (and similarly at
1277-1283), the code opens DependRelationId with RowExclusiveLock for a package
spec duplicate scan; change the lock to a less-restrictive one (use
RowShareLock) when opening the relation for these read-only duplicate checks so
you don’t take an unnecessarily strong lock; update the table_open calls to use
RowShareLock and keep the surrounding logic and table_close calls unchanged.
| --the datatype returned by function is table | ||
| create table test(id integer,name varchar(23)); | ||
|
|
||
| drop function test; |
There was a problem hiding this comment.
DROP FUNCTION is invalid without a signature; this function has no IN args.
The upcoming definition is test(id OUT integer) — zero IN parameters. Use empty arg list and guard with IF EXISTS.
-drop function test;
+drop function if exists test();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| drop function test; | |
| drop function if exists test(); |
🤖 Prompt for AI Agents
In src/pl/plisql/src/sql/plisql_out_parameter.sql around line 946, the DROP
FUNCTION statement is invalid because it lacks a signature for a function with
zero IN parameters; change the statement to include an empty argument list and
guard with IF EXISTS, e.g. replace "drop function test;" with "DROP FUNCTION IF
EXISTS test();" so the drop targets the zero-argument function safely.
| perform f1(1); | ||
| END; | ||
| / |
There was a problem hiding this comment.
Non-Oracle keyword “perform” used in PL/iSQL block.
perform is PL/pgSQL-only. Use an assignment or call context to evaluate f1.
-BEGIN
- perform f1(1);
-END;
+DECLARE
+ _tmp int;
+BEGIN
+ _tmp := f1(1);
+END;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| perform f1(1); | |
| END; | |
| / | |
| DECLARE | |
| _tmp int; | |
| BEGIN | |
| _tmp := f1(1); | |
| END; | |
| / |
🤖 Prompt for AI Agents
In src/pl/plisql/src/sql/plisql_type_rowtype.sql around lines 1767-1769, the
PL/iSQL block uses the PostgreSQL-only keyword "perform" which is invalid;
replace the PERFORM f1(1); with a valid call or assignment (for example call the
function in a statement context or store its result into a variable via SELECT
INTO or simple assignment) so the function is evaluated without the non-Oracle
keyword.
| while (HeapTupleIsValid(tup = systable_getnext(scan))) | ||
| { | ||
| Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); | ||
| if (foundDep->objid == bodyStruct->oid) | ||
| { | ||
| isduplicate = true; | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Tighten duplicate-detection predicate to avoid false positives.
Also check depender class and dependency type when deciding duplicates.
- if (foundDep->objid == bodyStruct->oid)
+ if (foundDep->objid == bodyStruct->oid &&
+ foundDep->classid == PackageBodyRelationId &&
+ foundDep->deptype == DEPENDENCY_TYPE)
{
isduplicate = true;
break;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| while (HeapTupleIsValid(tup = systable_getnext(scan))) | |
| { | |
| Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); | |
| if (foundDep->objid == bodyStruct->oid) | |
| { | |
| isduplicate = true; | |
| break; | |
| } | |
| } | |
| while (HeapTupleIsValid(tup = systable_getnext(scan))) | |
| { | |
| Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); | |
| if (foundDep->objid == bodyStruct->oid && | |
| foundDep->classid == PackageBodyRelationId && | |
| foundDep->deptype == DEPENDENCY_TYPE) | |
| { | |
| isduplicate = true; | |
| break; | |
| } | |
| } |
| while (HeapTupleIsValid(tup = systable_getnext(scan))) | ||
| { | ||
| Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); | ||
| if (foundDep->objid == pkgStruct->oid) | ||
| { | ||
| isduplicate = true; | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Ditto for package spec duplicate scan.
- if (foundDep->objid == pkgStruct->oid)
+ if (foundDep->objid == pkgStruct->oid &&
+ foundDep->classid == PackageRelationId &&
+ foundDep->deptype == DEPENDENCY_TYPE)
{
isduplicate = true;
break;
}🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_package.c around lines 1256 to 1264, the
duplicate-package-spec scan is comparing foundDep->objid against pkgStruct->oid
(body oid) instead of the package spec OID; change the comparison to use
pkgSpec->oid (or the correct spec OID variable) so the scan detects spec
duplicates, keep the isduplicate=true and break behavior, and ensure the
scan/tuples are properly ended/freed (systable_endscan/heap_freetuple as
appropriate) after the loop.
| else if (oids[i] == NUMERICOID || | ||
| oids[i] == NUMBEROID) | ||
| { | ||
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | ||
| paramDataLengths[i] = Int32GetDatum(0); | ||
|
|
||
| /* precision (i.e. max number of digits) is in upper bits of typmod */ | ||
| paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff); | ||
| paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff); | ||
| } | ||
| else | ||
| { | ||
| if (type_is_rowtype(oids[i])) | ||
| paramObjectTypes[i] = CStringGetTextDatum("table"); | ||
| else | ||
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | ||
|
|
||
| paramDataLengths[i] = Int32GetDatum(0); | ||
| paramDataPrecisions[i] = Int32GetDatum(0); | ||
| paramDataScales[i] = Int32GetDatum(0); | ||
| } |
There was a problem hiding this comment.
Guard NUMERIC typmod decode against typmod = -1 (return/OUT record).
Right-shifting a negative typmod yields garbage precision/scale.
- else if (oids[i] == NUMERICOID ||
- oids[i] == NUMBEROID)
- {
- paramObjectTypes[i] = CStringGetTextDatum("_NULL_");
- paramDataLengths[i] = Int32GetDatum(0);
- /* precision (i.e. max number of digits) is in upper bits of typmod */
- paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff);
- paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff);
- }
+ else if (oids[i] == NUMERICOID || oids[i] == NUMBEROID)
+ {
+ paramObjectTypes[i] = CStringGetTextDatum("_NULL_");
+ paramDataLengths[i] = Int32GetDatum(0);
+ if (typmode != -1)
+ {
+ /* precision (i.e. max number of digits) is in upper bits of typmod */
+ paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff);
+ paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff);
+ }
+ else
+ {
+ paramDataPrecisions[i] = Int32GetDatum(0);
+ paramDataScales[i] = Int32GetDatum(0);
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else if (oids[i] == NUMERICOID || | |
| oids[i] == NUMBEROID) | |
| { | |
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | |
| paramDataLengths[i] = Int32GetDatum(0); | |
| /* precision (i.e. max number of digits) is in upper bits of typmod */ | |
| paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff); | |
| paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff); | |
| } | |
| else | |
| { | |
| if (type_is_rowtype(oids[i])) | |
| paramObjectTypes[i] = CStringGetTextDatum("table"); | |
| else | |
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | |
| paramDataLengths[i] = Int32GetDatum(0); | |
| paramDataPrecisions[i] = Int32GetDatum(0); | |
| paramDataScales[i] = Int32GetDatum(0); | |
| } | |
| else if (oids[i] == NUMERICOID || oids[i] == NUMBEROID) | |
| { | |
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | |
| paramDataLengths[i] = Int32GetDatum(0); | |
| if (typmode != -1) | |
| { | |
| /* precision (i.e. max number of digits) is in upper bits of typmod */ | |
| paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff); | |
| paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff); | |
| } | |
| else | |
| { | |
| paramDataPrecisions[i] = Int32GetDatum(0); | |
| paramDataScales[i] = Int32GetDatum(0); | |
| } | |
| } | |
| else | |
| { | |
| if (type_is_rowtype(oids[i])) | |
| paramObjectTypes[i] = CStringGetTextDatum("table"); | |
| else | |
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | |
| paramDataLengths[i] = Int32GetDatum(0); | |
| paramDataPrecisions[i] = Int32GetDatum(0); | |
| paramDataScales[i] = Int32GetDatum(0); | |
| } |
| else if (oids[i] == NUMERICOID || | ||
| oids[i] == NUMBEROID) | ||
| { | ||
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | ||
| paramDataLengths[i] = Int32GetDatum(0); | ||
|
|
||
| /* precision (i.e. max number of digits) is in upper bits of typmod */ | ||
| paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff); | ||
| paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff); | ||
| } | ||
| else | ||
| { |
There was a problem hiding this comment.
Same typmod guard for procedure arguments.
- else if (oids[i] == NUMERICOID ||
- oids[i] == NUMBEROID)
- {
- paramObjectTypes[i] = CStringGetTextDatum("_NULL_");
- paramDataLengths[i] = Int32GetDatum(0);
- /* precision (i.e. max number of digits) is in upper bits of typmod */
- paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff);
- paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff);
- }
+ else if (oids[i] == NUMERICOID || oids[i] == NUMBEROID)
+ {
+ paramObjectTypes[i] = CStringGetTextDatum("_NULL_");
+ paramDataLengths[i] = Int32GetDatum(0);
+ if (typmode != -1)
+ {
+ paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff);
+ paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff);
+ }
+ else
+ {
+ paramDataPrecisions[i] = Int32GetDatum(0);
+ paramDataScales[i] = Int32GetDatum(0);
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else if (oids[i] == NUMERICOID || | |
| oids[i] == NUMBEROID) | |
| { | |
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | |
| paramDataLengths[i] = Int32GetDatum(0); | |
| /* precision (i.e. max number of digits) is in upper bits of typmod */ | |
| paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff); | |
| paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff); | |
| } | |
| else | |
| { | |
| else if (oids[i] == NUMERICOID || | |
| oids[i] == NUMBEROID) | |
| { | |
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | |
| paramDataLengths[i] = Int32GetDatum(0); | |
| if (typmode != -1) | |
| { | |
| paramDataPrecisions[i] = Int32GetDatum(((typmode - VARHDRSZ) >> 16) & 0xffff); | |
| paramDataScales[i] = Int32GetDatum((typmode - VARHDRSZ) & 0xffff); | |
| } | |
| else | |
| { | |
| paramDataPrecisions[i] = Int32GetDatum(0); | |
| paramDataScales[i] = Int32GetDatum(0); | |
| } | |
| } | |
| else | |
| { |
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_package.c around lines 3390 to 3401, the code computes
numeric precision/scale by subtracting VARHDRSZ from typmod without guarding for
absent typmod; add the same typmod guard used elsewhere so you only compute
precision/scale when typmod is present (e.g. typmod >= VARHDRSZ), and when
typmod is not present set paramDataPrecisions[i] and paramDataScales[i] to an
appropriate sentinel (e.g. Int32GetDatum(-1)) instead of performing the
subtraction.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (8)
src/oracle_test/regress/expected/domain.outis excluded by!**/*.outsrc/oracle_test/regress/expected/plpgsql_2.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_record.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_type_rowtype.outis excluded by!**/*.outsrc/pl/plisql/src/expected/plisql_varprops.outis excluded by!**/*.outsrc/pl/plpgsql/src/expected/plpgsql_record_1.outis excluded by!**/*.outsrc/pl/plpgsql/src/expected/plpgsql_varprops_1.outis excluded by!**/*.outsrc/test/regress/expected/plpgsql.outis excluded by!**/*.out
📒 Files selected for processing (1)
src/oracle_test/regress/sql/domain.sql(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: meson_build (ubuntu-latest)
- GitHub Check: contrib_regression
| create domain d_fail as int4 constraint cc check (velues > 1) no inherit; | ||
| create domain d_fail as int4 constraint cc check (velues > 1) deferrable; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Typo in CHECK expression changes failure semantics; use VALUE, not "velues".
These negative tests should fail due to unsupported options (NO INHERIT / DEFERRABLE) on domain constraints, not because the expression references a nonexistent identifier. The typo masks the intended failure and can make expected-output diffs noisy.
Apply:
-create domain d_fail as int4 constraint cc check (velues > 1) no inherit;
-create domain d_fail as int4 constraint cc check (velues > 1) deferrable;
+create domain d_fail as int4 constraint cc check (value > 1) no inherit;
+create domain d_fail as int4 constraint cc check (value > 1) deferrable;Run to confirm no other typos slipped in:
🏁 Script executed:
#!/bin/bash
rg -nP '\bvelues\b' --type=sql -C2Length of output: 578
Fix typo in CHECK expressions (velues → value) in domain.sql
The typo in check (velues > 1) masks the intended failure semantics—these tests should error on unsupported options (NO INHERIT / DEFERRABLE), not an undefined identifier. Update in src/oracle_test/regress/sql/domain.sql lines 31–32:
- create domain d_fail as int4 constraint cc check (velues > 1) no inherit;
- create domain d_fail as int4 constraint cc check (velues > 1) deferrable;
+ create domain d_fail as int4 constraint cc check (value > 1) no inherit;
+ create domain d_fail as int4 constraint cc check (value > 1) deferrable;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| create domain d_fail as int4 constraint cc check (velues > 1) no inherit; | |
| create domain d_fail as int4 constraint cc check (velues > 1) deferrable; | |
| create domain d_fail as int4 constraint cc check (value > 1) no inherit; | |
| create domain d_fail as int4 constraint cc check (value > 1) deferrable; |
🤖 Prompt for AI Agents
In src/oracle_test/regress/sql/domain.sql around lines 31 to 32, the CHECK
expressions use the misspelled identifier "velues" which hides the intended test
semantics; change "velues" to "value" in both domain definitions so the CHECK
reads "check (value > 1)" and the test will fail for the unsupported options (NO
INHERIT / DEFERRABLE) rather than an undefined identifier error.
Support %rowtype and %type
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests