ydb-sqlglot-plugin 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ydb-sqlglot-plugin
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: YDB dialect plugin for sqlglot
5
5
  Author: YDB Team
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ydb-sqlglot-plugin"
7
- version = "0.2.0" # AUTOVERSION
7
+ version = "0.2.2" # AUTOVERSION
8
8
  description = "YDB dialect plugin for sqlglot"
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}
@@ -0,0 +1 @@
1
+ VERSION = "0.2.2"
@@ -1,3 +1,4 @@
1
+ import inspect as _inspect
1
2
  import re
2
3
  import typing as t
3
4
 
@@ -403,26 +404,6 @@ def _apply_subquery_alias_columns(expression: exp.Expression) -> None:
403
404
  alias.set("columns", [])
404
405
 
405
406
 
406
- def _has_implicit_cross_join(expression: exp.Expression) -> bool:
407
- """Return True if the expression tree contains an implicit cross join.
408
-
409
- An implicit cross join arises from comma-separated FROM tables, e.g.
410
- ``FROM t1, t2``. In the sqlglot AST this appears as a ``Join`` node
411
- with no ``kind``, no ``on`` clause, and no ``using`` clause.
412
- YDB disables implicit cross joins by default; callers can prepend
413
- ``PRAGMA AnsiImplicitCrossJoin;`` when this returns True.
414
- """
415
- for node in expression.walk():
416
- if isinstance(node, exp.Join):
417
- if (
418
- not node.args.get("kind")
419
- and not node.args.get("on")
420
- and not node.args.get("using")
421
- ):
422
- return True
423
- return False
424
-
425
-
426
407
  class FlattenBy(exp.Expression):
427
408
  """YDB-specific FLATTEN [LIST|DICT] BY clause on a table reference."""
428
409
  arg_types = {"this": True, "expressions": True, "kind": False}
@@ -446,6 +427,13 @@ _YDB_GENERIC_TYPES = {
446
427
  }
447
428
 
448
429
 
430
+ # sqlglot >= 30.0.0 changed Parser.expression() to take a pre-built instance instead of
431
+ # (cls, **kwargs). Detect once so the YDB parser override below can support both APIs.
432
+ _EXPRESSION_TAKES_INSTANCE = (
433
+ "instance" in _inspect.signature(parser.Parser.expression).parameters
434
+ )
435
+
436
+
449
437
  def _reassemble_ctes(
450
438
  statements: t.List[t.Optional[exp.Expression]],
451
439
  ) -> t.List[t.Optional[exp.Expression]]:
@@ -602,6 +590,38 @@ class YDB(Dialect):
602
590
  statements = super().parse(raw_tokens, sql)
603
591
  return _reassemble_ctes(statements)
604
592
 
593
+ def expression(self, exp_class_or_instance, token=None, comments=None, **kwargs):
594
+ """Bridge sqlglot's two `Parser.expression()` calling conventions.
595
+
596
+ sqlglot < 30 expects ``expression(cls, **kwargs)`` and instantiates internally.
597
+ sqlglot >= 30 expects a pre-built ``expression(instance)`` and rejects kwargs.
598
+ Several call sites in this dialect (and a few in upstream code paths we exercise)
599
+ mix both styles, so normalise here before delegating.
600
+ """
601
+ if _EXPRESSION_TAKES_INSTANCE:
602
+ if not isinstance(exp_class_or_instance, exp.Expression):
603
+ exp_class_or_instance = exp_class_or_instance(**kwargs)
604
+ return super().expression(
605
+ exp_class_or_instance, token=token, comments=comments
606
+ )
607
+
608
+ if isinstance(exp_class_or_instance, exp.Expression):
609
+ # Old super() would attempt instance(**kwargs) -> "object is not callable".
610
+ instance = exp_class_or_instance
611
+ if token is not None:
612
+ update_positions = getattr(instance, "update_positions", None)
613
+ if update_positions is not None:
614
+ update_positions(token)
615
+ if comments:
616
+ instance.add_comments(comments)
617
+ else:
618
+ self._add_comments(instance)
619
+ return self.validate_expression(instance)
620
+
621
+ return super().expression(
622
+ exp_class_or_instance, token=token, comments=comments, **kwargs
623
+ )
624
+
605
625
  def _parse_dcolon(self) -> t.Optional[exp.Expression]:
606
626
  return self._parse_function(anonymous=True) or self._parse_var(any_token=True)
607
627
 
@@ -779,7 +799,9 @@ class YDB(Dialect):
779
799
 
780
800
  def _parse_lambda_body(self, params):
781
801
  if (
782
- self._curr.token_type != TokenType.R_PAREN
802
+ self._curr is None
803
+ or self._curr.token_type != TokenType.R_PAREN
804
+ or self._next is None
783
805
  or self._next.token_type != TokenType.ARROW
784
806
  ):
785
807
  return None
@@ -1384,12 +1406,6 @@ class YDB(Dialect):
1384
1406
  else:
1385
1407
  sql = self._generate_create_table(expression)
1386
1408
 
1387
- # Prepend PRAGMA AnsiImplicitCrossJoin only when the query contains
1388
- # implicit cross joins (FROM t1, t2 syntax). YDB disables them by
1389
- # default; the pragma restores standard SQL semantics.
1390
- if _has_implicit_cross_join(expression):
1391
- sql = "PRAGMA AnsiImplicitCrossJoin;\n" + sql
1392
-
1393
1409
  return sql
1394
1410
 
1395
1411
  def unnest_subqueries(self, expression):
@@ -2180,15 +2196,15 @@ class YDB(Dialect):
2180
2196
  # we move the WHERE expression from ON, using literals
2181
2197
  def join_sql(self, expression: exp.Join) -> str:
2182
2198
  on_condition = expression.args.get("on")
2183
- join_kind = expression.kind or ""
2184
-
2185
- # If LEFT/RIGHT/FULL JOIN has no ON clause, convert to CROSS JOIN
2186
- # YDB requires LEFT JOINs to have an ON clause
2187
- if not on_condition and any(
2188
- kind in join_kind.upper() for kind in ["LEFT", "RIGHT", "FULL", "OUTER", ""]
2189
- ):
2190
- expression.set("kind", None)
2191
- expression.set("on", None)
2199
+ using = expression.args.get("using")
2200
+
2201
+ # Any join with no ON/USING clause becomes an explicit CROSS JOIN.
2202
+ # YDB requires an ON clause for outer joins, and emitting CROSS JOIN
2203
+ # explicitly (instead of the comma-separated form) keeps the output
2204
+ # valid without any extra pragma.
2205
+ if not on_condition and not using:
2206
+ expression.set("kind", "CROSS")
2207
+ expression.set("side", None)
2192
2208
  return super().join_sql(expression)
2193
2209
 
2194
2210
  if on_condition:
@@ -2256,18 +2272,11 @@ class YDB(Dialect):
2256
2272
  on_condition = exp.and_(on_condition, cond)
2257
2273
  expression.set("on", on_condition)
2258
2274
  else:
2259
- # No valid equality conditions
2260
- # For LEFT/RIGHT/FULL JOINs, YDB requires ON clause, so convert to CROSS JOIN
2261
- join_kind = expression.side or ""
2262
- if any(
2263
- kind in join_kind.upper() for kind in ["LEFT", "RIGHT", "FULL", "OUTER"]
2264
- ):
2265
- # Convert to CROSS JOIN by removing kind and ON
2266
- expression.set("kind", None)
2267
- expression.set("on", None)
2268
- expression.set("side", "CROSS")
2269
- else:
2270
- expression.set("on", None)
2275
+ # No valid equality conditions remain on the JOIN — fall back
2276
+ # to an explicit CROSS JOIN regardless of the original kind.
2277
+ expression.set("kind", "CROSS")
2278
+ expression.set("on", None)
2279
+ expression.set("side", None)
2271
2280
 
2272
2281
  if conditions_to_move:
2273
2282
  select_stmt = expression.find_ancestor(exp.Select)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ydb-sqlglot-plugin
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: YDB dialect plugin for sqlglot
5
5
  Author: YDB Team
6
6
  License: Apache-2.0
@@ -1 +0,0 @@
1
- VERSION = "0.2.0"