Skip to content

Commit e7f67ae

Browse files
committed
fix: remove redundant Postgres-specific expression subclasses, add docstrings, fix lint
- Remove PostgresCreateIndexExpression, PostgresDropIndexExpression, PostgresForUpdateClause — use generic expressions with dialect_options - Add comprehensive docstrings to all format_* methods in PG mixins - Fix ruff D413 (blank line after docstring sections) + E501 (line length) - Update tests to use generic expressions + dialect_options - Remove obsolete protocol conformance entries
1 parent d0c9e5f commit e7f67ae

23 files changed

Lines changed: 655 additions & 310 deletions

File tree

‎src/rhosocial/activerecord/backend/impl/postgres/dialect.py‎

Lines changed: 164 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ def __init__(self, version: Optional[Tuple[int, int, int]] = None):
436436
If None, the dialect must be adapted via
437437
backend.introspect_and_adapt() before version-dependent
438438
features can be used.
439+
439440
"""
440441
super().__init__()
441442
if version is not None:
@@ -532,10 +533,17 @@ def format_explain_statement(self, explain_expr: "ExplainExpression") -> tuple:
532533
533534
PostgreSQL syntax: ``EXPLAIN [ ( option [, ...] ) ] statement``
534535
535-
Supported options assembled here:
536+
Supported options:
536537
- ``ANALYZE``
537538
- ``FORMAT { TEXT | XML | JSON | YAML }``
538-
- ``VERBOSE`` (passed through ``ExplainOptions.verbose`` if present)
539+
- ``QUERY PLAN`` type — silently omitted (plain EXPLAIN is equivalent).
540+
541+
Args:
542+
explain_expr: ExplainExpression instance
543+
544+
Returns:
545+
Tuple of (SQL string, params tuple)
546+
539547
"""
540548
from rhosocial.activerecord.backend.expression.statements import ExplainType
541549

@@ -591,6 +599,7 @@ def supports_lock_strength(self, strength) -> bool:
591599
592600
Returns:
593601
True if the lock strength is supported, False otherwise
602+
594603
"""
595604
from rhosocial.activerecord.backend.impl.postgres.expression.locking import LockStrength
596605

@@ -628,6 +637,7 @@ def get_upsert_syntax_type(self) -> str:
628637
629638
Returns:
630639
'ON CONFLICT' (PostgreSQL) or 'ON DUPLICATE KEY' (MySQL)
640+
631641
"""
632642
return "ON CONFLICT"
633643

@@ -681,7 +691,17 @@ def supports_ilike(self) -> bool:
681691
return True
682692

683693
def format_ilike_expression(self, column: Any, pattern: str, negate: bool = False) -> Tuple[str, Tuple]:
684-
"""Format ILIKE expression for PostgreSQL."""
694+
"""Format ILIKE expression for PostgreSQL.
695+
696+
Args:
697+
column: Column name string or expression with ``to_sql()``.
698+
pattern: Right-hand-side pattern (replaced with ``%s`` placeholder).
699+
negate: If ``True``, produces ``NOT ILIKE`` instead of ``ILIKE``.
700+
701+
Returns:
702+
Tuple of (SQL string, (pattern,) params tuple)
703+
704+
"""
685705
if isinstance(column, str):
686706
col_sql = self.format_identifier(column)
687707
else:
@@ -745,7 +765,19 @@ def supports_truncate_cascade(self) -> bool:
745765
return True
746766

747767
def format_truncate_statement(self, expr: "TruncateExpression") -> Tuple[str, tuple]:
748-
"""Format TRUNCATE statement for PostgreSQL."""
768+
"""Format TRUNCATE statement for PostgreSQL.
769+
770+
- ``expr.table_name`` — target table.
771+
- ``expr.restart_identity`` — add ``RESTART IDENTITY`` (PG 8.4+).
772+
- ``expr.cascade`` — add ``CASCADE``.
773+
774+
Args:
775+
expr: TruncateExpression instance
776+
777+
Returns:
778+
Tuple of (SQL string, empty params tuple)
779+
780+
"""
749781
parts = ["TRUNCATE TABLE"]
750782
parts.append(self.format_identifier(expr.table_name))
751783

@@ -769,6 +801,7 @@ def format_identifier(self, identifier: str) -> str:
769801
770802
Returns:
771803
Quoted identifier with escaped internal quotes
804+
772805
"""
773806
# Escape any internal double quotes by doubling them
774807
escaped = identifier.replace('"', '""')
@@ -792,7 +825,11 @@ def format_column(self, name: str, table: Optional[str] = None,
792825
# In PostgreSQL, when a table has an alias, column references
793826
# must use the alias — schema_name is irrelevant in this context.
794827
if schema_name and not alias:
795-
col_sql = f"{self.format_identifier(schema_name)}.{self.format_identifier(table)}.{self.format_identifier(name)}"
828+
col_sql = (
829+
f"{self.format_identifier(schema_name)}."
830+
f"{self.format_identifier(table)}."
831+
f"{self.format_identifier(name)}"
832+
)
796833
else:
797834
col_sql = f"{self.format_identifier(table)}.{self.format_identifier(name)}"
798835
else:
@@ -809,6 +846,18 @@ def format_on_conflict_clause(self, expr) -> Tuple[str, tuple]:
809846
Overrides the base implementation to handle EXCLUDED pseudo-table
810847
references without quoting, as EXCLUDED is a special PostgreSQL
811848
keyword in ON CONFLICT context and must not be double-quoted.
849+
850+
- ``expr.conflict_target`` — optional list of column names / expressions.
851+
- ``expr.do_nothing`` — ``DO NOTHING``.
852+
- ``expr.update_assignments`` — dict of ``{column: expression}`` for ``DO UPDATE SET``.
853+
- ``expr.update_where`` — optional WHERE predicate for the update.
854+
855+
Args:
856+
expr: OnConflictClause expression instance
857+
858+
Returns:
859+
Tuple of (SQL string, params tuple)
860+
812861
"""
813862
from rhosocial.activerecord.backend.expression import bases
814863
from rhosocial.activerecord.backend.expression.core import Column
@@ -904,7 +953,22 @@ def supports_cascade_view(self) -> bool:
904953
return True # PostgreSQL supports CASCADE
905954

906955
def format_create_view_statement(self, expr: "CreateViewExpression") -> Tuple[str, tuple]:
907-
"""Format CREATE VIEW statement for PostgreSQL."""
956+
"""Format CREATE VIEW statement for PostgreSQL.
957+
958+
- ``expr.temporary`` — add ``TEMPORARY``.
959+
- ``expr.replace`` — add ``OR REPLACE``.
960+
- ``expr.view_name`` — view name (identifier).
961+
- ``expr.column_aliases`` — optional list of column aliases.
962+
- ``expr.query`` — source SELECT expression.
963+
- ``expr.options.check_option`` — ``WITH {LOCAL|CASCADED} CHECK OPTION``.
964+
965+
Args:
966+
expr: CreateViewExpression instance
967+
968+
Returns:
969+
Tuple of (SQL string, params tuple)
970+
971+
"""
908972
parts = ["CREATE"]
909973

910974
if expr.temporary:
@@ -930,7 +994,19 @@ def format_create_view_statement(self, expr: "CreateViewExpression") -> Tuple[st
930994
return " ".join(parts), query_params
931995

932996
def format_drop_view_statement(self, expr: "DropViewExpression") -> Tuple[str, tuple]:
933-
"""Format DROP VIEW statement for PostgreSQL."""
997+
"""Format DROP VIEW statement for PostgreSQL.
998+
999+
- ``expr.if_exists`` — add ``IF EXISTS``.
1000+
- ``expr.view_name`` — view name (identifier).
1001+
- ``expr.cascade`` — add ``CASCADE``.
1002+
1003+
Args:
1004+
expr: DropViewExpression instance
1005+
1006+
Returns:
1007+
Tuple of (SQL string, empty params tuple)
1008+
1009+
"""
9341010
parts = ["DROP VIEW"]
9351011
if expr.if_exists:
9361012
parts.append("IF EXISTS")
@@ -940,7 +1016,22 @@ def format_drop_view_statement(self, expr: "DropViewExpression") -> Tuple[str, t
9401016
return " ".join(parts), ()
9411017

9421018
def format_create_materialized_view_statement(self, expr: "CreateMaterializedViewExpression") -> Tuple[str, tuple]:
943-
"""Format CREATE MATERIALIZED VIEW statement for PostgreSQL."""
1019+
"""Format CREATE MATERIALIZED VIEW statement for PostgreSQL.
1020+
1021+
- ``expr.view_name`` — view name (identifier).
1022+
- ``expr.column_aliases`` — optional list of column aliases.
1023+
- ``expr.tablespace`` — optional tablespace.
1024+
- ``expr.storage_options`` — optional dict of storage parameters (``WITH (… )``).
1025+
- ``expr.query`` — source SELECT expression.
1026+
- ``expr.with_data`` — ``WITH DATA`` / ``WITH NO DATA``.
1027+
1028+
Args:
1029+
expr: CreateMaterializedViewExpression instance
1030+
1031+
Returns:
1032+
Tuple of (SQL string, params tuple)
1033+
1034+
"""
9441035
parts = ["CREATE MATERIALIZED VIEW"]
9451036
parts.append(self.format_identifier(expr.view_name))
9461037

@@ -968,7 +1059,19 @@ def format_create_materialized_view_statement(self, expr: "CreateMaterializedVie
9681059
return " ".join(parts), query_params
9691060

9701061
def format_drop_materialized_view_statement(self, expr: "DropMaterializedViewExpression") -> Tuple[str, tuple]:
971-
"""Format DROP MATERIALIZED VIEW statement for PostgreSQL."""
1062+
"""Format DROP MATERIALIZED VIEW statement for PostgreSQL.
1063+
1064+
- ``expr.if_exists`` — add ``IF EXISTS``.
1065+
- ``expr.view_name`` — view name (identifier).
1066+
- ``expr.cascade`` — add ``CASCADE``.
1067+
1068+
Args:
1069+
expr: DropMaterializedViewExpression instance
1070+
1071+
Returns:
1072+
Tuple of (SQL string, empty params tuple)
1073+
1074+
"""
9721075
parts = ["DROP MATERIALIZED VIEW"]
9731076
if expr.if_exists:
9741077
parts.append("IF EXISTS")
@@ -980,7 +1083,19 @@ def format_drop_materialized_view_statement(self, expr: "DropMaterializedViewExp
9801083
def format_refresh_materialized_view_statement(
9811084
self, expr: "RefreshMaterializedViewExpression"
9821085
) -> Tuple[str, tuple]:
983-
"""Format REFRESH MATERIALIZED VIEW statement for PostgreSQL."""
1086+
"""Format REFRESH MATERIALIZED VIEW statement for PostgreSQL.
1087+
1088+
- ``expr.concurrent`` — add ``CONCURRENTLY`` (PG 9.4+).
1089+
- ``expr.view_name`` — view name (identifier).
1090+
- ``expr.with_data`` — ``WITH DATA`` / ``WITH NO DATA``.
1091+
1092+
Args:
1093+
expr: RefreshMaterializedViewExpression instance
1094+
1095+
Returns:
1096+
Tuple of (SQL string, empty params tuple)
1097+
1098+
"""
9841099
parts = ["REFRESH MATERIALIZED VIEW"]
9851100
if expr.concurrent and self.supports_materialized_view_concurrent_refresh():
9861101
parts.append("CONCURRENTLY")
@@ -1210,6 +1325,7 @@ def format_create_table_statement(self, expr: "CreateTableExpression") -> Tuple[
12101325
12111326
Returns:
12121327
Tuple of (SQL string, parameters tuple)
1328+
12131329
"""
12141330
# Check for LIKE syntax in dialect_options (highest priority)
12151331
if "like_table" in expr.dialect_options:
@@ -1303,6 +1419,7 @@ def format_cast_expression(
13031419
For chained type conversions, each ::type is appended:
13041420
>>> col.cast('money').cast('numeric').cast('float8')
13051421
# Generates: col::money::numeric::float8
1422+
13061423
"""
13071424
sql = f"{expr_sql}::{target_type}"
13081425
if alias:
@@ -1391,6 +1508,7 @@ def _format_exclude_constraint(
13911508
}
13921509
)
13931510
# Generates: EXCLUDE USING gist (range WITH &&)
1511+
13941512
"""
13951513
params: list = []
13961514

@@ -1488,6 +1606,7 @@ def supports_functions(self) -> Dict[str, "FunctionSupportInfo"]:
14881606
14891607
Returns:
14901608
Dict mapping function names to FunctionSupportInfo.
1609+
14911610
"""
14921611
from .function_versions import FunctionSupportInfo
14931612
from rhosocial.activerecord.backend.expression.functions import (
@@ -1514,6 +1633,7 @@ def _check_function_support(self, func_name: str) -> "FunctionSupportInfo":
15141633
15151634
Returns:
15161635
FunctionSupportInfo with support status and reason if unsupported
1636+
15171637
"""
15181638
from .function_versions import FunctionSupportInfo
15191639

@@ -1563,6 +1683,7 @@ def _is_postgres_function_supported(self, func_name: str) -> bool:
15631683
15641684
Returns:
15651685
True if supported, False otherwise
1686+
15661687
"""
15671688
requirement = self._POSTGRES_FUNCTION_VERSIONS.get(func_name)
15681689
if requirement is None:
@@ -1672,7 +1793,17 @@ def format_begin_transaction(
16721793
def format_commit_transaction(
16731794
self, expr: "CommitTransactionExpression"
16741795
) -> Tuple[str, tuple]:
1675-
"""Format COMMIT TRANSACTION statement for PostgreSQL."""
1796+
"""Format COMMIT TRANSACTION statement for PostgreSQL.
1797+
1798+
Always returns ``COMMIT``; ``expr`` is ignored.
1799+
1800+
Args:
1801+
expr: CommitTransactionExpression instance (unused)
1802+
1803+
Returns:
1804+
Tuple of ("COMMIT", ())
1805+
1806+
"""
16761807
return "COMMIT", ()
16771808

16781809
def format_rollback_transaction(
@@ -1691,15 +1822,35 @@ def format_rollback_transaction(
16911822
def format_savepoint(
16921823
self, expr: "SavepointExpression"
16931824
) -> Tuple[str, tuple]:
1694-
"""Format SAVEPOINT statement for PostgreSQL."""
1825+
"""Format SAVEPOINT statement for PostgreSQL.
1826+
1827+
``expr.get_params()["name"]`` — savepoint name (identifier).
1828+
1829+
Args:
1830+
expr: SavepointExpression instance
1831+
1832+
Returns:
1833+
Tuple of (SQL string, empty params tuple)
1834+
1835+
"""
16951836
params = expr.get_params()
16961837
name = params.get("name", "")
16971838
return f"SAVEPOINT {self.format_identifier(name)}", ()
16981839

16991840
def format_release_savepoint(
17001841
self, expr: "ReleaseSavepointExpression"
17011842
) -> Tuple[str, tuple]:
1702-
"""Format RELEASE SAVEPOINT statement for PostgreSQL."""
1843+
"""Format RELEASE SAVEPOINT statement for PostgreSQL.
1844+
1845+
``expr.get_params()["name"]`` — savepoint name (identifier).
1846+
1847+
Args:
1848+
expr: ReleaseSavepointExpression instance
1849+
1850+
Returns:
1851+
Tuple of (SQL string, empty params tuple)
1852+
1853+
"""
17031854
params = expr.get_params()
17041855
name = params.get("name", "")
17051856
return f"RELEASE SAVEPOINT {self.format_identifier(name)}", ()

‎src/rhosocial/activerecord/backend/impl/postgres/expression/__init__.py‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,13 @@
1818
PostgresTryAdvisoryLockExpression,
1919
AdvisoryLockType,
2020
)
21-
from .locking import LockStrength, PostgresForUpdateClause
21+
from .locking import LockStrength
2222
from .ddl import (
2323
PostgresVacuumExpression,
2424
PostgresAnalyzeExpression,
2525
PostgresCreatePartitionExpression,
2626
PostgresDetachPartitionExpression,
2727
PostgresAttachPartitionExpression,
28-
PostgresCreateIndexExpression,
29-
PostgresDropIndexExpression,
3028
PostgresAlterIndexExpression,
3129
PostgresAlterIndexActionType,
3230
PostgresReindexExpression,
@@ -53,15 +51,12 @@
5351
"AdvisoryLockType",
5452
# locking
5553
"LockStrength",
56-
"PostgresForUpdateClause",
5754
# ddl
5855
"PostgresVacuumExpression",
5956
"PostgresAnalyzeExpression",
6057
"PostgresCreatePartitionExpression",
6158
"PostgresDetachPartitionExpression",
6259
"PostgresAttachPartitionExpression",
63-
"PostgresCreateIndexExpression",
64-
"PostgresDropIndexExpression",
6560
"PostgresAlterIndexExpression",
6661
"PostgresAlterIndexActionType",
6762
"PostgresReindexExpression",

0 commit comments

Comments
 (0)