From 950765d774e5b8bc6ba009a449bd2c9b22a51f79 Mon Sep 17 00:00:00 2001 From: Frank Patz-Brockmann Date: Fri, 19 Jun 2026 18:04:17 +0200 Subject: [PATCH 1/2] fix(postgres): preserve contained-by operator direction CODEX --- sqlglot/expressions/array.py | 4 ++++ sqlglot/generator.py | 1 + sqlglot/generators/mysql.py | 4 ++++ sqlglot/parser.py | 2 +- tests/dialects/test_duckdb.py | 4 ++-- tests/dialects/test_postgres.py | 5 ++--- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/sqlglot/expressions/array.py b/sqlglot/expressions/array.py index f8d265f097..913774bc99 100644 --- a/sqlglot/expressions/array.py +++ b/sqlglot/expressions/array.py @@ -114,6 +114,10 @@ class ArrayContainsAll(Expression, Binary, Func): _sql_names = ["ARRAY_CONTAINS_ALL", "ARRAY_HAS_ALL"] +class ArrayContainedBy(Expression, Binary, Func): + _sql_names = ["ARRAY_CONTAINED_BY"] + + class ArrayExcept(Expression, Func): arg_types = {"this": True, "expression": True, "is_multiset": False} diff --git a/sqlglot/generator.py b/sqlglot/generator.py index 43264fa33f..27d392a9a1 100644 --- a/sqlglot/generator.py +++ b/sqlglot/generator.py @@ -140,6 +140,7 @@ class Generator: ), exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"), exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "), + exp.ArrayContainedBy: lambda self, e: self.binary(e, "<@"), exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"), exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"), exp.AssumeColumnConstraint: lambda self, e: f"ASSUME ({self.sql(e, 'this')})", diff --git a/sqlglot/generators/mysql.py b/sqlglot/generators/mysql.py index 005f129420..21974021e3 100644 --- a/sqlglot/generators/mysql.py +++ b/sqlglot/generators/mysql.py @@ -653,6 +653,10 @@ def arraycontainsall_sql(self, expression: exp.ArrayContainsAll) -> str: self.unsupported("Array operations are not supported by MySQL") return self.function_fallback_sql(expression) + def arraycontainedby_sql(self, expression: exp.ArrayContainedBy) -> str: + self.unsupported("Array operations are not supported by MySQL") + return self.function_fallback_sql(expression) + def dpipe_sql(self, expression: exp.DPipe) -> str: return self.func("CONCAT", *expression.flatten()) diff --git a/sqlglot/parser.py b/sqlglot/parser.py index 85ac3d5407..d1e22ffa0b 100644 --- a/sqlglot/parser.py +++ b/sqlglot/parser.py @@ -1192,7 +1192,7 @@ class Parser: TokenType.IRLIKE: binary_range_parser(exp.RegexpILike), TokenType.IS: lambda self, this: self._parse_is(this), TokenType.LIKE: binary_range_parser(exp.Like), - TokenType.LT_AT: binary_range_parser(exp.ArrayContainsAll, reverse_args=True), + TokenType.LT_AT: binary_range_parser(exp.ArrayContainedBy), TokenType.OVERLAPS: binary_range_parser(exp.Overlaps), TokenType.RLIKE: binary_range_parser(exp.RegexpLike), TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo), diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py index 4ba526c09f..f48866a0dd 100644 --- a/tests/dialects/test_duckdb.py +++ b/tests/dialects/test_duckdb.py @@ -1350,8 +1350,8 @@ def test_duckdb(self): self.validate_identity("a ** b", "POWER(a, b)") self.validate_identity("a ~~~ b", "a GLOB b") self.validate_identity("a ~~ b", "a LIKE b") - self.validate_identity("a @> b") - self.validate_identity("a <@ b", "b @> a") + self.validate_identity("a @> b").assert_is(exp.ArrayContainsAll) + self.validate_identity("a <@ b").assert_is(exp.ArrayContainedBy) self.validate_identity("a && b").assert_is(exp.ArrayOverlaps) self.validate_identity("a ^@ b", "STARTS_WITH(a, b)") self.validate_identity( diff --git a/tests/dialects/test_postgres.py b/tests/dialects/test_postgres.py index 47cedbde03..99077113ea 100644 --- a/tests/dialects/test_postgres.py +++ b/tests/dialects/test_postgres.py @@ -213,9 +213,8 @@ def test_postgres(self): "SELECT SUBSTRING('Thomas' FOR 3 FROM 2)", "SELECT SUBSTRING('Thomas' FROM 2 FOR 3)", ) - self.validate_identity( - "SELECT ARRAY[1, 2, 3] <@ ARRAY[1, 2]", - "SELECT ARRAY[1, 2] @> ARRAY[1, 2, 3]", + self.validate_identity("SELECT ARRAY[1, 2, 3] <@ ARRAY[1, 2]").expressions[0].assert_is( + exp.ArrayContainedBy ) self.validate_identity( "SELECT DATE_PART('isodow'::varchar(6), current_date)", From cc50191d0d9a87aa8afb49fcd7881b11e03b4521 Mon Sep 17 00:00:00 2001 From: Jo <46752250+georgesittas@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:43:23 +0300 Subject: [PATCH 2/2] Update sqlglot/expressions/array.py --- sqlglot/expressions/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlglot/expressions/array.py b/sqlglot/expressions/array.py index 913774bc99..20601d8c58 100644 --- a/sqlglot/expressions/array.py +++ b/sqlglot/expressions/array.py @@ -115,7 +115,7 @@ class ArrayContainsAll(Expression, Binary, Func): class ArrayContainedBy(Expression, Binary, Func): - _sql_names = ["ARRAY_CONTAINED_BY"] + pass class ArrayExcept(Expression, Func):