Repository navigation
fix(binder): map COPY FROM subquery columns by position - #1374
Merged
Merged
Conversation
added 2 commits
October 24, 2026 09:41
Adds issue1365.test, which copies node and relationship tables from subqueries whose RETURN list reuses one variable. Expected values follow the positional mapping of result columns onto the table schema. Signed-off-by: Tyagiquamar <347892697+Tyagiquamar@users.noreply.github.com>
Binder::bindQueryScanSource renamed the subquery result columns in place with setAlias. A RETURN list that binds the same variable twice resolves both positions to one shared Expression object, so the object kept only the last expected name and the copy binder, which looked columns up by that name, fell back to the column default. A reused primary key column then raised a NULL PK violation and a reused non-key column was inserted as NULL. Scan sources name their leading columns from the expected schema by position (file, object and parameter readers do this while validating the count, and bindQueryScanSource validates the count too), so bind each table property against that position and stop mutating the bound subquery expressions. Columns beyond the expected names are copy warnings and a source bound without expected names keeps its own, so both still resolve by name. Signed-off-by: Tyagiquamar <347892697+Tyagiquamar@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1365
Problem
COPY <table> FROM (subquery)raised a spurious NULL primary key error whenever the subqueryRETURNlist reused a variable:Binder::bindQueryScanSourcerenamed the subquery result columns in place withsetAlias.RETURN i, i, ibinds all three positions to the sameExpressionobject, so that object ended up holding only the last expected name (b).matchColumnExpressionthen looked the primary key column up by name, found nothing, and fell back to the column default, which is NULL for a key. The same defect inserts NULL instead of the value for any reused non-key column, which is why PR #1350's regression test had to writei + 0.BoundStatementResultalready documents why the name cannot be recovered from the expression: one column object can carry several names.Fix
Scan sources already align their columns with the expected schema by position. The file, object, table function and parameter binders name their bound columns from
expectedColumnNamesindex by index and validate the count, andbindQueryScanSourcevalidates the count too. So the copy binder now resolves each table property againstexpectedColumnNamesby position and no longer mutates the bound subquery expressions.Two cases keep matching by name, exactly as before:
IGNORE_ERRORScopy warning columns;SERIAL(dropped byskipPropertyInSchema), where the names the reader produced are all there is to match against.For every source that goes through the expected-schema renaming, positional and name resolution select the same column, so the change is only observable for a query source whose
RETURNreuses an expression.Test
test/test_files/issue/issue1365.testcovers node copy withRETURN i, i, iandRETURN i, 'Grace', i, the two workarounds from the issue, an aliasedRETURNlist,COPY r2 (a, ID) FROM (subquery)where the property order differs from the source order, and a relationship copy with a reused variable.Validation
No local build was run, this machine has no configured Ladybug C++ build, so the evidence comes from the CI Workflow dispatched on my fork. The workflow has
cancel-in-progress: trueper ref, so the two steps were dispatched on separate refs.75033d8: https://github.com/Tyagiquamar/ladybug/actions/runs/40061763673.minimal test (linux)fails with exactly one of 3181 tests,e2e_test_issue~issue1365.Issue1365CopyFromSubqueryDuplicateVariable, reportingCopy exception: Found NULL, which violates the non-null constraint of the primary key column.for both node cases, and rows1|/2|instead of1|1/2|2forCOPY r2 (a, ID).890feb0: https://github.com/Tyagiquamar/ladybug/actions/runs/40064897262.sanity checks,clang-formatandminimal test (linux)pass with100% tests passed, 0 tests failed out of 3181. Theminimal linux extension testjob has no cloud-storage secrets on a fork, so it says nothing about this change; upstream CI will run it.An intermediate revision of this fix asserted that the source column count equals the expected column count. CI caught that it is not true for
IGNORE_ERRORScopies, where warning columns are appended, and for a table whose only column isSERIAL, where there are no expected names at all. That is why the two cases above are handled explicitly.Formatting was checked with clang-format 19 locally; the repo pins 18, and the
clang-formatjob above is the authoritative check.