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.
WalkthroughImplements Oracle-style %TYPE and %ROWTYPE end-to-end: parsing, type lookup, PL/iSQL compilation, dependency recording/invalidation, and catalog/view exposure. Adds prostatus to pg_proc with subtransaction-based validation for PL/iSQL, expands SYS.* views with arg metadata sourced via a new SRF, integrates new dependency kind (DEPENDENCY_TYPE), and adds ROW-based INSERT/UPDATE handling. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Parser as Parser (ROWTYPE/PLISQL)
participant Resolver as Type Resolver
participant PL as PL/iSQL Compiler
participant Catalog as pg_proc/pg_depend
participant Inval as Invalidation
User->>Parser: CREATE FUNCTION ... (%TYPE/%ROWTYPE)
Parser->>Resolver: LookupOraTypeName / ROWTYPE reltype
Resolver-->>Parser: Type OIDs/typmod
Parser->>PL: Compile PL/iSQL (subxact)
PL->>Catalog: Insert/Update pg_proc (prostatus)
alt check_function_bodies ON
PL->>PL: Validate body in subtransaction
alt success
PL->>Catalog: set prostatus = VALID
else failure
PL->>Catalog: set prostatus = INVALID
end
else OFF
PL->>Catalog: set prostatus = NA
end
PL->>Catalog: Record DEPENDENCY_TYPE to referenced rel/col/pkg
Note over Catalog,Inval: Dependencies stored for later invalidation
sequenceDiagram
autonumber
actor DDL as DDL (ALTER TABLE ...)
participant DepScan as pg_depend scan
participant Proc as Functions/Packages
participant Catalog as pg_proc/pkg cache
DDL->>DepScan: Find DEPENDENCY_TYPE on rel/column
DepScan->>Proc: Collect dependent OIDs
Proc->>Proc: Sort & deduplicate
loop For each dependent
alt Function
Proc->>Catalog: set prostatus = NA (invalidate)
else Package
Proc->>Catalog: set package spec update flag
else Package Body
Proc->>Catalog: set package body update flag
end
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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: 47
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/include/nodes/parsenodes.h (1)
264-277: Regenerate node support to include TypeName.row_type
The node support files (src/backend/nodes/outfuncs.c,readfuncs.c,copyfuncs.c,equalfuncs.c) have noT_TypeNamecase or handling for the newrow_typefield (nor even for the existingpct_type), so serialization, deserialization, copying, and equality checks will be broken. Rerungen_node_support.pl(or manually add branches mirroringpct_typehandling) to restore correct behavior.src/backend/catalog/namespace.c (1)
1536-1574: CRITICAL: wrong index variable and too-strict missing_ok in type-fallbackInside the named-args branch, the loop uses
iinstead ofj, corrupting indexing and risking OOB access. Also,LookupOraTypeName(..., false)will error on missing types; this fallback should be non-throwing. Fix both:Apply:
- for (int j in 0..pronargs-1) { - newResult->args[j] = proargtypes[argnumbers[j]]; - if (argtypenames != NULL && strcmp(argtypenames[argnumbers[i]], "") != 0) { - TypeName *tname; - PkgType *pkgtype; - tname = (TypeName *) stringToNode(argtypenames[argnumbers[i]]); + for (int j = 0; j < pronargs; j++) { + newResult->args[j] = proargtypes[argnumbers[j]]; + if (argtypenames != NULL && strcmp(argtypenames[argnumbers[j]], "") != 0) { + TypeName *tname; + PkgType *pkgtype; + 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); + typtup = LookupOraTypeName(NULL, tname, NULL, true); 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); } }src/backend/utils/cache/lsyscache.c (2)
1895-1932: Release syscache before ERROR and use errcode (all-argtypes path).Repeat the same fix here; also free
p_argtypeNames[i]strings and the array after use to avoid leaks.Apply this diff:
- if (pkgtype == NULL) - { - Type typtup; - - typtup = LookupOraTypeName(NULL, tname, NULL, true); - - if (typtup) - { - if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) - { - pfree(tname); - elog(ERROR, "package, relation or view does not exist"); - } - - proargtypes[i] = typeTypeId(typtup); - ReleaseSysCache(typtup); - } - else - { - elog(ERROR, "package, relation or view does not exist"); - } - } + if (pkgtype == NULL) + { + Type typtup; + + typtup = LookupOraTypeName(NULL, tname, NULL, true); + + if (typtup) + { + if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) + { + ReleaseSysCache(typtup); + pfree(tname); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("package, relation or view does not exist"))); + } + proargtypes[i] = typeTypeId(typtup); + ReleaseSysCache(typtup); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("package, relation or view does not exist"))); + } + } @@ - pfree(p_argtypeNames); + for (i = 0; i < numargs; i++) + if (p_argtypeNames[i]) + pfree(p_argtypeNames[i]); + pfree(p_argtypeNames);
1801-1808: Use PROARGMODE_OUT and PROARGMODE_TABLE instead of FUNC_PARAM_ in lsyscache.c*
In src/backend/utils/cache/lsyscache.c (lines 1803–1804),promodesholds pg_proc proargmodes values; replaceFUNC_PARAM_OUT/FUNC_PARAM_TABLEwithPROARGMODE_OUT/PROARGMODE_TABLE.src/pl/plisql/src/pl_comp.c (1)
3334-3414: compute_function_hashkey leaks (and may mis-hash triggers).
- The
argtypenamesarray andelemsfromdeconstruct_array()are never freed.- Minor: ensure
nelems >= pronargsassumption holds.Apply:
@@ - Datum protypenames; - bool isNull; - Datum *elems; - int nelems; + Datum protypenames; + bool isNull; + Datum *elems = NULL; + int nelems = 0; @@ - if (argtypenames != NULL) + if (argtypenames != NULL) { for (i = 0; i < procStruct->pronargs; i++) { @@ } + /* free nested strings, container, and deconstructed elems */ + for (i = 0; i < nelems; i++) + if (argtypenames[i] != NULL) + pfree(argtypenames[i]); + pfree(argtypenames); + if (!isNull && elems != NULL) + pfree(elems); } else memcpy(hashkey->argtypes, procStruct->proargtypes.values, procStruct->pronargs * sizeof(Oid));Also consider guarding with:
Assert(nelems == procStruct->pronargs);or handle shorter arrays defensively.
🧹 Nitpick comments (40)
src/pl/plisql/src/sql/plisql_nested_subproc2.sql (1)
3909-3919: Add teardown for test hygieneDrop test_f2 after the block to avoid leaking objects into later tests that run in the same database.
Example to append after creation:
DROP FUNCTION test_f2(integer);Please confirm no other tests rely on test_f2 persisting beyond this script.
src/backend/commands/functioncmds.c (1)
1681-1686: Redundant initialization code.This loop initializes the arrays that should have been initialized at declaration (as mentioned in the earlier comment). If you add the initialization at declaration, this loop becomes redundant.
If the arrays are initialized at declaration, this loop can be removed to avoid duplicate initialization.
src/include/parser/parser.h (1)
46-50: Enum extended without doc update — clarify PL/iSQL modes.Please extend the comment above RawParseMode to describe the PL/iSQL variants for parity with PL/pgSQL.
Apply this doc tweak:
* RAW_PARSE_PLPGSQL_ASSIGNn: parse a PL/pgSQL assignment statement, * and return a one-element List containing a RawStmt node. "n" * gives the number of dotted names comprising the target ColumnRef. + * + * RAW_PARSE_PLISQL_EXPR: parse a PL/iSQL expression; returns a one-element + * List containing a RawStmt. + * + * RAW_PARSE_PLISQL_ASSIGNn: parse a PL/iSQL assignment statement; returns a + * one-element List containing a RawStmt. "n" is the dotted target length.src/include/parser/parse_target.h (1)
58-62: Use extern for consistency with this header’s prototypesEvery other declaration in this header uses extern; keep style consistent.
-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/include/catalog/dependency.h (2)
152-153: Expose comparator — document orderComparator returns -1 when a.objectId > b.objectId (descending). Add a brief comment here to record sort order for callers relying on it.
101-111: RenameflagstoclassIdand document the first‐field invariantFile: src/include/catalog/dependency.h (lines 101–111)
- Change the field
toOid flags; /* flags used to identify function, package and package body */since it isn’t a bitmask but the catalog‐relation OID.Oid classId; /* OID of pg_proc/pg_package/pg_package_body */- Replace the vague “MUST BE FIRST” comment with a clear explanation—or better, add
to document whystatic_assert(offsetof(ObjectFunOrPkg, objectId) == 0, "ObjectFunOrPkg.objectId must be first for sort/dedup logic");objectIdmust remain at offset 0 for the comparator and qunique calls.- No other code relies on this struct’s layout beyond the palloc/repalloc, pg_qsort and qunique usage.
src/include/catalog/pg_proc.h (2)
202-216: Fix typos and tighten comments for consistency; keep style to single-line summaries.Current text has typos (“te”), repetition, and verbose phrasing. Suggest:
-#define PROSTATUS_NA 'n' /* procedure status is unknown, - * must call ALTER FUNCTION COMPILE statement to - * validate te procedure, otherwise failed to - * call the procedure - */ -#define PROSTATUS_VALID 'v' /* procedure can be called */ -#define PROSTATUS_INVALID 'i' /* procedure status is invalid, - * must call ALTER FUNCTION COMPILE statement to - * validate te procedure, otherwise failed to - * call the procedure - */ +#define PROSTATUS_NA 'n' /* unknown/unvalidated; run ALTER FUNCTION ... COMPILE before calls succeed */ +#define PROSTATUS_VALID 'v' /* procedure can be called */ +#define PROSTATUS_INVALID 'i' /* invalid; run ALTER FUNCTION ... COMPILE to revalidate before calls succeed */
220-248: Confirm how prostatus is assigned during CREATE/REPLACE and PL/iSQL validation. Consider explicit param only if needed.If ProcedureCreate does not expose a prostatus parameter, ensure it initializes to PROSTATUS_VALID and PL/iSQL paths subsequently flip to NA/INVALID as appropriate. If multiple callers need different initial states, consider adding a prostatus arg to ProcedureCreate to avoid post-hoc UPDATEs.
src/backend/parser/analyze.c (1)
3828-3929: Row-target dispatcher: polish booleans and fast-paths; leverage transformed TList.
- Use bool for flags; initialize attrrowtype to InvalidOid.
- Avoid duplicate transformTargetList calls by transforming once and reusing.
- Minor correctness: treat ROW identifier case-insensitively consistently (you used pg_strcasecmp here; elsewhere strcmp).
- int target_has_row = false; - bool cols_has_row = false; - int col_is_complex = false; - int row_no; - Oid attrrowtype; + bool target_has_row = false; + bool cols_has_row = false; + bool col_is_complex = false; + int row_no = 0; + Oid attrrowtype = InvalidOid;[ suggest_nitpick ]
src/backend/parser/parse_target.c (1)
2069-2093: Star handling messages should be specific.“unsupported syntax” is vague. Prefer messages similar to existing code (“row expansion via "*" is not supported here” or “invalid user.table.column, table.column, or column specification”) with errposition.
src/backend/catalog/dependency.c (2)
92-95: Remove duplicate include of funcapi.h.funcapi.h is already included at Line 82. Redundant include increases rebuild churn.
Apply:
#include "utils/packagecache.h" -#include "commands/tablecmds.h" -#include "funcapi.h" -#include "lib/qunique.h" +#include "commands/tablecmds.h" +#include "lib/qunique.h"
927-929: Unnecessary classid==0 check.pg_depend.classid should always be valid. If you intend to future-proof, add a comment; otherwise remove for clarity.
src/backend/catalog/pg_proc.c (1)
598-610: DRY the prostatus assignment (minor).The prostatus initialization for plisql/ORA mode is duplicated. Compute a bool is_plisql once and reuse.
Example:
+ bool is_plisql = (ORA_PARSER == compatible_db && + languageObjectId == get_language_oid("plisql", true)); ... - if (ORA_PARSER == compatible_db && - languageObjectId == get_language_oid("plisql", true)) + if (is_plisql) { if (check_function_bodies) values[Anum_pg_proc_prostatus - 1] = CharGetDatum(PROSTATUS_VALID); else values[Anum_pg_proc_prostatus - 1] = CharGetDatum(PROSTATUS_NA); } ... - if (ORA_PARSER == compatible_db && - languageObjectId == get_language_oid("plisql", true)) + if (is_plisql)Also applies to: 643-655
src/backend/oracle_parser/ora_gram.y (1)
14592-14614: End-to-end verification checklist for %ROWTYPE and VALUES changes.Before merge, please validate:
- %ROWTYPE types flow: arguments, RETURNS, dependency recording, and error positions.
- SETOF %ROWTYPE (if added) parses and transforms.
- No bison conflicts; %expect 0 holds.
- No regression in INSERT … VALUES syntax.
- Views/rules/util code (ruleutils.c) handle valuesIsrow appropriately.
Would you like me to draft a minimal SQL test matrix to cover these paths?
Also applies to: 9339-9350, 14004-14005, 892-896
src/backend/commands/tablecmds.c (4)
13550-13552: FYI: Watch for unused locals.ObjectAddress address looks unused in the provided context. If truly unused, drop it to avoid warnings.
13705-13786: Minor fixes and dedup suggestion.
- Typo: “funciton” → “function”.
- Error message wording: prefer “failed to lookup package body …” for consistency (also elsewhere).
- The invalidation switch (function/package/package body) is duplicated here and in check_function_dependency_internal; extract a tiny static helper to reduce duplication and drift.
- * Now find out the dependent funciton which parameters datatype + * Now find out the dependent function whose parameters datatype
14022-14066: Error handling branch is fine; keep DEPENDENCY_TYPE guard narrow.Flow is correct: block AT_AlterColumnType for non-ORA paths; collect dependents only in ORA mode. Keep the elog(ERROR, "unexpected object…") conservative—consider LOG/DEBUG1 instead to avoid hard errors if new dep types appear.
13725-13735: Optional: extract common invalidation into a helper to avoid drift.Both blocks do the same three-way switch. A static invalidate_fun_or_pkg(ObjectFunOrPkg *entry) reduces duplication.
Also applies to: 21703-21716
contrib/ivorysql_ora/src/sysview/sysview--1.0.sql (3)
464-467: Simplify and harden PL/SQL RECORD detection (case-insensitive, NULL-safe).Use lower(COALESCE(...)) IN ('table','view') to avoid four equality checks and handle NULLs cleanly.
- 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' - THEN 'PL/SQL RECORD' + WHEN lower(COALESCE(ARG.TYPE_OBJECT_TYPE::text, '')) IN ('table','view') + THEN 'PL/SQL RECORD'
679-682: Apply the same PL/SQL RECORD detection simplification in ALL_ARGUMENTS.Keep logic consistent with 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' - THEN 'PL/SQL RECORD' + WHEN lower(COALESCE(ARG.TYPE_OBJECT_TYPE::text, '')) IN ('table','view') + THEN 'PL/SQL RECORD'
759-776: Unify TYPE_ null handling via NULLIF across all argument views*
Replace each
CASE WHEN ARG.TYPE_<X> != '_NULL_' THEN SYS.ORA_CASE_TRANS(ARG.TYPE_<X>::VARCHAR2)::VARCHAR2(128) ELSE NULL END::VARCHAR2(128) AS TYPE_<X>with
SYS.ORA_CASE_TRANS(NULLIF(ARG.TYPE_<X>, '_NULL_')::VARCHAR2)::VARCHAR2(128) AS TYPE_<X>for TYPE_NAME, TYPE_SUBNAME and TYPE_OBJECT_TYPE in ALL_ARGUMENTS, USER_ARGUMENTS and DBA_ARGUMENTS.
PLS_TYPE logic (WHEN (T.TYPTYPE = 'b' OR 'c') AND ARG.PLS_TYPE IS NOT NULL → use ARG.PLS_TYPE; WHEN TYPTYPE='b' AND ARG.PLS_TYPE IS NULL → fallback to T.OID; ELSE NULL) is consistent and requires no change.
src/pl/plisql/src/plisql.h (1)
1281-1283: No state leak: globals are reset and nested contexts handled. check_referenced_objects and plisql_referenced_objects are set to NIL at the start of each compile (in both pl_comp.c and pl_package.c), freed at the end, and saved/restored for nested calls—no cross-session leaks or thread‐safety issues in the single‐process backend. Optional: encapsulate these fields into a compile‐context struct for clarity.src/pl/plisql/src/pl_subproc_function.c (1)
1511-1548: Extended reload-check for %TYPE/%ROWTYPE looks correctThe deeper comparison (pctrowtypname presence, pct_type/row_type flags, name list case-insensitive match) makes overload resolution robust for
%TYPE/%ROWTYPE. Consider early-continue after name-list length mismatch to skip the loop and save cycles.src/backend/catalog/namespace.c (1)
1602-1618: Optional: deduplicate Ora-type fallback logicThe positional branch duplicates the package/Ora fallback. Consider extracting a small helper to resolve one arg-type name (handles pkg type, Ora type, validity notice) and call it from both branches. This reduces drift and future bugs.
src/backend/utils/fmgr/funcapi.c (1)
2716-2739: Typos and naming consistency in prostatus helper.Minor: fix typos in comment (“catalog”), and variable
prostaus_datum→prostatus_datumfor clarity.Apply this diff:
- * get function prostatus from the pg_proc catatlog + * get function prostatus from the pg_proc catalog @@ - Datum prostaus_datum; + Datum prostatus_datum; @@ - prostaus_datum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prostatus, - &isNull); + prostatus_datum = SysCacheGetAttr(PROCOID, procTup, + Anum_pg_proc_prostatus, &isNull); @@ - result = DatumGetChar(prostaus_datum); + result = DatumGetChar(prostatus_datum);src/backend/utils/cache/lsyscache.c (2)
1676-1681: Comment typos and clarity.Fix grammar: “dropped”, “really”. This improves developer comprehension.
Apply this diff:
- * If A function returns a variable in a package, - * the variable may be changed with package. If A function's - * returned datatype is tablename.columnname%TYPE, the tablename - * or the columnname can be changed or droped. - * so, this function created to get the realy type. + * If a function returns a variable in a package, + * the variable may change with the package. If a function's + * returned datatype is tablename.columnname%TYPE, the table name + * or the column name can be changed or dropped. + * This function gets the real underlying type.
2005-2018: Free arg type-name strings to avoid leaking memory.
argtypenamesentries come fromTextDatumGetCStringand should be freed after use.Outside the shown hunk, add before
return desc;inbuild_function_result_tupdesc_d:if (argtypenames) { for (i = 0; i < numargs; i++) if (argtypenames[i]) pfree(argtypenames[i]); pfree(argtypenames); }src/pl/plisql/src/sql/plisql_type_rowtype.sql (1)
55-63: Stability: proname LIKE filters can be ambiguous.SELECTs on pg_proc using proname LIKE may match multiple versions across schemas or after redefinitions, causing flaky counts or statuses.
Use exact match in current schema:
SELECT prostatus FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace WHERE p.proname = 'fun1' AND n.nspname = current_schema;Also applies to: 69-76, 154-175, 261-275, 281-292, 324-331, 154-157, 185-187, 298-303, 324-331, 518-522, 686-692, 707-714, 880-885, 896-903, 1556-1611, 2143-2146, 2373-2384, 2395-2415
src/backend/parser/parse_type.c (2)
30-34: Remove unused includes to keep compile tight.access/table.h and catalog/pg_class.h appear unused in this TU. If not required by macros, drop them.
-#include "catalog/pg_class.h" -#include "access/table.h"
912-918: Code duplication between LookupTypeNameExtended and LookupOraTypeNameExtended.The two functions are nearly identical except for the %TYPE notice behavior. Consider factoring into a single internal helper with a “emit_pct_type_notice” boolean to avoid drift.
static Type LookupTypeNameExtendedInternal(ParseState*, const TypeName*, int32*, bool temp_ok, bool missing_ok, bool emit_pct_notice);Also applies to: 924-1084
src/pl/plisql/src/pl_comp.c (2)
1149-1149: Nit: fix typo in comment.“alreay” → “already”.
Apply:
- /* Check duplicate dependencies that alreay been built in CREATE FUNCTION/PROCEDURE statment */ + /* Check duplicate dependencies that already were built in CREATE FUNCTION/PROCEDURE statement */
2597-2604: Minor API consistency and immutability notes.
pctrowtypnameis stored by pointer; ensure it lives in a long-lived context.- Keep
PLiSQL_typeimmutable after creation; all per-variable attributes should be on the var.Also applies to: 2669-2671, 3031-3037
src/pl/plisql/src/pl_package.c (3)
941-998: Dup-dependency scan is repeated and heavy; consider factoring.The pg_depend scan logic is duplicated in spec/body compiles. Factor into a small helper to DRY and reduce future divergence.
I can factor this into
static void record_pct_dependencies(ObjectAddress *myself)shared by both callers.Also applies to: 1218-1284
3226-3240: Minor: free temporary arrays after tuplestore_putvalues.The container arrays (
oids,paramModes, etc.) can be pfree’d after constructing ArrayTypes to reduce peak memory.Also applies to: 3270-3280, 3415-3476
454-456: Typos in DEBUG messages.“realy” → “really”.
Apply similar replacements across the file.
Also applies to: 574-577, 585-587, 469-471, 579-581
src/backend/utils/adt/ruleutils.c (5)
80-80: Dependency layering: parse_package.h include in ruleutils.cPulling parser/parse_package.h into ruleutils.c couples deparsing with Oracle package-type lookup. Prefer moving the tiny helper you need behind a utils-facing API (e.g., a small wrapper in utils/ or parser/ gate-kept by ORA_PARSER) to avoid spreading parser headers across utils/adt. If we keep this, add a short comment explaining why the include is safe here.
546-554: Forward-decl and fixed column count: hide magic 9NUM_RETURN_COLUMNS is fine, but consider an enum for column indexes (OID_COL, POSITION_COL, …) to prevent accidental mismatch between writers and consumers. Also, declare the helper with static inline comments on each column’s meaning to make maintenance easier.
14036-14044: RELKIND coverage for composite typesYou only classify RELKIND_RELATION and RELKIND_VIEW/MATVIEW. Composite types created via CREATE TYPE AS (relkind == RELKIND_COMPOSITE_TYPE) will be mislabelled. Consider setting type_object_type = "type" for RELKIND_COMPOSITE_TYPE.
13853-13858: Random-access flag should be booleanPassing (rsinfo->allowedModes & SFRM_Materialize_Random) to tuplestore_begin_heap works, but is a bitwise mask; wrap it as a boolean cast for clarity: (rsinfo->allowedModes & SFRM_Materialize_Random) != 0.
Apply this small change:
- tupstore = - tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, + tupstore = + tuplestore_begin_heap((rsinfo->allowedModes & SFRM_Materialize_Random) != 0, false, work_mem);
13959-14033: Error positioning helper with NULL pstateparser_errposition(NULL, typeName->location) will always yield 0; if you want a hint without pstate, consider errhint with NameListToString(...) and keep location-less errors, or thread a ParseState only when available. At minimum, drop parser_errposition(NULL, ...) to avoid confusion.
📜 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/utils/cache/packagecache.c (1)
src/backend/access/common/tupdesc.c (2)
CreateTemplateTupleDesc(66-102)TupleDescInitEntry(659-728)
src/include/parser/parse_target.h (1)
src/backend/parser/parse_target.c (2)
transformRowExpression(2045-2112)transformRowTargetList(2119-2182)
src/backend/catalog/namespace.c (3)
src/backend/parser/parse_type.c (3)
LookupOraTypeName(912-918)TypeNameToString(511-519)typeTypeId(675-681)src/backend/utils/error/elog.c (2)
errcode(856-867)errmsg(1069-1085)src/backend/utils/cache/syscache.c (1)
ReleaseSysCache(265-269)
src/include/commands/tablecmds.h (1)
src/backend/commands/tablecmds.c (2)
check_function_depend_on_relation(21494-21526)check_function_depend_on_relation_column(21534-21570)
src/backend/parser/parser.c (1)
src/backend/utils/error/elog.c (2)
errcode(856-867)errmsg(1069-1085)
src/include/parser/parse_type.h (1)
src/backend/parser/parse_type.c (2)
LookupOraTypeName(912-918)LookupOraTypeNameExtended(924-1084)
src/backend/parser/analyze.c (6)
src/include/nodes/pg_list.h (3)
list_length(151-155)list_head(127-131)list_nth(298-303)src/backend/parser/parse_target.c (5)
transformRowExpression(2045-2112)transformExpressionList(220-278)transformRowTargetList(2119-2182)updateTargetListEntry(619-644)transformTargetList(121-204)src/backend/parser/parse_node.c (1)
parser_errposition(108-123)src/backend/nodes/bitmapset.c (1)
bms_add_member(814-858)src/backend/parser/parse_relation.c (2)
attnameAttNum(3494-3515)attnumTypeId(3566-3579)src/backend/nodes/nodeFuncs.c (1)
exprType(44-313)
src/include/catalog/dependency.h (1)
src/backend/catalog/dependency.c (1)
object_funpkgoid_comparator(2708-2720)
src/pl/plisql/src/plisql.h (1)
src/pl/plisql/src/pl_comp.c (1)
plisql_free_function(3799-3887)
src/pl/plisql/src/pl_handler.c (3)
src/pl/plisql/src/pl_comp.c (1)
plisql_compile(136-262)src/backend/executor/spi.c (2)
SPI_finish(186-220)SPI_result_code_string(1997-2070)src/backend/utils/cache/syscache.c (1)
ReleaseSysCache(265-269)
src/backend/catalog/pg_proc.c (9)
src/backend/commands/proclang.c (1)
get_language_oid(225-237)src/backend/utils/cache/lsyscache.c (1)
get_typtype(2907-2924)src/backend/commands/functioncmds.c (1)
get_transform_oid(2706-2721)src/backend/access/transam/xact.c (3)
BeginInternalSubTransaction(4643-4708)ReleaseCurrentSubTransaction(4717-4736)RollbackAndReleaseCurrentSubTransaction(4745-4802)src/backend/utils/misc/guc.c (1)
AtEOXact_GUC(2268-2545)src/backend/utils/error/elog.c (5)
CopyErrorData(1728-1792)FlushErrorState(1849-1862)errmsg(1069-1085)errhint(1316-1331)errposition(1445-1456)src/backend/utils/cache/syscache.c (2)
SearchSysCache1(217-226)ReleaseSysCache(265-269)src/backend/access/common/heaptuple.c (1)
heap_modify_tuple(1247-1301)src/backend/catalog/indexing.c (1)
CatalogTupleUpdate(312-326)
src/include/funcapi.h (1)
src/backend/utils/fmgr/funcapi.c (2)
get_func_prostatus(2720-2738)change_func_prostatus(2743-2784)
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(1402-1834)src/backend/parser/parse_expr.c (1)
transformExpr(143-159)
src/backend/utils/cache/lsyscache.c (5)
src/backend/nodes/read.c (1)
stringToNode(89-93)src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/backend/parser/parse_type.c (2)
LookupOraTypeName(912-918)typeTypeId(675-681)src/backend/utils/mmgr/mcxt.c (1)
pfree(1519-1533)src/backend/utils/cache/syscache.c (1)
ReleaseSysCache(265-269)
src/backend/parser/parse_func.c (4)
src/backend/utils/cache/syscache.c (2)
SearchSysCache1(217-226)ReleaseSysCache(265-269)src/backend/nodes/read.c (1)
stringToNode(89-93)src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/backend/utils/mmgr/mcxt.c (1)
pfree(1519-1533)
src/backend/utils/adt/ruleutils.c (9)
src/backend/access/common/tupdesc.c (2)
CreateTemplateTupleDesc(66-102)TupleDescInitEntry(659-728)src/backend/utils/mmgr/mcxt.c (2)
pfree(1519-1533)pstrdup(1694-1698)src/backend/nodes/read.c (1)
stringToNode(89-93)src/backend/catalog/namespace.c (2)
DeconstructQualifiedName(3527-3569)NameListToString(3820-3845)src/backend/nodes/makefuncs.c (1)
makeRangeVar(424-438)src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/backend/utils/cache/lsyscache.c (5)
type_is_rowtype(2933-2950)get_rel_name(2205-2222)get_attnum(863-880)get_atttypetypmodcoll(948-967)get_rel_relkind(2280-2297)src/backend/parser/parse_type.c (1)
typeidTypeRelid(753-767)src/backend/utils/adt/regproc.c (1)
regtypeout(1280-1323)
src/backend/commands/functioncmds.c (13)
src/backend/parser/parse_type.c (1)
typeTypeId(675-681)src/backend/nodes/outfuncs.c (1)
nodeToString(793-797)src/backend/nodes/makefuncs.c (1)
makeRangeVar(424-438)src/include/nodes/pg_list.h (1)
list_length(151-155)src/backend/utils/error/elog.c (3)
CopyErrorData(1728-1792)FlushErrorState(1849-1862)errposition(1445-1456)src/backend/catalog/namespace.c (2)
NameListToString(3820-3845)DeconstructQualifiedName(3527-3569)src/backend/parser/parse_node.c (1)
parser_errposition(108-123)src/backend/access/transam/xact.c (3)
BeginInternalSubTransaction(4643-4708)ReleaseCurrentSubTransaction(4717-4736)RollbackAndReleaseCurrentSubTransaction(4745-4802)src/backend/utils/cache/lsyscache.c (3)
get_func_real_rettype(1682-1735)repl_func_real_argtype(1742-1847)get_func_real_allargtype(1852-1943)src/backend/utils/cache/syscache.c (2)
SysCacheGetAttr(478-501)ReleaseSysCache(265-269)src/backend/access/common/heaptuple.c (2)
heap_modify_tuple(1247-1301)heap_freetuple(1472-1476)src/backend/catalog/indexing.c (1)
CatalogTupleUpdate(312-326)src/backend/catalog/pg_depend.c (1)
recordDependencyOnCurrentExtension(193-244)
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)
src/backend/catalog/dependency.c (4)
src/backend/utils/mmgr/mcxt.c (3)
palloc(1315-1343)repalloc(1539-1573)pfree(1519-1533)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/backend/utils/fmgr/funcapi.c (5)
src/backend/parser/parse_type.c (2)
LookupOraTypeName(912-918)typeTypeId(675-681)src/backend/utils/mmgr/mcxt.c (1)
pfree(1519-1533)src/backend/utils/cache/syscache.c (3)
ReleaseSysCache(265-269)SysCacheGetAttr(478-501)SearchSysCache1(217-226)src/backend/access/common/heaptuple.c (1)
heap_modify_tuple(1247-1301)src/backend/catalog/indexing.c (1)
CatalogTupleUpdate(312-326)
src/backend/parser/parse_type.c (4)
src/backend/catalog/namespace.c (4)
DeconstructQualifiedName(3527-3569)NameListToString(3820-3845)LookupExplicitNamespace(3611-3642)TypenameGetTypidExtended(1088-1112)src/backend/nodes/makefuncs.c (1)
makeRangeVar(424-438)src/backend/utils/cache/lsyscache.c (4)
get_rel_type_id(2256-2273)get_attnum(863-880)get_atttype(918-937)get_array_type(3129-3142)src/backend/parser/parse_node.c (3)
parser_errposition(108-123)setup_parser_errposition_callback(142-153)cancel_parser_errposition_callback(158-163)
src/pl/plisql/src/pl_package.c (3)
src/backend/catalog/dependency.c (4)
new_object_addresses(2728-2742)add_exact_object_address(2774-2792)record_object_address_dependencies(2983-2992)free_object_addresses(3014-3021)src/backend/utils/cache/lsyscache.c (1)
type_is_rowtype(2933-2950)src/backend/utils/adt/arrayfuncs.c (1)
construct_array(3373-3386)
src/pl/plisql/src/pl_comp.c (9)
src/backend/utils/cache/syscache.c (2)
ReleaseSysCache(265-269)SearchSysCache1(217-226)src/backend/parser/parse_type.c (3)
LookupOraTypeName(912-918)TypeNameToString(511-519)typeTypeId(675-681)src/backend/parser/parse_node.c (1)
parser_errposition(108-123)src/backend/utils/fmgr/funcapi.c (1)
get_func_typename_info(1889-1933)src/backend/catalog/dependency.c (4)
new_object_addresses(2728-2742)add_exact_object_address(2774-2792)record_object_address_dependencies(2983-2992)free_object_addresses(3014-3021)src/backend/nodes/makefuncs.c (2)
makeTypeNameFromNameList(458-468)makeTypeName(446-450)src/backend/parser/parse_package.c (1)
LookupPkgTypeByTypename(352-377)src/include/nodes/pg_list.h (1)
list_length(151-155)src/backend/catalog/namespace.c (2)
NameListToString(3820-3845)DeconstructQualifiedName(3527-3569)
src/backend/commands/tablecmds.c (5)
src/backend/utils/mmgr/mcxt.c (3)
palloc(1315-1343)pfree(1519-1533)repalloc(1539-1573)src/backend/catalog/dependency.c (1)
object_funpkgoid_comparator(2708-2720)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/backend/utils/cache/syscache.c (2)
SearchSysCache1(217-226)ReleaseSysCache(265-269)
| GENERATE_SERIES(1::INTEGER, SYS._ORA_SRC_COUNT(P.OID))::NUMBER AS LINE, | ||
| UNNEST(STRING_TO_ARRAY(P.PROSRC, CHR(10)))::VARCHAR2(4000) AS TEXT, | ||
| UNNEST(STRING_TO_ARRAY(substring(pg_get_functiondef(P.OID), length('CREATE OR REPLACE ') + 1), CHR(10)))::VARCHAR2(4000) AS TEXT, | ||
| 0::VARCHAR2(256) AS ORIGIN_CON_ID |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Fix LINE/TEXT mismatch in ALL_SOURCE (uses pg_get_functiondef for TEXT but PROSRC for LINE).
LINE is derived from SYS._ORA_SRC_COUNT(P.OID) which counts PROSRC lines, while TEXT now comes from pg_get_functiondef(...). This misaligns rows. Align both by switching DBA_SOURCE/ALL_SOURCE/USER_SOURCE to the same source and updating SYS._ORA_SRC_COUNT accordingly.
Apply these diffs:
- Use pg_get_functiondef in DBA_SOURCE TEXT and keep LINE in sync:
- GENERATE_SERIES(1::INTEGER, SYS._ORA_SRC_COUNT(P.OID))::NUMBER AS LINE,
- UNNEST(STRING_TO_ARRAY(P.PROSRC, CHR(10)))::VARCHAR2(4000) AS TEXT,
+ 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,- Count lines from the same definition text and mark the function STABLE (not IMMUTABLE):
CREATE OR REPLACE FUNCTION SYS._ORA_SRC_COUNT(PROID OID)
RETURNS INTEGER AS $$
@@
BEGIN
- SELECT
- COUNT(1)
- FROM
- (
- SELECT
- UNNEST(STRING_TO_ARRAY(PROSRC, CHR(10)))
- FROM
- PG_PROC
- WHERE
- OID = $1
- ) AS SQ
+ SELECT COUNT(*)
+ FROM (
+ SELECT UNNEST(
+ STRING_TO_ARRAY(
+ substring(pg_get_functiondef($1), length('CREATE OR REPLACE ') + 1),
+ CHR(10)
+ )
+ )
+ ) AS sq
INTO RET;
RETURN RET;
END;
-$$ LANGUAGE PLPGSQL IMMUTABLE;
+$$ LANGUAGE PLPGSQL STABLE;Also update USER_SOURCE to match (see comment below).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In contrib/ivorysql_ora/src/sysview/sysview--1.0.sql around lines 365 to 367,
the ALL_SOURCE row generation is mismatched: LINE is computed from
SYS._ORA_SRC_COUNT(P.OID) (which counts PROSRC lines) while TEXT comes from
pg_get_functiondef(P.OID). Fix by switching LINE generation to count lines from
the same pg_get_functiondef text (i.e., update SYS._ORA_SRC_COUNT to base its
count on pg_get_functiondef output), change any function declaration using
IMMUTABLE to STABLE for that helper so it can call pg_get_functiondef, and
ensure USER_SOURCE/DBA_SOURCE are updated identically to use pg_get_functiondef
for TEXT and the new line-counting function for LINE so both columns are derived
from the same source.
| GENERATE_SERIES(1::INTEGER, SYS._ORA_SRC_COUNT(P.OID))::NUMBER AS LINE, | ||
| UNNEST(STRING_TO_ARRAY(P.PROSRC, CHR(10)))::VARCHAR2(4000) AS TEXT, | ||
| UNNEST(STRING_TO_ARRAY(substring(pg_get_functiondef(P.OID), length('CREATE OR REPLACE ') + 1), CHR(10)))::VARCHAR2(4000) AS TEXT, | ||
| 0::VARCHAR2(256) AS ORIGIN_CON_ID |
There was a problem hiding this comment.
🛠️ Refactor suggestion
USER_SOURCE has the same LINE/TEXT mismatch.
Mirror the fix used in ALL_SOURCE/DBA_SOURCE so LINE and TEXT derive from the same definition text.
- 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,
+ 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,(After applying the prior _ORA_SRC_COUNT change, this view will stay consistent.)
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In contrib/ivorysql_ora/src/sysview/sysview--1.0.sql around lines 394 to 396,
the view uses SYS._ORA_SRC_COUNT(P.OID) for LINE but builds TEXT from
pg_get_functiondef(P.OID) separately, causing LINE/TEXT mismatch; refactor so
both LINE and TEXT come from the same computed definition text (e.g., compute
def := substring(pg_get_functiondef(P.OID), length('CREATE OR REPLACE ') + 1) or
equivalent in a subquery/CTE), use SYS._ORA_SRC_COUNT on that same def for
GENERATE_SERIES bounds and unnest STRING_TO_ARRAY(def, CHR(10)) for TEXT, and
keep ORIGIN_CON_ID as before.
| 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, | ||
| 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, | ||
| NULL::VARCHAR2(128) AS TYPE_LINK, | ||
| NULL::VARCHAR2(128) AS TYPE_OBJECT_TYPE, | ||
| CASE | ||
| WHEN T.TYPTYPE = 'b' | ||
| 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, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid sentinel string 'NULL' in TYPE_ columns; use NULLIF for cleaner SQL.*
Sentinel comparison with "!= 'NULL'" is brittle and NULL-unsafe. Prefer NULLIF and a single expression.
- 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,
+ SYS.ORA_CASE_TRANS(NULLIF(ARG.TYPE_NAME, '_NULL_')::VARCHAR2)::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,
+ SYS.ORA_CASE_TRANS(NULLIF(ARG.TYPE_SUBNAME, '_NULL_')::VARCHAR2)::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,
+ SYS.ORA_CASE_TRANS(NULLIF(ARG.TYPE_OBJECT_TYPE, '_NULL_')::VARCHAR2)::VARCHAR2(128) AS TYPE_OBJECT_TYPE,If possible, also switch the SRF to emit NULL instead of 'NULL'.
Also applies to: 557-561, 563-565
| 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.
ReleaseDeletionLock for TYPE dependencies to avoid lock leak.
You acquire a deletion lock on otherObject (Line 872) but skip recursion and return to the loop for DEPENDENCY_TYPE without releasing it. This can hold AccessExclusiveLock indefinitely and cause deadlocks.
Apply:
if (foundDep->deptype == DEPENDENCY_TYPE &&
(object->classId == RelationRelationId ||
object->classId == PackageRelationId ||
object->classId == PackageBodyRelationId ))
{
if (foundDep->classid == 0)
continue;
@@
}
- continue;
+ /* We're not recursing to this object; release the lock we took. */
+ ReleaseDeletionLock(&otherObject);
+ continue;
}📝 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.
| 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; | |
| } | |
| if (foundDep->deptype == DEPENDENCY_TYPE && | |
| (object->classId == RelationRelationId || | |
| object->classId == PackageRelationId || | |
| object->classId == PackageBodyRelationId)) | |
| { | |
| if (foundDep->classid == 0) | |
| continue; | |
| switch ((&otherObject)->classId) | |
| { | |
| case ProcedureRelationId: | |
| /* existing handling… */ | |
| break; | |
| case PackageRelationId: | |
| /* existing handling… */ | |
| break; | |
| case PackageBodyRelationId: | |
| /* existing handling… */ | |
| break; | |
| default: | |
| break; | |
| } | |
| /* We're not recursing to this object; release the lock we took. */ | |
| ReleaseDeletionLock(&otherObject); | |
| continue; | |
| } |
🤖 Prompt for AI Agents
In src/backend/catalog/dependency.c around lines 922 to 1004, the code takes a
deletion lock on otherObject (around line 872) but when handling DEPENDENCY_TYPE
it skips recursion and uses multiple continue paths without releasing that lock,
causing a lock leak; fix by calling ReleaseDeletionLock(&otherObject) before any
early continue in this block: add ReleaseDeletionLock(&otherObject) immediately
before the "if (foundDep->classid == 0) continue;" and also add
ReleaseDeletionLock(&otherObject) just before the "continue;" that follows the
switch (i.e., right before the block's ending continue) so the lock is always
released when recursion is skipped.
| /* sort to avoid OID counter reverses */ | ||
| if (numDependentFuncPkgOids > 1) | ||
| { | ||
| /* Sort and de-dup OID arrays, so we can use binary search. */ | ||
| pg_qsort(dependentFuncPkgOids, numDependentFuncPkgOids, | ||
| sizeof(ObjectFunOrPkg), object_funpkgoid_comparator); | ||
|
|
||
| numDependentFuncPkgOids = qunique((void *)dependentFuncPkgOids, numDependentFuncPkgOids, | ||
| sizeof(ObjectFunOrPkg), object_funpkgoid_comparator); | ||
| } |
There was a problem hiding this comment.
Fix comparator/dedup: don’t collapse different classes with same OID.
object_funpkgoid_comparator compares only objectId. qunique() will drop entries where objectId matches but class (flags) differs (e.g., function OID == package OID), skipping needed invalidations.
Apply:
int
object_funpkgoid_comparator(const void *a, const void *b)
{
const ObjectFunOrPkg *obja = (const ObjectFunOrPkg *) a;
const ObjectFunOrPkg *objb = (const ObjectFunOrPkg *) b;
if (obja->objectId > objb->objectId)
return -1;
if (obja->objectId < objb->objectId)
return 1;
-
- return 0;
+ /* Tie-break on class (stored in flags) to keep function/pkg distinct. */
+ if (obja->flags < objb->flags)
+ return -1;
+ if (obja->flags > objb->flags)
+ return 1;
+ return 0;
}Also applies to: 2709-2720
| --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.
Fix DROP FUNCTION syntax: add explicit argument list (zero IN args here).
drop function test; is ambiguous/invalid in PG-compatible syntax. This function has zero IN parameters (only an OUT), so the drop should target test() explicitly.
Apply this diff:
-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 invocation is ambiguous/invalid; replace the bare "drop function test;"
with an explicit zero-argument signature by calling "DROP FUNCTION test();" so
the DB targets the function with no IN parameters (only an OUT) unambiguously.
| CREATE OR REPLACE PACKAGE BODY pkg1 is | ||
| FUNCTION pfun(v tmpe%ROWTYPE) RETURN tmpe%ROWTYPE AS | ||
| r tmpe%ROWTYPE; | ||
| BEGIN | ||
| var1.phone_number := 'abc'; | ||
| r.phone_number := var1; | ||
| RETURN v; | ||
| END; | ||
| END; | ||
| / |
There was a problem hiding this comment.
Bug: assigning a row to a scalar column field.
In pkg1.pfun body, “r.phone_number := var1;” assigns a tmpe%ROWTYPE to a scalar. Use the specific field.
Apply:
- var1.phone_number := 'abc';
- r.phone_number := var1;
- RETURN v;
+ var1.phone_number := 'abc';
+ r.phone_number := var1.phone_number;
+ RETURN v;🤖 Prompt for AI Agents
In src/pl/plisql/src/sql/plisql_type_rowtype.sql around lines 1214 to 1223, the
function body assigns an entire tmpe%ROWTYPE (var1) to the scalar column
r.phone_number which is invalid; change the assignment to use the specific field
(e.g., r.phone_number := var1.phone_number) and ensure the function returns the
intended row (if you meant to return the modified row, return r instead of v).
| --Record that variables are allowed in the VALUES clause of an INSERT statement | ||
| DECLARE | ||
| v1 t1%ROWTYPE; | ||
| BEGIN | ||
| FOR i IN 1 .. 5 LOOP | ||
| v1.id := i; | ||
| v1.name := 'a' || i; | ||
| INSERT INTO t1 VALUES v1; | ||
| END LOOP; | ||
| END; | ||
| / | ||
| SELECT * FROM t1; | ||
|
|
||
| --Record that variables are allowed in the INTO subclause of a RETURNING clause | ||
| DECLARE | ||
| v1 t1%ROWTYPE; | ||
| v2 t1%ROWTYPE; | ||
| BEGIN | ||
| v1.id := 6; | ||
| v1.name := 'a6'; | ||
| INSERT INTO t1 VALUES v1 returning id,name into v2; | ||
| raise notice 'v2 = %', v2; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Harness robustness: expected-failure statements may abort test run.
Many blocks are intended to fail (comments say “failed”/“not support”). Unless pg_regress/psql is configured to continue on errors, these will abort the script. Wrap rainy-day cases in an anonymous block with EXCEPTION to assert failure but keep the run going.
Example pattern:
DO $$
BEGIN
-- expected to fail
BEGIN
INSERT INTO t1 VALUES v1, v1;
RAISE EXCEPTION 'Expected failure did not occur';
EXCEPTION WHEN others THEN
RAISE NOTICE 'Expected error: %', SQLERRM;
END;
END$$;Also applies to: 1880-1898, 1900-1916, 1960-1985, 1994-2001, 2065-2074, 2081-2093, 2096-2115
🤖 Prompt for AI Agents
In src/pl/plisql/src/sql/plisql_type_rowtype.sql around lines 1728-1749 (and
similarly for ranges 1880-1898, 1900-1916, 1960-1985, 1994-2001, 2065-2074,
2081-2093, 2096-2115), several PL/pgSQL blocks are expected to fail but are
written plainly and will abort the test run; change each of those "expected to
fail" examples to execute inside an anonymous DO block that wraps the failing
statement in its own BEGIN...EXCEPTION WHEN others THEN handler so the error is
caught and the run continues, and inside the inner block raise an explicit
exception if the failing statement does not error (to assert failure), while on
exception raise a NOTICE with SQLERRM to record the expected error; ensure each
replacement preserves the original failing statement and message text.
| a t1%ROWTYPE; | ||
| BEGIN | ||
| RETURN v; | ||
| END; | ||
| / | ||
| SELECT prostatus FROM pg_proc WHERE proname like 'fun9'; --v | ||
|
|
There was a problem hiding this comment.
Bug: undefined identifier in return.
fun9() returns t1%ROWTYPE but returns “v”, which is not defined. Should return the local “a”.
- RETURN v;
+ RETURN a;🤖 Prompt for AI Agents
In src/pl/plisql/src/sql/plisql_type_rowtype.sql around lines 2207 to 2213, the
function fun9 declares a local row variable (a) and is declared to return
t1%ROWTYPE but the RETURN statement uses an undefined identifier "v"; change the
RETURN to use the local row variable "a" (RETURN a) so the function returns the
correct, defined row variable.
| a t1%ROWTYPE; | ||
| b t1%ROWTYPE; | ||
| BEGIN | ||
| RETURN v; | ||
| END; | ||
| / |
There was a problem hiding this comment.
Bug: wrong return in scalar-returning function.
fun10() returns int but returns “v” (undefined). Return a scalar.
- RETURN v;
+ RETURN 1;📝 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.
| a t1%ROWTYPE; | |
| b t1%ROWTYPE; | |
| BEGIN | |
| RETURN v; | |
| END; | |
| / | |
| a t1%ROWTYPE; | |
| b t1%ROWTYPE; | |
| BEGIN | |
| RETURN 1; | |
| END; | |
| / |
🤖 Prompt for AI Agents
In src/pl/plisql/src/sql/plisql_type_rowtype.sql around lines 2221-2226, the
function fun10 is declared to return an int but currently returns an undefined
identifier "v" (and only rowtype variables a and b are declared); replace the
invalid RETURN v with a proper scalar integer return: either return a specific
integer field from one of the rowtype variables (e.g., RETURN a.some_int_column)
or declare and set a local integer variable (e.g., declare i integer := <expr>;
… RETURN i;), ensuring the returned identifier exists and is assigned an integer
value before RETURN.
| /* switch context to build tuplestore */ | ||
| per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; | ||
| oldcontext = MemoryContextSwitchTo(per_query_ctx); | ||
|
|
||
| /* | ||
| * build tupdesc for result tuples. | ||
| */ | ||
| tupdesc = CreateTemplateTupleDesc(NUM_RETURN_COLUMNS); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 1, "oid", | ||
| OIDOID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 2, "position", | ||
| INT4OID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type_name", | ||
| TEXTOID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 4, "type_subname", | ||
| TEXTOID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 5, "type_object_type", | ||
| TEXTOID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_length", | ||
| INT4OID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_precision", | ||
| INT4OID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 8, "data_scale", | ||
| INT4OID, -1, 0); | ||
| TupleDescInitEntry(tupdesc, (AttrNumber) 9, "pls_type", | ||
| TEXTOID, -1, 0); | ||
|
|
||
| /* | ||
| * put all the tuples into a tuplestore by one scan of the hashtable. | ||
| * this way we can avoid the possible issue of the hashtable changing between calls. | ||
| */ | ||
| tupstore = | ||
| tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, | ||
| false, work_mem); | ||
|
|
||
| MemoryContextSwitchTo(oldcontext); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
tuplestore and lock handling; finalize and close with matching lock
- tuplestore_donestoring(tupstore) is commented out; call it before returning to signal completion.
- table_close(proc_rel, NoLock) should match the lock you acquired (AccessShareLock) per project conventions.
Also free rettypename after use to avoid per-query bloat while scanning pg_proc.
Apply this diff in the SRF:
@@
- if (rettypename != NULL)
- pg_get_function_arg_reference_typerowtype_internal(&tupstore, tupdesc,
- rettypename, 0,
- form_proc->oid, values, nulls);
+ if (rettypename != NULL)
+ {
+ pg_get_function_arg_reference_typerowtype_internal(&tupstore, tupdesc,
+ rettypename, 0,
+ form_proc->oid, values, nulls);
+ pfree(rettypename);
+ }
@@
- /* clean up and return the generated tuplestore */
- //tuplestore_donestoring(tupstore);
- table_close(proc_rel, NoLock);
+ /* clean up and return the generated tuplestore */
+ tuplestore_donestoring(tupstore);
+ table_close(proc_rel, AccessShareLock);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/backend/utils/adt/ruleutils.c around lines 13822-13858, the SRF currently
omits finalization/cleanup steps: call tuplestore_donestoring(tupstore) just
after finishing populating the tuplestore to signal completion, replace
table_close(proc_rel, NoLock) with table_close(proc_rel, AccessShareLock) so the
close matches the AccessShareLock acquired earlier, and pfree(rettypename) (or
the appropriate memory free call used in this codebase) after you finish using
rettypename to avoid per-query memory bloat during the pg_proc scan.
| Form_pg_proc form_proc = (Form_pg_proc) GETSTRUCT(proc_tuple); | ||
| int numargs = form_proc->pronargs; | ||
| char **p_argtypeNames = NULL; | ||
| char *rettypename = NULL; | ||
| int nth_arg; | ||
| Datum values[NUM_RETURN_COLUMNS]; | ||
| bool nulls[NUM_RETURN_COLUMNS]; | ||
|
|
||
| get_func_typename_info(proc_tuple, &p_argtypeNames, &rettypename); | ||
|
|
||
| if (p_argtypeNames == NULL && rettypename == NULL) | ||
| continue; | ||
|
|
||
| MemSet(nulls, 0, sizeof(nulls)); | ||
|
|
||
| if (rettypename != NULL) | ||
| pg_get_function_arg_reference_typerowtype_internal(&tupstore, tupdesc, | ||
| rettypename, 0, | ||
| form_proc->oid, values, nulls); | ||
|
|
||
| if (p_argtypeNames!= NULL) | ||
| { | ||
| for (nth_arg = 0; nth_arg < numargs; nth_arg++) | ||
| { | ||
| MemSet(nulls, 0, sizeof(nulls)); | ||
|
|
||
| if (strcmp(p_argtypeNames[nth_arg], "") != 0) | ||
| pg_get_function_arg_reference_typerowtype_internal(&tupstore, tupdesc, | ||
| p_argtypeNames[nth_arg], nth_arg + 1, | ||
| form_proc->oid, values, nulls); | ||
| } | ||
|
|
||
| pfree(p_argtypeNames); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Array length vs pronargs; potential out-of-bounds on p_argtypeNames
get_func_typename_info returns a char** but not its length; indexing it with form_pg_proc->pronargs assumes the array length equals pronargs (input-only), which may not hold if proallargtypes is present (OUT/TABLE args). Prefer deriving the count from get_func_arg_info() or extending get_func_typename_info to also return the array length, then iterate up to that length.
Run this to inspect get_func_typename_info’s allocation semantics and whether it guarantees length == pronargs:
🏁 Script executed:
#!/bin/bash
rg -n -C2 "get_func_typename_info\(" src/backend/fmgr/funcapi.c
rg -n -C3 "protypenames|prorettypename" src/backend/fmgr/funcapi.cLength of output: 268
🏁 Script executed:
#!/bin/bash
# locate get_func_typename_info definition and references across the repo
rg -n "get_func_typename_info" -C3 .
# find where protypenames and prorettypename are populated within that function
rg -n "protypenames" -C3 .
rg -n "prorettypename" -C3 .Length of output: 13697
Use actual array length from get_func_typename_info instead of pronargs
ruleutils.c indexes p_argtypeNames up to form_proc->pronargs, but get_func_typename_info allocates exactly nelems entries (no guarantee nelems==pronargs), risking out-of-bounds. Change get_func_typename_info to return its element count (or add a NULL terminator) and iterate by that count rather than pronargs.
| values[0] = ObjectIdGetDatum(funcOid); | ||
| values[1] = Int32GetDatum(position); | ||
| values[2] = CStringGetTextDatum("_NULL_"); | ||
| values[3] = CStringGetTextDatum("_NULL_"); | ||
| values[4] = CStringGetTextDatum("_NULL_"); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use NULLs instead of sentinel strings "NULL"
Returning "NULL" text for type_name/type_subname/type_object_type conflates NULL with a string value and complicates consumers. Leave those columns NULL when not applicable.
Included in the main helper diff above.
🤖 Prompt for AI Agents
In src/backend/utils/adt/ruleutils.c around lines 14070-14075, the code writes
the sentinel string "_NULL_" into the type_name/type_subname/type_object_type
columns; instead set those columns to SQL NULL instead. Replace the
CStringGetTextDatum("_NULL_") assignments with values[2] = values[3] = values[4]
= (Datum) 0 and set the corresponding nulls[2] = nulls[3] = nulls[4] = true (so
the tuple builder emits SQL NULLs), leaving other value/null handling unchanged.
| if (fieldTypeId == ORACHARCHAROID || | ||
| fieldTypeId == ORACHARBYTEOID || | ||
| fieldTypeId == ORAVARCHARCHAROID || | ||
| fieldTypeId == ORAVARCHARBYTEOID || | ||
| fieldTypeId == BPCHAROID || | ||
| fieldTypeId == VARCHAROID) | ||
| { | ||
| data_length = fieldTypMod - VARHDRSZ; | ||
| values[5] = Int32GetDatum(data_length); | ||
| nulls[6] = true; | ||
| nulls[7] = true; | ||
| values[8] = CStringGetTextDatum(DatumGetCString(DirectFunctionCall1(regtypeout, fieldTypeId))); | ||
| } | ||
| else if (fieldTypeId == NUMERICOID || | ||
| fieldTypeId == NUMBEROID) | ||
| { | ||
| /* precision (ie, max number of digits) is in upper bits of typmod */ | ||
| data_precision = Int32GetDatum(((fieldTypMod - VARHDRSZ) >> 16) & 0xffff); | ||
| data_scale = Int32GetDatum((fieldTypMod - VARHDRSZ) & 0xffff); | ||
| nulls[5] = true; | ||
| values[6] = data_precision; | ||
| values[7] = data_scale; | ||
| values[8] = CStringGetTextDatum("number"); | ||
| } | ||
| else | ||
| { | ||
| nulls[5] = true; | ||
| nulls[6] = true; | ||
| nulls[7] = true; | ||
| if (fieldTypeId == INT2OID || | ||
| fieldTypeId == INT4OID || | ||
| fieldTypeId == INT8OID) | ||
| values[8] = CStringGetTextDatum("number"); | ||
| else | ||
| values[8] = CStringGetTextDatum(DatumGetCString(DirectFunctionCall1(regtypeout, fieldTypeId))); | ||
| } |
There was a problem hiding this comment.
Numeric/char typmod handling and INT/Datum confusion
- Do not assign Datum to int variables (data_precision/data_scale). Compute as ints, wrap with Int32GetDatum when assigning into values[].
- Guard typmod < 0 to avoid negative lengths/precision; set the corresponding columns NULL when typmod is unknown.
Included in the main helper diff above.
🤖 Prompt for AI Agents
In src/backend/utils/adt/ruleutils.c around lines 14076-14111, the code
incorrectly assigns Datum values into int variables for numeric precision/scale
and fails to guard against negative typmod; change data_precision and data_scale
to be computed as plain ints from (fieldTypMod - VARHDRSZ) only when fieldTypMod
>= 0, then wrap with Int32GetDatum when storing into values[]; similarly, only
compute data_length for char types when fieldTypMod >= 0 and otherwise set the
corresponding values[] entries to NULL (set the matching nulls[] flags); ensure
you do not mix Datum and int types (use int temporaries, then Int32GetDatum(...)
on assignment) and correct the nulls/values indices so unknown typmods produce
NULLs.
| check_referenced_objects = true; | ||
| plisql_referenced_objects = NIL; | ||
|
|
There was a problem hiding this comment.
Re-entrant, exception-safe dependency tracking in package body compile.
Same global-state problem as in pl_comp.c. Save/restore and free deeply.
Apply:
@@
- check_referenced_objects = true;
- plisql_referenced_objects = NIL;
+ bool prev_check_referenced_objects = check_referenced_objects;
+ List *prev_plisql_referenced_objects = plisql_referenced_objects;
+ check_referenced_objects = true;
+ plisql_referenced_objects = NIL;
@@
- list_free(plisql_referenced_objects);
+ list_free_deep(plisql_referenced_objects);
+ plisql_referenced_objects = prev_plisql_referenced_objects;
+ check_referenced_objects = prev_check_referenced_objects;
@@
- check_referenced_objects = false;
- list_free(plisql_referenced_objects);
+ list_free_deep(plisql_referenced_objects);
+ plisql_referenced_objects = prev_plisql_referenced_objects;
+ check_referenced_objects = prev_check_referenced_objects;Also applies to: 1001-1003, 1036-1038
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_package.c around lines 885-887 (and similarly update
blocks at 1001-1003 and 1036-1038), the function sets global state
check_referenced_objects = true and plisql_referenced_objects = NIL without
saving/restoring or freeing the prior state; modify each block to save the old
values (e.g. bool old_check = check_referenced_objects; List
*old_plisql_referenced_objects = plisql_referenced_objects), then set the new
values, wrap the compile/processing code in a PG_TRY/PG_FINALLY (or equivalent)
so that in the FINALLY you restore check_referenced_objects = old_check and set
plisql_referenced_objects = old_plisql_referenced_objects, and if a new list was
allocated free it with list_free_deep(plisql_referenced_objects) before
restoring; ensure this pattern is applied at all three locations to make
dependency tracking re-entrant and exception-safe.
| check_referenced_objects = true; | ||
| plisql_referenced_objects = NIL; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Same fix for package spec compile: save/restore referenced-objects tracking.
Apply:
@@
- check_referenced_objects = true;
- plisql_referenced_objects = NIL;
+ bool prev_check_referenced_objects = check_referenced_objects;
+ List *prev_plisql_referenced_objects = plisql_referenced_objects;
+ check_referenced_objects = true;
+ plisql_referenced_objects = NIL;
@@
- check_referenced_objects = false;
- list_free(plisql_referenced_objects);
+ list_free_deep(plisql_referenced_objects);
+ plisql_referenced_objects = prev_plisql_referenced_objects;
+ check_referenced_objects = prev_check_referenced_objects;Also applies to: 1322-1324
🤖 Prompt for AI Agents
In src/pl/plisql/src/pl_package.c around lines 1158-1160 (and similarly at
1322-1324), the code unconditionally sets check_referenced_objects = true and
plisql_referenced_objects = NIL for package spec compilation but does not save
and restore the previous tracking state; modify both places to (1) save the
current values of check_referenced_objects and plisql_referenced_objects into
local temporaries, (2) set check_referenced_objects = true and
plisql_referenced_objects = NIL for the spec compile, and (3) after the spec
compile completes (including on error paths), restore the saved values so the
global tracking state is unchanged for callers.
|
|
||
| if (oids[i] == ORACHARCHAROID || | ||
| oids[i] == ORACHARBYTEOID || | ||
| oids[i] == ORAVARCHARCHAROID || | ||
| oids[i] == ORAVARCHARBYTEOID || | ||
| oids[i] == BPCHAROID || | ||
| oids[i] == VARCHAROID) | ||
| { | ||
| paramObjectTypes[i] = CStringGetTextDatum("_NULL_"); | ||
|
|
||
| if (typmode != -1) | ||
| paramDataLengths[i] = Int32GetDatum(typmode - VARHDRSZ); | ||
| else | ||
| 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 | ||
| { | ||
| 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); | ||
| } | ||
|
|
||
| i++; | ||
| } | ||
|
|
||
| foreach (lc, subproc->arg) | ||
| { |
There was a problem hiding this comment.
Guard NUMERIC typmod decode when typmod = -1.
(typmode - VARHDRSZ) is undefined for -1; leads to bogus precision/scale. Set zeros when typmod < 0.
Apply:
@@
- else if (oids[i] == NUMERICOID ||
- oids[i] == NUMBEROID)
+ 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);
+ if (typmode >= 0)
+ {
+ /* precision (max 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 (oids[i] == NUMERICOID ||
- oids[i] == NUMBEROID)
+ 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);
+ if (typmode >= 0)
+ {
+ /* precision (max 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);
+ }
}Also applies to: 3373-3411
When the declaration of the referenced item changes, the declaration of the referencing item would change accordingly.
The referencing item inherits data type, data size, constraints (unless the referenced item is a column) of the referenced item.
Argument datatype of function and prorcedure can be %ROWTYPE. Funtion's returned datatype can be %ROWTYPE.
Summary by CodeRabbit
New Features
Enhancements
Compatibility
Tests