Clean build warning and fix "invalid zero OID reference in pg_type.dat" issue#993
Clean build warning and fix "invalid zero OID reference in pg_type.dat" issue#993gaoxueyu merged 2 commits intoIvorySQL:masterfrom
Conversation
WalkthroughThis PR includes multiple refactoring changes across the codebase: variable declaration relocations within preprocessor guards, loop index renamings for consistency across several files, modified error handling paths, and addition of a module unload cleanup function. Most changes maintain existing behavior while improving code organization. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–25 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/backend/utils/adt/ruleutils.c (1)
14413-14555: Guard missing relations and avoid emitting tuples for unresolved%TYPEreferencesThe new use of
RangeVarGetRelid(rel, NoLock, true)meansrelid_1can legitimately beInvalidOidwhen the referenced relation does not exist. In that case:
get_attnum(relid_1, field)is still called, which issues a syscache lookup with OID 0; while it happens to returnInvalidAttrNumber, it’s safer and clearer not to query with an invalid OID at all.- When either the relation is missing or the attribute name is wrong,
attnumstaysInvalidAttrNumberandis_valid_typeremainsfalse, yet the function still callstuplestore_putvalues(*tupstore, tupdesc, values, nulls);withvalues[]never populated for this call. That can produce bogus rows (including zero OIDs) in the result, which undermines the goal of this change.Two concrete fixes:
- Only look up the attribute when
relid_1is valid, and keepis_valid_typefalseotherwise:- RangeVar *rel = makeRangeVar(NULL, NULL, typeName->location); - char *field = NULL; - Oid relid_1; + RangeVar *rel = makeRangeVar(NULL, NULL, typeName->location); + char *field = NULL; + Oid relid_1 = InvalidOid; @@ - relid_1 = RangeVarGetRelid(rel, NoLock, true); - attnum = get_attnum(relid_1, field); - - if (attnum != InvalidAttrNumber) - { - get_atttypetypmodcoll(relid_1, attnum, - &fieldTypeId, &fieldTypMod, &fieldCollation); - - /* this construct should never have an array indicator */ - Assert(typeName->arrayBounds == NIL); - is_valid_type = true; - } + relid_1 = RangeVarGetRelid(rel, NoLock, true); + + if (OidIsValid(relid_1)) + { + attnum = get_attnum(relid_1, field); + + if (attnum != InvalidAttrNumber) + { + get_atttypetypmodcoll(relid_1, attnum, + &fieldTypeId, &fieldTypMod, &fieldCollation); + + /* this construct should never have an array indicator */ + Assert(typeName->arrayBounds == NIL); + is_valid_type = true; + } + }
- Only emit a row when the reference could actually be resolved (either to a rowtype or a valid scalar type); otherwise, silently skip the bad reference:
- if (is_rowtype) - { - ... - } - else if (is_valid_type) - { - ... - } - - tuplestore_putvalues(*tupstore, tupdesc, values, nulls); + if (is_rowtype) + { + ... + tuplestore_putvalues(*tupstore, tupdesc, values, nulls); + } + else if (is_valid_type) + { + ... + tuplestore_putvalues(*tupstore, tupdesc, values, nulls); + } + else + { + /* unresolved %TYPE/%ROWTYPE reference: nothing to emit */ + }This way unresolved
%TYPE/%ROWTYPE references do not generate partially-initialized tuples, and syscache lookups are never performed with an invalid (0) relid.src/pl/plisql/src/pl_comp.c (1)
3345-3451:compute_function_hashkey’s newprotypenameslogic leaks temporaries and assumes array length matchespronargsThe idea of deriving
hashkey->argtypes[i]frompg_proc.protypenames(viaTypeName/package or Oracle type lookup) is a good way to make the cache key sensitive to%TYPE/package-based argument types.Two issues worth addressing:
- Unfreed allocations in a potentially long‑lived context
Inside the
if (procStruct->pronargs > 0)block:
deconstruct_arrayallocateselems.argtypenames = (char **) palloc(sizeof(char *) * nelems);- Each
TextDatumGetCString(elems[i])allocates achar *.None of
elems,argtypenames, or the per‑element strings are freed in this function. Ifcompute_function_hashkeyruns in a long‑lived context (e.g.,TopMemoryContextorfn_mcxtvia the call fromplisql_compile/plisql_free_function), that becomes a permanent leak per compiled function / free‑call.You can clean this up with minimal code churn by freeing per‑element strings in the loop and the arrays afterward, e.g.:
@@ -3401,6 +3401,10 @@ compute_function_hashkey(FunctionCallInfo fcinfo, - if (argtypenames != NULL) - { - for (i = 0; i < procStruct->pronargs; i++) - { + if (argtypenames != NULL) + { + for (i = 0; i < procStruct->pronargs; i++) + { hashkey->argtypes[i] = procStruct->proargtypes.values[i]; if (strcmp(argtypenames[i], "") != 0) { @@ -3435,7 +3439,13 @@ compute_function_hashkey(FunctionCallInfo fcinfo, - pfree(tname); - } - } - } + pfree(tname); + } + + /* `argtypenames[i]` was palloc'ed via TextDatumGetCString */ + pfree(argtypenames[i]); + } + + pfree(argtypenames); + pfree(elems); + }(If you prefer, you can also just
pfree(elems)and leave the text Datums to the context reset, following the usualdeconstruct_arraypattern.)
- Implicit assumption that
nelems == procStruct->pronargsThe loop iterates
i < procStruct->pronargsbutargtypenameshas lengthnelems. The catalog should normally guaranteenelems == pronargswhenprotypenamesis non‑NULL, but if that invariant is ever broken (corrupt row, partially initialized entry, etc.), you’d read past the end ofargtypenames.At minimum, an assertion or defensive clamp would make this more robust, for example:
Assert(nelems == procStruct->pronargs); /* or */ int nused = Min(nelems, procStruct->pronargs); for (i = 0; i < nused; i++) ...Together these tweaks keep the new behavior while avoiding silent leaks and making the hashkey construction safer against catalog inconsistencies.
🧹 Nitpick comments (4)
src/bin/psql/common.c (1)
3698-3698: LGTM: Variable scoping improvements.The changes improve code clarity:
- Explicit initialization of
iat line 3698 is defensive programming- Using
jfor the inner loop (line 3819) avoids shadowing the outerivariable (used at line 4007)- Proper variable scoping with
jdeclared within the nested block at line 3805These refactoring changes likely address compiler warnings about variable shadowing while maintaining correct logic.
Optional: The initialization of
i = 0at line 3698 is redundant since the for-loop at line 4007 re-initializes it. You could change it to justint i;to match typical C style, though the current form is harmless and may suppress certain compiler warnings.Also applies to: 3805-3830
src/pl/plisql/src/pl_scanner.c (1)
121-121: Remove dead code instead of commenting it out.These global variables are now unused since the scanner state has been moved to the
yyextrastructure for re-entrancy. Commented-out code clutters the codebase and can cause confusion during future maintenance.Apply this diff to remove the dead code:
-//static yyscan_t plisql_scanner; +-//static int cur_line_num; +Similarly, consider removing the corresponding commented-out lines in
plisql_recover_yylex_global_proper(line 814 and 827).Also applies to: 143-143
src/pl/plisql/src/pl_subproc_function.c (1)
414-414: Remove the redundant declaration instead of commenting it out.The outer-scope declaration at line 258 is sufficient. This inner-scope declaration was redundant and shadowed the outer variable. Rather than leaving commented-out code, remove it entirely.
- //PLiSQL_variable *argvariable;src/pl/plisql/src/pl_comp.c (1)
1097-1115: Reusingargtypenames/rettypenameis fine; consider tightening lifetime of the first set of namesResetting the outer
argtypenames/rettypenameand using a dedicated loop indexjremoves the variable shadowing and keeps the logic of buildingplisql_referenced_objectsintact. No functional issues here.One thing to be aware of:
get_func_typename_info(procTup, &argtypenames, &rettypename)is called earlier indo_compile(around the argument‑building logic) and its results are never freed; here you invoke it a second time, then onlypfree(argtypenames)andpfree(rettypename)from the second call. The strings produced byTextDatumGetCStringin both calls are left to the current memory context to clean up.Given this runs only at compile time it’s not critical, but if the earlier call happens in a long‑lived context you may eventually want to either:
- reuse the first
argtypenames/rettypenamehere instead of re-fetching, or- ensure the first call is made in a short‑lived context that’s reset at the end of compilation.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
contrib/uuid-ossp/uuid-ossp.c(2 hunks)src/backend/catalog/genbki.pl(1 hunks)src/backend/utils/adt/arrayfuncs.c(1 hunks)src/backend/utils/adt/ruleutils.c(2 hunks)src/bin/psql/common.c(5 hunks)src/fe_utils/string_utils.c(1 hunks)src/interfaces/libpq/ivy-exec.c(9 hunks)src/pl/plisql/src/pl_comp.c(1 hunks)src/pl/plisql/src/pl_handler.c(1 hunks)src/pl/plisql/src/pl_scanner.c(2 hunks)src/pl/plisql/src/pl_subproc_function.c(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-24T08:19:29.453Z
Learnt from: OreoYang
Repo: IvorySQL/IvorySQL PR: 931
File: src/backend/oracle_parser/ora_gram.y:0-0
Timestamp: 2025-10-24T08:19:29.453Z
Learning: In IvorySQL’s parser (src/backend/oracle_parser/ora_gram.y and related headers), downcase_identifier has the 4-argument signature:
extern char *downcase_identifier(const char *ident, int len, bool warn, bool truncate);
Applied to files:
src/fe_utils/string_utils.c
🧬 Code graph analysis (4)
src/backend/utils/adt/ruleutils.c (1)
src/backend/utils/cache/lsyscache.c (2)
get_attnum(955-972)get_atttypetypmodcoll(1040-1059)
src/pl/plisql/src/pl_handler.c (2)
src/backend/oracle_parser/liboracle_parser.c (1)
_PG_fini(84-91)contrib/ivorysql_ora/src/ivorysql_ora.c (1)
_PG_fini(107-119)
src/bin/psql/common.c (1)
src/bin/psql/variables.c (1)
BindVariableExist(1080-1108)
src/pl/plisql/src/pl_comp.c (1)
src/backend/utils/fmgr/funcapi.c (1)
get_func_typename_info(1889-1933)
⏰ 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: contrib_regression
- GitHub Check: pg_regression (ubuntu-latest)
- GitHub Check: oracle_pg_regression (ubuntu-latest)
- GitHub Check: oracle_regression (ubuntu-latest)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: meson_build (ubuntu-latest)
🔇 Additional comments (8)
contrib/uuid-ossp/uuid-ossp.c (1)
561-595: Hoisting OSSP UUID declarations cleanly resolves mixed declaration/statement warningsMoving
uuid_t *uuidanduuid_rc_t rcunder#ifdef HAVE_UUID_OSSPto the top ofora_sys_guidkeeps all declarations before the first statement while preserving the existing control flow and error handling. This is a safe, no-op refactor that should silence C89-style build warnings without changing behavior.src/fe_utils/string_utils.c (1)
105-116: fmtIdEnc Oracle fast-path reordering looks correct and avoids unnecessary workDeferring
getLocalPQExpBuffer()until after theDB_ORACLE == db_modecheck cleanly skips the shared buffer setup when the Oracle fast-path is taken, without introducing any uninitialized use ofid_returnfor non-Oracle paths. Behavior for non-Oracle callers remains unchanged; only the side effect of touching the PQExpBuffer in Oracle mode is removed, which is safe given the function’s early return ofora_fmtId(rawid).src/bin/psql/common.c (1)
3604-3619: LGTM: Loop variable renaming improves clarity.The renaming of the loop index from
itojis good practice, as it avoids potential confusion with theivariable used in the nested loop at line 3666. This change maintains the existing logic while improving code readability.src/pl/plisql/src/pl_handler.c (1)
228-234: LGTM!The
_PG_finifunction correctly pairs with_PG_initby callingplisql_unregister_internal_func()to clean up registered internal functions. This follows the established pattern used in other modules likeliboracle_parser.candivorysql_ora.c.Note: The forward declaration on line 228 could be placed with the other static function declarations near the top of the file for consistency, but this is a minor stylistic preference.
src/backend/utils/adt/arrayfuncs.c (1)
459-472: Consistent use ofereturnfor[m..n]missing-upper-bound error looks correctSwitching the
[m..n]“no digits” branch toereturn(escontext, false, ...)makes this path consistent with the[m:n]case and the other error sites inReadArrayDimensions(). It correctly routes the error throughescontextinstead of just returningfalse. No functional issues spotted.src/interfaces/libpq/ivy-exec.c (3)
3000-3002: Variable declarations properly initialized at function scope.The declarations of
pre,tmp, andfindare correctly initialized (NULL and 0 respectively) and used consistently throughout the function. This refactor maintains proper variable scope and initialization.
3766-3766: Loop variablejcorrectly renamed to avoid shadowing outeri.The inner loop at lines 3803–3809 properly uses
jas the string traversal index, distinct from the outer loop variableiused in the error-handling path (line 4037). Variable initialization and loop increment are correct. The SQL string escaping logic is preserved.Also applies to: 3790-3790, 3803-3809
4060-4060: Loop variablejcorrectly scoped to avoid conflicts with outer error-handling loop usingi.The inner loop at lines 4089–4094 (SQL string escaping) and the tuple iteration loop at lines 4236–4254 both correctly use
jas the loop index, distinct from outer variableiused in the error path (line 4309). Variable declarations, bounds checks, and array accesses are all correct.Also applies to: 4075-4075, 4089-4094, 4236-4236, 4242-4243
| if (defined($bki_values->{type_name}) | ||
| and $bki_values->{type_name} ne 'oracharchar' | ||
| and $bki_values->{type_name} ne 'oracharbyte' | ||
| and $bki_values->{type_name} ne 'oravarcharchar' | ||
| and $bki_values->{type_name} ne 'oravarcharbyte' | ||
| and $bki_values->{type_name} ne 'oradate' | ||
| and $bki_values->{type_name} ne 'oratimestamp' | ||
| and $bki_values->{type_name} ne 'oratimestamptz' | ||
| and $bki_values->{type_name} ne 'oratimestampltz' | ||
| and $bki_values->{type_name} ne 'yminterval' | ||
| and $bki_values->{type_name} ne 'dsinterval' | ||
| and $bki_values->{type_name} ne 'number' | ||
| and $bki_values->{type_name} ne 'binary_float' | ||
| and $bki_values->{type_name} ne 'binary_double' | ||
| ) | ||
| { | ||
| warn sprintf | ||
| "invalid zero OID reference in %s.dat field %s line %s\n", | ||
| $catname, $attname, $bki_values->{line_number} | ||
| if !$lookup_opt; | ||
| } |
There was a problem hiding this comment.
Zero‑OID warning is now completely suppressed when type_name is undefined
The new condition:
if (defined($bki_values->{type_name})
and $bki_values->{type_name} ne 'oracharchar'
...
and $bki_values->{type_name} ne 'binary_double')
{
warn sprintf "invalid zero OID reference ...\n"
if !$lookup_opt;
}means that for any row where type_name is not set (which is true for most catalogs), zero or '-' OID placeholders will no longer produce an "invalid zero OID reference" warning at all. That’s a broader behavioral change than “suppress warnings for these specific Oracle types” and may hide genuinely invalid zero OID references in other catalogs.
If the intent is “suppress only for the listed Oracle types, keep existing behavior everywhere else (including when type_name is absent)”, the condition should be inverted to treat the whitelist as exceptions, for example:
- if (defined($bki_values->{type_name})
- and $bki_values->{type_name} ne 'oracharchar'
- and $bki_values->{type_name} ne 'oracharbyte'
- and $bki_values->{type_name} ne 'oravarcharchar'
- and $bki_values->{type_name} ne 'oravarcharbyte'
- and $bki_values->{type_name} ne 'oradate'
- and $bki_values->{type_name} ne 'oratimestamp'
- and $bki_values->{type_name} ne 'oratimestamptz'
- and $bki_values->{type_name} ne 'oratimestampltz'
- and $bki_values->{type_name} ne 'yminterval'
- and $bki_values->{type_name} ne 'dsinterval'
- and $bki_values->{type_name} ne 'number'
- and $bki_values->{type_name} ne 'binary_float'
- and $bki_values->{type_name} ne 'binary_double'
- )
- {
- warn sprintf
- "invalid zero OID reference in %s.dat field %s line %s\n",
- $catname, $attname, $bki_values->{line_number}
- if !$lookup_opt;
- }
+ if (!$lookup_opt
+ && (!defined $bki_values->{type_name}
+ || ($bki_values->{type_name} ne 'oracharchar'
+ && $bki_values->{type_name} ne 'oracharbyte'
+ && $bki_values->{type_name} ne 'oravarcharchar'
+ && $bki_values->{type_name} ne 'oravarcharbyte'
+ && $bki_values->{type_name} ne 'oradate'
+ && $bki_values->{type_name} ne 'oratimestamp'
+ && $bki_values->{type_name} ne 'oratimestamptz'
+ && $bki_values->{type_name} ne 'oratimestampltz'
+ && $bki_values->{type_name} ne 'yminterval'
+ && $bki_values->{type_name} ne 'dsinterval'
+ && $bki_values->{type_name} ne 'number'
+ && $bki_values->{type_name} ne 'binary_float'
+ && $bki_values->{type_name} ne 'binary_double')))
+ {
+ warn sprintf
+ "invalid zero OID reference in %s.dat field %s line %s\n",
+ $catname, $attname, $bki_values->{line_number};
+ }This keeps the warning for all non‑whitelisted types and for rows without type_name, while silencing it only for the explicit Oracle compatibility types.
For readability/maintainability, you might also consider a small whitelist hash instead of a long chain of string comparisons:
my %zero_oid_ok_type = map { $_ => 1 } qw(
oracharchar oracharbyte oravarcharchar oravarcharbyte
oradate oratimestamp oratimestamptz oratimestampltz
yminterval dsinterval number binary_float binary_double
);
...
if (!$lookup_opt
&& (!defined $bki_values->{type_name}
|| !$zero_oid_ok_type{$bki_values->{type_name}}))
{
warn sprintf "invalid zero OID reference in %s.dat field %s line %s\n",
$catname, $attname, $bki_values->{line_number};
}🤖 Prompt for AI Agents
In src/backend/catalog/genbki.pl around lines 1121-1141, the current condition
requires type_name to be defined before warning, which suppresses zero‑OID
warnings for rows with no type_name; change the logic to treat the listed Oracle
types as exceptions instead of requiring type_name be present: warn on invalid
zero OID when lookup_opt is false and the type is not in the explicit whitelist
(or when type_name is undefined), i.e. build a small whitelist (hash or list) of
allowed type names and only skip the warning if the current type_name is in that
whitelist; update the conditional accordingly to restore warnings for rows
lacking type_name while silencing them for the explicit Oracle types.
Clean build warning and fix "invalid zero OID reference in pg_type.dat" issue
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.