matrixorigin / matrixorigin/matrixone

[Compatibility]: row constructors cannot compare directly with multi-column scalar subqueries

Open
#28,295 0 comments 0 reactions 1 assignee Claimed by @aptend View on GitHub
kind/bug needs-triage
Dominant language
Go
Stars
1.9k
Forks
311
Avg merge
1d 3h
Merged PRs (30d)
768

Description

## Description

MatrixOne cannot compare a row constructor with a multi-column scalar subquery. MySQL 8.0 supports the comparison and applies normal scalar-subquery cardinality and NULL semantics. MatrixOne already supports the closely related row-valued `IN` and `= ANY` forms, so the failure is specific to direct scalar comparison.

## Environment

- Branch: `main`
- Commit: `74668fc075c965c462b30f10ae24a1c19c289cfd`
- Deployment: local single-CN `etc/launch/launch.toml`
- Comparison: MySQL 8.0.45
- Date: 2026-09-07

## Steps to reproduce

```sql
CREATE DATABASE row_scalar_repro;
USE row_scalar_repro;

CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT);
INSERT INTO t VALUES (1,1,5),(2,1,NULL),(3,2,8),(4,2,10);

SELECT (1,5) = (SELECT a,b FROM t WHERE id=1) AS v;
SELECT (1,5) = (SELECT a,b FROM t WHERE id=99) AS empty_v;
```

## Actual behavior

Both statements are rejected during expression binding:

```text
ERROR 20203 (HY000): invalid argument operator =, bad value [TUPLE TUPLE]
```

`<>` and a prepared form such as `SELECT (?,?) = (SELECT a,b ...)` are rejected in the same way.

## Expected behavior

MySQL-compatible results are:

- one matching row: `1`;
- an empty scalar subquery: `NULL`;
- a returned row containing NULL: SQL row-comparison NULL semantics;
- more than one returned row: error 1242, `Subquery returns more than 1 row`.

## Stability and controls

- `=`, `<>`, NULL-row, empty-row, multi-row, and prepared variants reproduced 3/3.
- MySQL returned the expected Boolean/NULL values and error 1242 in all three runs.
- `(a,b) IN (SELECT ...)` and `(a,b) = ANY (SELECT ...)` returned the same rows on MatrixOne and MySQL in all three runs.
- Read-only statements; subsequent queries remained usable.

## Code analysis

`baseBinder.bindComparisonExpr` expands a row comparison only when both AST operands are literal `tree.Tuple` nodes. A multi-column subquery is bound as a scalar-subquery expression whose type is TUPLE, so the generic function binder receives `[TUPLE TUPLE]` before `flatten_subquery.go` can generate per-column row comparisons. The quantified-subquery path already carries a `SubqueryRef.Child` list and calls `generateRowComparison`, which explains why `= ANY` works.

## Regression coverage

After implementation, cover direct `=`, `<>`, ordering comparisons and `<=>` where supported; one row, empty row, NULL fields, multi-row error, correlated input, type coercion, and SQL/binary prepared statements.

## Related

- No matching open or closed issue was found for direct row-constructor versus multi-column scalar-subquery comparison.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.