Skip to content

[Bug] array_sort's second comparator argument takes the first argument's name: a nested lambda reads the wrong argument, and alias functions sort nothing #69919

Open

Description

@matanper

Search before asking

  • I had searched in the issues and found no similar issues.

Version

master and branch-4.1 (the translator code is the same on both). Reproduced on a 4.1 build that includes the nested-lambda capture changes (#68062, #68471).

What's wrong?

ExpressionTranslator builds the ColumnRef of array_sort's second comparator argument as a copy of the first one, and keeps the first argument's name:

ColumnRefExpr column2 = new ColumnRefExpr(column);
column2.setColumnId(1);

That causes two wrong results.

1. A nested lambda in the comparator reads the wrong argument. The BE identifies comparator arguments by name when it rejects a nested lambda that captures one, and both arguments are named after the first. So a nested lambda that re-declares the first argument's name and reads the second passes that check. At run time it reads its own argument instead, the comparator returns a constant, and the array comes back unsorted with no error:

select array_sort((x, y) -> if(array_contains(array_map(x -> x < y, array(x)), true), -1, 1), [3, 1, 2]);
-- returns [3, 1, 2]

2. Alias functions with an array_sort comparator sort nothing, even with a flat comparator. CREATE ALIAS FUNCTION stores the translated expression and rebuilds the function from it, so both comparator arguments come back under the first name, as (x, y) -> ... (x < x) ...:

CREATE ALIAS FUNCTION db.asc_sort(ARRAY<INT>) WITH PARAMETER(arr)
    AS array_sort((x, y) -> IF(x < y, -1, IF(x = y, 0, 1)), arr);
select db.asc_sort([3, 1, 2]);
-- returns [3, 1, 2]

The same comparator in a plain query returns [1, 2, 3].

What you expected to happen

  1. The first query is rejected, like any nested lambda that captures a comparator argument (array_sort comparator does not support nested lambda capturing comparator argument 'y').
  2. The alias returns [1, 2, 3].

How to reproduce

Run the statements above.

Anything else?

Fix: give the second ColumnRef its own name. The comparator frame is position-based (column ids 0 and 1), so flat comparators in queries are unaffected:

         arrayItemReference = lambda.getLambdaArgument(1);
         ColumnRefExpr column2 = new ColumnRefExpr(column);
+        column2.setName(arrayItemReference.getName());
         column2.setColumnId(1);

With that change, query 1 is rejected with the error above, and a flat comparator still sorts [3, 1, 2] to [1, 2, 3]. Aliases created before the fix keep the stored broken body until they are dropped and recreated.

Related: #68977

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. Doris-Breakwater commented on Oct 31, 2026

    @Doris-Breakwater
    Collaborator

    Breakwater-GitHub-Analysis-Slot: slot_69d75b8c6c1a

    The source supports treating this as a correctness bug in FE translation, with two downstream effects: an unsupported nested capture escapes validation, and alias reconstruction changes the comparator expression. Suggested ownership: Nereids/expression translation, with BE lambda regression coverage. The issue currently has no labels.

    I inspected local source snapshots of master at 2a4191c1dc94e789e1725d6cb345f46c31a17272 and branch-4.1 at 5fc51d21256d598dc89afe5732550b62baeac4a9. Both contain the missing rename. This is source verification; I have not independently executed the SQL or tested the proposed patch, and these snapshots do not establish the latest branch-tip status.

    Verified mechanism

    • visitArraySort copies the first ColumnRefExpr and changes only its column ID. The copy constructor preserves its name. Consequently, references to y carry (name=x, id=1), while references to x carry (name=x, id=0). The lambda declaration itself still retains x,y; the corruption is in the references. See translator and copy constructor.
    • The BE nested-capture guard accepts a reference whose name belongs to an enclosing nested lambda before checking whether it captures a comparator argument. Thus the incorrectly named y matches the inner x. Runtime name resolution also selects that inner binding. For the supplied non-null integers, this explains why x < y becomes an effective self-comparison and the comparator returns 1. This supports the reported wrong result; the exact returned order remains a runtime observation from the report. See capture guard and reference resolution.
    • Alias creation stores the translated expression. Reconstruction renders that expression as SQL and reparses it; ColumnRefExpr SQL rendering uses the name, losing the distinction supplied by IDs. The example therefore becomes comparisons of x with itself and returns zero for non-null values. See alias creation, SQL rendering, and alias reconstruction.

    Recommended next step

    The proposed column2.setName(arrayItemReference.getName()) addresses the verified metadata defect. Keep IDs 0/1 and the existing positional comparator frame. Update the misleading “just a placeholder” comment as well. Validate the patch on both target branches before selecting backports.

    Please include regression coverage for:

    1. The reported nested-shadowing query failing with the capture error naming y; also capture of x, and legal nested shadowing without comparator capture.
    2. A flat comparator sorting correctly in a plain query and in a newly created alias, including duplicate values and an alias reload/reconstruction check.
    3. FE translation preserving distinct reference names and IDs. Existing BE capture tests construct expression nodes directly, so they cannot alone catch this FE translation defect.

    The fix affects newly translated expressions; it does not repair already persisted alias bodies. Recreate affected aliases from their original intended DDL after upgrading. Until then, use an inline flat comparator, or ordinary array_sort(arr) for the ascending integer example.

    Remaining evidence needed

    Please provide the exact FE and BE build commits and before/after SQL outputs on the patched build, including the alias's rendered definition. No profile or broad log bundle is needed to establish this source defect. Also clarify the related-issue link: #68977 currently points to a cloud load/storage tracking issue, rather than a lambda issue. The supplied reproductions are otherwise sufficient to proceed with a focused PR.

  2. adarshomrey commented on Nov 1, 2026

    @adarshomrey

    Hi, I'd like to work on this. I'll start by reproducing the nested-lambda case and
    then fix the missing column name in visitArraySort with regression coverage.
    Is there anything I should keep in mind for the backport to branch-4.1?

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions