Skip to content

Clean build warning and fix "invalid zero OID reference in pg_type.dat" issue#993

Merged
gaoxueyu merged 2 commits intoIvorySQL:masterfrom
bigplaice:clean_warning
Dec 3, 2025
Merged

Clean build warning and fix "invalid zero OID reference in pg_type.dat" issue#993
gaoxueyu merged 2 commits intoIvorySQL:masterfrom
bigplaice:clean_warning

Conversation

@bigplaice
Copy link
Copy Markdown
Collaborator

@bigplaice bigplaice commented Dec 2, 2025

Clean build warning and fix "invalid zero OID reference in pg_type.dat" issue

Summary by CodeRabbit

  • New Features

    • Added module unload cleanup function for proper extension lifecycle management during module unload.
  • Bug Fixes

    • Improved error handling and reporting in array dimension parsing.
  • Refactor

    • Internal code reorganization including variable name standardization and declaration reordering across multiple modules to improve code clarity.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Walkthrough

This 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

Cohort / File(s) Summary
Variable Declaration and Initialization Refactoring
src/backend/utils/adt/ruleutils.c, src/bin/psql/common.c, src/interfaces/libpq/ivy-exec.c, src/pl/plisql/src/pl_comp.c
Renamed local loop index variables (relid → relid_1, i → j, argtypenames indexing) and adjusted initialization statements for consistency; behavior remains functionally identical with improved variable naming clarity.
Error Handling and Reporting
src/backend/utils/adt/arrayfuncs.c
Changed error handling in ReadArrayDimensions from return(escontext, ...) to ereturn(escontext, ...), altering error routing through ErrorSaveContext while preserving error payload.
Conditional Warning Suppression Logic
src/backend/catalog/genbki.pl
Added selective warning suppression in lookup_oids for specific Oracle/internal column types (oracharchar, oradate, number, etc.) when encountering zero/dash OIDs; other types retain warnings.
Module Lifecycle Management
src/pl/plisql/src/pl_handler.c
Added _PG_fini(void) function to unregister internal PL/iSQL routines during module unload, complementing existing _PG_init().
PL/iSQL Scanner State
src/pl/plisql/src/pl_scanner.c
Commented out global scanner handle and line number tracking variables (yyscan_t plisql_scanner, cur_line_num).
Subprocedure Variable Declaration
src/pl/plisql/src/pl_subproc_function.c
Removed local declaration of argvariable by commenting out; subsequent code references this identifier.
Declaration Relocation with Preprocessor Guards
contrib/uuid-ossp/uuid-ossp.c
Relocated variable declarations (uuid, rc) to top of ora_sys_guid function within #ifdef HAVE_UUID_OSSP; added EOF newline.
String Utility Initialization Reordering
src/fe_utils/string_utils.c
Reordered initialization in fmtIdEnc to allocate id_return only after confirming non-Oracle path; logic remains unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • src/pl/plisql/src/pl_subproc_function.c: Removed argvariable declaration while code still references it—potential compilation error requiring immediate clarification.
  • src/backend/catalog/genbki.pl: Conditional warning suppression logic needs verification that type name set is complete and warnings are appropriately silenced only for intended cases.
  • src/pl/plisql/src/pl_handler.c: Verify _PG_fini() cleanup function is correctly registered and called on module unload; ensure no resource leaks.
  • Variable renamings across multiple files: While mostly mechanical, cross-file consistency should be checked to ensure no accidental shadowing or misaligned references.

Possibly related PRs

Suggested reviewers

  • jiaoshuntian
  • OreoYang
  • NotHimmel

Poem

🐰 Variables hop and declarations flow,
Loop indices renamed, cleanly so!
Module unload now has its mate,
Refactored code, organized state!
Twitches nose — this PR's great! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main objectives: cleaning build warnings and fixing the invalid zero OID reference issue, which aligns with the core changes across multiple files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 %TYPE references

The new use of RangeVarGetRelid(rel, NoLock, true) means relid_1 can legitimately be InvalidOid when 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 return InvalidAttrNumber, 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, attnum stays InvalidAttrNumber and is_valid_type remains false, yet the function still calls tuplestore_putvalues(*tupstore, tupdesc, values, nulls); with values[] 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:

  1. Only look up the attribute when relid_1 is valid, and keep is_valid_type false otherwise:
-        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;
+				}
+			}
  1. 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 new protypenames logic leaks temporaries and assumes array length matches pronargs

The idea of deriving hashkey->argtypes[i] from pg_proc.protypenames (via TypeName/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:

  1. Unfreed allocations in a potentially long‑lived context

Inside the if (procStruct->pronargs > 0) block:

  • deconstruct_array allocates elems.
  • argtypenames = (char **) palloc(sizeof(char *) * nelems);
  • Each TextDatumGetCString(elems[i]) allocates a char *.

None of elems, argtypenames, or the per‑element strings are freed in this function. If compute_function_hashkey runs in a long‑lived context (e.g., TopMemoryContext or fn_mcxt via the call from plisql_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 usual deconstruct_array pattern.)

  1. Implicit assumption that nelems == procStruct->pronargs

The loop iterates i < procStruct->pronargs but argtypenames has length nelems. The catalog should normally guarantee nelems == pronargs when protypenames is non‑NULL, but if that invariant is ever broken (corrupt row, partially initialized entry, etc.), you’d read past the end of argtypenames.

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 i at line 3698 is defensive programming
  • Using j for the inner loop (line 3819) avoids shadowing the outer i variable (used at line 4007)
  • Proper variable scoping with j declared within the nested block at line 3805

These refactoring changes likely address compiler warnings about variable shadowing while maintaining correct logic.

Optional: The initialization of i = 0 at line 3698 is redundant since the for-loop at line 4007 re-initializes it. You could change it to just int 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 yyextra structure 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: Reusing argtypenames/rettypename is fine; consider tightening lifetime of the first set of names

Resetting the outer argtypenames/rettypename and using a dedicated loop index j removes the variable shadowing and keeps the logic of building plisql_referenced_objects intact. No functional issues here.

One thing to be aware of: get_func_typename_info(procTup, &argtypenames, &rettypename) is called earlier in do_compile (around the argument‑building logic) and its results are never freed; here you invoke it a second time, then only pfree(argtypenames) and pfree(rettypename) from the second call. The strings produced by TextDatumGetCString in 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/rettypename here 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

📥 Commits

Reviewing files that changed from the base of the PR and between b1caefd and 6b23023.

📒 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 warnings

Moving uuid_t *uuid and uuid_rc_t rc under #ifdef HAVE_UUID_OSSP to the top of ora_sys_guid keeps 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 work

Deferring getLocalPQExpBuffer() until after the DB_ORACLE == db_mode check cleanly skips the shared buffer setup when the Oracle fast-path is taken, without introducing any uninitialized use of id_return for 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 of ora_fmtId(rawid).

src/bin/psql/common.c (1)

3604-3619: LGTM: Loop variable renaming improves clarity.

The renaming of the loop index from i to j is good practice, as it avoids potential confusion with the i variable 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_fini function correctly pairs with _PG_init by calling plisql_unregister_internal_func() to clean up registered internal functions. This follows the established pattern used in other modules like liboracle_parser.c and ivorysql_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 of ereturn for [m..n] missing-upper-bound error looks correct

Switching the [m..n] “no digits” branch to ereturn(escontext, false, ...) makes this path consistent with the [m:n] case and the other error sites in ReadArrayDimensions(). It correctly routes the error through escontext instead of just returning false. 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, and find are correctly initialized (NULL and 0 respectively) and used consistently throughout the function. This refactor maintains proper variable scope and initialization.


3766-3766: Loop variable j correctly renamed to avoid shadowing outer i.

The inner loop at lines 3803–3809 properly uses j as the string traversal index, distinct from the outer loop variable i used 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 variable j correctly scoped to avoid conflicts with outer error-handling loop using i.

The inner loop at lines 4089–4094 (SQL string escaping) and the tuple iteration loop at lines 4236–4254 both correctly use j as the loop index, distinct from outer variable i used 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

Comment on lines +1121 to +1141
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;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants