Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ default_language_version:
python: "3"
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v4.3.0
rev: v4.4.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
Expand All @@ -17,7 +17,7 @@ repos:
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: "v0.15.0"
rev: "v0.15.2"
hooks:
- id: ruff
args: ["--fix"]
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }]
name = "sqlspec"
readme = "README.md"
requires-python = ">=3.10, <4.0"
version = "0.39.0"
version = "0.40.0"

[project.urls]
Discord = "https://discord.gg/litestar"
Expand All @@ -49,15 +49,15 @@ flask = ["flask"]
fsspec = ["fsspec"]
litestar = ["litestar"]
msgspec = ["msgspec"]
mypyc = []
mypyc = ["sqlglot[c]"]
mysql-connector = ["mysql-connector-python"]
nanoid = ["fastnanoid>=0.4.1"]
obstore = ["obstore"]
opentelemetry = ["opentelemetry-instrumentation"]
oracledb = ["oracledb"]
orjson = ["orjson"]
pandas = ["pandas", "pyarrow"]
performance = ["sqlglot[rs]", "msgspec"]
performance = ["msgspec"]
polars = ["polars", "pyarrow"]
prometheus = ["prometheus-client"]
psqlpy = ["psqlpy"]
Expand Down Expand Up @@ -241,7 +241,7 @@ opt_level = "3" # Maximum optimization (0-3)
allow_dirty = true
commit = false
commit_args = "--no-verify"
current_version = "0.39.0"
current_version = "0.40.0"
ignore_missing_files = false
ignore_missing_version = false
message = "chore(release): bump to v{new_version}"
Expand Down
19 changes: 10 additions & 9 deletions sqlspec/adapters/spanner/dialect/_spangres.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

from sqlglot import exp
from sqlglot.dialects.postgres import Postgres
from sqlglot.tokens import TokenType
from sqlglot.tokenizer_core import TokenType

__all__ = ("Spangres",)


_ROW_DELETION_NAME = "ROW_DELETION_POLICY"
_TTL_MIN_COMPONENTS = 2

Expand All @@ -16,16 +17,16 @@ class SpangresParser(Postgres.Parser):
"""Parse Spanner row deletion policies."""

def _parse_property(self) -> exp.Expression:
if self._match_text_seq("ROW", "DELETION", "POLICY"): # type: ignore[no-untyped-call]
self._match(TokenType.L_PAREN) # type: ignore[no-untyped-call]
self._match_text_seq("OLDER_THAN") # type: ignore[no-untyped-call]
self._match(TokenType.L_PAREN) # type: ignore[no-untyped-call]
if self._match_text_seq("ROW", "DELETION", "POLICY"):
self._match(TokenType.L_PAREN)
self._match_text_seq("OLDER_THAN")
self._match(TokenType.L_PAREN)
column = cast("exp.Expression", self._parse_id_var())
self._match(TokenType.COMMA) # type: ignore[no-untyped-call]
self._match_text_seq("INTERVAL") # type: ignore[no-untyped-call]
self._match(TokenType.COMMA)
self._match_text_seq("INTERVAL")
interval = cast("exp.Expression", self._parse_expression())
self._match(TokenType.R_PAREN) # type: ignore[no-untyped-call]
self._match(TokenType.R_PAREN) # type: ignore[no-untyped-call]
self._match(TokenType.R_PAREN)
self._match(TokenType.R_PAREN)

return exp.Property(
this=exp.Literal.string(_ROW_DELETION_NAME), value=exp.Tuple(expressions=[column, interval])
Expand Down
32 changes: 16 additions & 16 deletions sqlspec/adapters/spanner/dialect/_spanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from sqlglot import exp
from sqlglot.dialects.bigquery import BigQuery
from sqlglot.tokens import TokenType
from sqlglot.tokenizer_core import TokenType

__all__ = ("Spanner",)

Expand Down Expand Up @@ -41,14 +41,14 @@ def _parse_table_parts(
"""Parse Spanner table options including interleaving metadata."""
table = super()._parse_table_parts(schema=schema, is_db_reference=is_db_reference, wildcard=wildcard)

if self._match_text_seq("INTERLEAVE", "IN", "PARENT"): # type: ignore[no-untyped-call]
if self._match_text_seq("INTERLEAVE", "IN", "PARENT"):
parent = cast("exp.Expression", self._parse_table(schema=True, is_db_reference=True))
on_delete: str | None = None

if self._match_text_seq("ON", "DELETE"): # type: ignore[no-untyped-call]
if self._match_text_seq("CASCADE"): # type: ignore[no-untyped-call]
if self._match_text_seq("ON", "DELETE"):
if self._match_text_seq("CASCADE"):
on_delete = "CASCADE"
elif self._match_text_seq("NO", "ACTION"): # type: ignore[no-untyped-call]
elif self._match_text_seq("NO", "ACTION"):
on_delete = "NO ACTION"

table.set("interleave_parent", parent)
Expand All @@ -59,25 +59,25 @@ def _parse_table_parts(

def _parse_property(self) -> exp.Expression:
"""Parse Spanner row deletion policy or PostgreSQL-style TTL."""
if self._match_text_seq("ROW", "DELETION", "POLICY"): # type: ignore[no-untyped-call]
self._match(TokenType.L_PAREN) # type: ignore[no-untyped-call]
self._match_text_seq("OLDER_THAN") # type: ignore[no-untyped-call]
self._match(TokenType.L_PAREN) # type: ignore[no-untyped-call]
if self._match_text_seq("ROW", "DELETION", "POLICY"):
self._match(TokenType.L_PAREN)
self._match_text_seq("OLDER_THAN")
self._match(TokenType.L_PAREN)
column = cast("exp.Expression", self._parse_id_var())
self._match(TokenType.COMMA) # type: ignore[no-untyped-call]
self._match_text_seq("INTERVAL") # type: ignore[no-untyped-call]
self._match(TokenType.COMMA)
self._match_text_seq("INTERVAL")
interval = cast("exp.Expression", self._parse_expression())
self._match(TokenType.R_PAREN) # type: ignore[no-untyped-call]
self._match(TokenType.R_PAREN) # type: ignore[no-untyped-call]
self._match(TokenType.R_PAREN)
self._match(TokenType.R_PAREN)

return exp.Property(
this=exp.Literal.string(_ROW_DELETION_NAME), value=exp.Tuple(expressions=[column, interval])
)

if self._match_text_seq("TTL"): # type: ignore[no-untyped-call] # PostgreSQL-dialect style, keep for compatibility
self._match_text_seq("INTERVAL") # type: ignore[no-untyped-call]
if self._match_text_seq("TTL"):
self._match_text_seq("INTERVAL")
interval = cast("exp.Expression", self._parse_expression())
self._match_text_seq("ON") # type: ignore[no-untyped-call]
self._match_text_seq("ON")
column = cast("exp.Expression", self._parse_id_var())

return exp.Property(this=exp.Literal.string("TTL"), value=exp.Tuple(expressions=[interval, column]))
Expand Down
12 changes: 3 additions & 9 deletions sqlspec/builder/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ def _parameterize_expression(self, expression: exp.Expression) -> exp.Expression
A new expression with literals replaced by parameter placeholders
"""

return expression.transform(_ExpressionParameterizer(self), copy=False)
return cast("exp.Expression", expression.transform(_ExpressionParameterizer(self), copy=False))

def add_parameter(self: Self, value: Any, name: str | None = None) -> tuple[Self, str]:
"""Explicitly adds a parameter to the query.
Expand Down Expand Up @@ -426,9 +426,6 @@ def load_parameters(self, parameters: "Mapping[str, Any]") -> None:

Args:
parameters: Mapping of parameter names to values.

Raises:
SQLBuilderError: If a parameter name already exists on the builder.
"""
if not parameters:
return
Expand All @@ -444,9 +441,6 @@ def load_ctes(self, ctes: "Iterable[exp.CTE]") -> None:

Args:
ctes: Iterable of CTE expressions to register.

Raises:
SQLBuilderError: If a CTE alias is missing or duplicated.
"""
for cte in ctes:
alias = self._resolve_cte_alias(cte)
Expand Down Expand Up @@ -543,7 +537,7 @@ def _update_placeholders_in_expression(
Updated expression with new placeholder names
"""

return expression.transform(_PlaceholderReplacer(param_mapping), copy=False)
return cast("exp.Expression", expression.transform(_PlaceholderReplacer(param_mapping), copy=False))

def _generate_builder_cache_key(self, config: "StatementConfig | None" = None) -> str:
"""Generate cache key based on builder state and configuration.
Expand Down Expand Up @@ -893,7 +887,7 @@ def _is_oracle_dialect(self, dialect: "DialectType | str | None") -> bool:
def _unquote_identifiers_for_oracle(self, expression: exp.Expression) -> exp.Expression:
"""Remove identifier quoting to avoid Oracle case-sensitive lookup issues."""

return expression.copy().transform(_unquote_identifier, copy=False)
return cast("exp.Expression", expression.copy().transform(_unquote_identifier, copy=False))

def _strip_lock_identifier_quotes(self, sql_string: str) -> str:
for keyword in ("FOR UPDATE OF ", "FOR SHARE OF "):
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/adapters/test_spanner/test_dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def test_dialect_inheritance() -> None:

def test_dialect_keywords() -> None:
"""Test that Spanner-specific keywords are registered."""
from sqlglot.tokens import TokenType
from sqlglot.tokenizer_core import TokenType

# Keywords are only registered if sqlglot supports them
keywords = Spanner.Tokenizer.KEYWORDS
Expand Down
5 changes: 3 additions & 2 deletions tests/unit/builder/test_temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ def test_join_as_of_dialect() -> None:
sql_str = query.build(dialect="bigquery").sql
normalized = normalize_sql(sql_str)
# Should use FOR SYSTEM_TIME AS OF because dialect is passed at build time
assert "LEFT JOIN audit_log FOR SYSTEM_TIME AS OF '-1h'" in normalized
assert "log ON orders.id = log.order_id" in normalized
assert "LEFT JOIN audit_log" in normalized
assert "FOR SYSTEM_TIME AS OF '-1h'" in normalized
assert "ON orders.id = log.order_id" in normalized


def test_join_as_of_dialect_override() -> None:
Expand Down
Loading
Loading