Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
717b988
chore: bump version to 1.0.0.dev16
vistart Jun 16, 2026
bd72d65
ci: update activerecord dependency to release/v1.0.0.dev29
vistart Jun 19, 2026
63ed8d1
fix: install testsuite from git release/v1.0.0.dev16 instead of PyPI …
vistart Jun 19, 2026
fb975d5
feat(schema): implement schema-diff support and DataType refactoring …
vistart Jun 24, 2026
e691dc4
feat(cli): add named-migration subcommand for PostgreSQL backend (#47)
vistart Jun 30, 2026
e45fc79
feat(postgres): fulltext_search capability, benchmark markers, compos…
vistart Jul 5, 2026
aca738b
test(postgres): rewrite fulltext tests to reflect tsvector search cap…
vistart Jul 6, 2026
ab3be40
fix(backend): defensively consume cursor results in get_server_version()
vistart Jul 6, 2026
300b87c
feat(pgq): gate quantified path and comma-separated patterns as unsup…
vistart Jul 7, 2026
23be803
fix: use correct fixture names 'postgres_backend' / 'async_postgres_b…
vistart Jul 7, 2026
f146d54
feat: sync bridge files with testsuite rename and add 4 async relatio…
vistart Jul 7, 2026
3d69c84
feat: add bridge files for derived_field, interface feature tests and…
vistart Jul 9, 2026
d5936eb
perf(backend): remove redundant fetchall() in get_server_version()
vistart Jul 12, 2026
bae710e
refactor(postgres): merge composite_pk/derived_field, switch to expre…
vistart Jul 16, 2026
96f7aaa
fix(postgres): move bridge files to subdirectories, add missing conft…
vistart Jul 16, 2026
57aa76f
fix(postgres): fix PROVIDER_KEY_ASYNC and method names in relation co…
vistart Jul 16, 2026
4fda619
fix(postgres): remove non-existent _ensure_user_post_comment_async_sc…
vistart Jul 17, 2026
eccdc3b
fix(postgres): extend cleanup_after_test table lists to ensure no cro…
vistart Jul 17, 2026
24e0b3a
fix(postgres): add DROP TABLE to relation/connection/benchmark cleanu…
vistart Jul 17, 2026
80a507d
refactor(test): add missing _async bridge files (61), fix naming
vistart Jul 18, 2026
d326af5
fix(test): remove conflicting test_derived_field bridges from basic/f…
vistart Jul 19, 2026
f5fe767
fix(test): restore __init__.py under tests/benchmark/packages
vistart Jul 19, 2026
be5daf3
fix(test): resolve cursor pollution, DataType errors, and import-file…
vistart Jul 19, 2026
326cfaa
fix(ci): update benchmark test file names to match testsuite (remove …
vistart Jul 19, 2026
7dcf56e
fix(test): replace FunctionCall('+') with infix BinaryArithmeticExpre…
vistart Jul 19, 2026
22c59b9
fix(test): add GROUP BY to fund_trace_aggregation, fix pre-existing a…
vistart Jul 19, 2026
f75910a
fix(test): track created tables per provider to eliminate xdist race …
vistart Jul 19, 2026
3a9fdb2
feat(postgres): set supports_json_arrow_operators=True
vistart Jul 24, 2026
86648e6
feat(postgres): add ON CONFLICT capability switches
vistart Jul 31, 2026
06f5e4b
test(postgres): add DROP TABLE CASCADE/RESTRICT rendering test
vistart Jul 31, 2026
e0976b4
fix(postgres): add missing supports_on_conflict_clause and supports_m…
vistart Aug 1, 2026
f14064e
fix(postgres): remove sync savepoint test import, only async tests apply
vistart Aug 1, 2026
b05eb68
fix(postgres): pass scenario name to type_adapter_fixtures provider t…
vistart Aug 1, 2026
dc9bb04
fix(postgres): import all testsuite basic fixtures in conftest while …
vistart Aug 1, 2026
f12f97c
fix(postgres): revert conftest override, restore import * to avoid br…
vistart Aug 1, 2026
67f2b2f
fix(postgres): pass scenario name to sync type_adapter_fixtures to av…
vistart Aug 1, 2026
2806f8d
fix(postgres): remove test bridge files accidentally added in protoco…
vistart Aug 1, 2026
fc8b6a6
fix(postgres): remove shadowing fixture import from type_adapter brid…
vistart Aug 1, 2026
5a13ac2
test(postgres): add unit tests for data type formatting and parsing
vistart Aug 1, 2026
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
24 changes: 12 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ jobs:

- name: Install activerecord and testsuite
run: |
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev28#egg=rhosocial-activerecord
pip install rhosocial-activerecord-testsuite==1.0.0.dev15
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev29#egg=rhosocial-activerecord
pip install git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev16#egg=rhosocial-activerecord-testsuite

- name: Create postgres scenario config
run: |
Expand Down Expand Up @@ -123,13 +123,13 @@ jobs:
--rootdir=. \
--confcutdir=. \
"$TESTSUITE_DIR/benchmark/crud/test_delete_async.py" \
"$TESTSUITE_DIR/benchmark/crud/test_delete_sync.py" \
"$TESTSUITE_DIR/benchmark/crud/test_delete.py" \
"$TESTSUITE_DIR/benchmark/crud/test_insert_async.py" \
"$TESTSUITE_DIR/benchmark/crud/test_insert_sync.py" \
"$TESTSUITE_DIR/benchmark/crud/test_insert.py" \
"$TESTSUITE_DIR/benchmark/crud/test_query_async.py" \
"$TESTSUITE_DIR/benchmark/crud/test_query_sync.py" \
"$TESTSUITE_DIR/benchmark/crud/test_query.py" \
"$TESTSUITE_DIR/benchmark/crud/test_update_async.py" \
"$TESTSUITE_DIR/benchmark/crud/test_update_sync.py" \
"$TESTSUITE_DIR/benchmark/crud/test_update.py" \
"$TESTSUITE_DIR/benchmark/query" \
"$TESTSUITE_DIR/benchmark/transaction" \
"$TESTSUITE_DIR/benchmark/mixin" \
Expand Down Expand Up @@ -279,8 +279,8 @@ jobs:

- name: Install activerecord and testsuite
run: |
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev28#egg=rhosocial-activerecord
pip install rhosocial-activerecord-testsuite==1.0.0.dev15
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev29#egg=rhosocial-activerecord
pip install git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev16#egg=rhosocial-activerecord-testsuite

- name: Create postgres scenario config
run: |
Expand Down Expand Up @@ -391,8 +391,8 @@ jobs:

- name: Install activerecord and testsuite
run: |
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev28#egg=rhosocial-activerecord
pip install rhosocial-activerecord-testsuite==1.0.0.dev15
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev29#egg=rhosocial-activerecord
pip install git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev16#egg=rhosocial-activerecord-testsuite

- name: Create postgres scenario config
run: |
Expand Down Expand Up @@ -490,8 +490,8 @@ jobs:

- name: Install activerecord and testsuite
run: |
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev28#egg=rhosocial-activerecord
pip install rhosocial-activerecord-testsuite==1.0.0.dev15
pip install git+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev29#egg=rhosocial-activerecord
pip install git+https://github.com/rhosocial/python-activerecord-testsuite.git@release/v1.0.0.dev16#egg=rhosocial-activerecord-testsuite

- name: Create postgres scenario config
run: |
Expand Down
1 change: 1 addition & 0 deletions changelog.d/46.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added PostgreSQL-specific DataType subclasses and schema-diff support with `PostgresSchemaDiffer` for accurate schema introspection and column comparison.
1 change: 1 addition & 0 deletions changelog.d/47.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added named-migration CLI subcommand with --async support and PostgreSQL-specific example migrations for the PostgreSQL backend.
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "rhosocial-activerecord-postgres"
version = "1.0.0.dev15"
version = "1.0.0.dev16"
description = "postgres backend implementation for rhosocial-activerecord, providing a robust and optimized postgres database support."
readme = "README.md"
license = { file = "LICENSE" }
Expand Down Expand Up @@ -90,6 +90,12 @@ markers = [
"requires_functions: marks tests requiring specific database functions",
"requires_inner_join: marks tests requiring INNER JOIN support",
"benchmark_fastapi: marks FastAPI benchmark tests",
"benchmark: marks benchmark tests",
"benchmark_backend: marks backend benchmark tests",
"benchmark_async: marks async benchmark tests",
"benchmark_sync: marks sync benchmark tests",
"benchmark_write: marks write benchmark tests",
"benchmark_read: marks read benchmark tests",
]

[tool.coverage.run]
Expand Down
97 changes: 97 additions & 0 deletions src/rhosocial/activerecord/backend/impl/postgres/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,55 @@
from .dialect import PostgresDialect
from .transaction import PostgresTransactionManager, AsyncPostgresTransactionManager
from .types import PostgresEnumType
from .expression.types import (
PostgresBigSerialType,
PostgresBitType,
PostgresBoxType,
PostgresByteaType,
PostgresCIDType,
PostgresCircleType,
PostgresDateMultirangeType,
PostgresDateRangeType,
PostgresGeographyType,
PostgresGeometryType,
PostgresHstoreType,
PostgresInetType,
PostgresInt4MultirangeType,
PostgresInt4RangeType,
PostgresInt8MultirangeType,
PostgresInt8RangeType,
PostgresJsonPathType,
PostgresLineSegmentType,
PostgresLineType,
PostgresMacAddr8Type,
PostgresMacAddrType,
PostgresMoneyType,
PostgresNumMultirangeType,
PostgresNumRangeType,
PostgresOIDType,
PostgresPathType,
PostgresPgLSNType,
PostgresPointType,
PostgresPolygonType,
PostgresRegClassType,
PostgresRegTypeType,
PostgresSerialType,
PostgresSmallSerialType,
PostgresTIDType,
PostgresTSQueryType,
PostgresTSVectorType,
PostgresTsMultirangeType,
PostgresTsRangeType,
PostgresTsTzMultirangeType,
PostgresTsTzRangeType,
PostgresUUIDType,
PostgresVarBitType,
PostgresVectorType,
PostgresXID8Type,
PostgresXIDType,
PostgresXMLType,
PostgresCidrType,
)
from .explain import PostgresExplainResult, PostgresExplainPlanLine
from .expression.ddl import (
PostgresCreateEnumTypeExpression,
Expand Down Expand Up @@ -505,4 +554,52 @@
# hstore functions - Subscript
"hstore_subscript_get",
"hstore_subscript_set",
# PostgreSQL DataType subclasses for DDL
"PostgresBigSerialType",
"PostgresBitType",
"PostgresBoxType",
"PostgresByteaType",
"PostgresCIDType",
"PostgresCircleType",
"PostgresDateMultirangeType",
"PostgresDateRangeType",
"PostgresGeographyType",
"PostgresGeometryType",
"PostgresHstoreType",
"PostgresInetType",
"PostgresInt4MultirangeType",
"PostgresInt4RangeType",
"PostgresInt8MultirangeType",
"PostgresInt8RangeType",
"PostgresJsonPathType",
"PostgresLineSegmentType",
"PostgresLineType",
"PostgresMacAddr8Type",
"PostgresMacAddrType",
"PostgresMoneyType",
"PostgresNumMultirangeType",
"PostgresNumRangeType",
"PostgresOIDType",
"PostgresPathType",
"PostgresPgLSNType",
"PostgresPointType",
"PostgresPolygonType",
"PostgresRegClassType",
"PostgresRegTypeType",
"PostgresSerialType",
"PostgresSmallSerialType",
"PostgresTIDType",
"PostgresTSQueryType",
"PostgresTSVectorType",
"PostgresTsMultirangeType",
"PostgresTsRangeType",
"PostgresTsTzMultirangeType",
"PostgresTsTzRangeType",
"PostgresUUIDType",
"PostgresVarBitType",
"PostgresVectorType",
"PostgresXID8Type",
"PostgresXIDType",
"PostgresXMLType",
"PostgresCidrType",
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

COMMAND_NAMES = [
'info', 'query', 'introspect', 'status',
'named-expression', 'named-procedure', 'named-procedure-graph', 'named-connection',
'named-expression', 'named-procedure', 'named-procedure-graph',
'named-migration', 'named-connection',
]


Expand All @@ -18,6 +19,7 @@ def register_commands(subparsers):
from .named_expression import create_parser as nq_parser
from .named_procedure import create_parser as np_parser
from .named_procedure_graph import create_parser as npg_parser
from .named_migration import create_parser as nm_parser
from .named_connection import create_parser as nc_parser
info_parser(subparsers)
query_parser(subparsers)
Expand All @@ -26,6 +28,7 @@ def register_commands(subparsers):
nq_parser(subparsers)
np_parser(subparsers)
npg_parser(subparsers)
nm_parser(subparsers)
nc_parser(subparsers)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# src/rhosocial/activerecord/backend/impl/postgres/cli/named_migration.py
"""named-migration subcommand — PostgreSQL adapter for the shared CLI helper."""

from __future__ import annotations

from rhosocial.activerecord.backend.impl.postgres import PostgresBackend, AsyncPostgresBackend

from .connection import create_connection_parent_parser, resolve_connection_config_from_args
from .output import create_provider


def create_parser(subparsers):
"""Create the named-migration subcommand parser.

Provides connection and output arguments via a parent parser.
"""
from rhosocial.activerecord.backend.migration.cli import create_named_migration_parser

local_parent = create_connection_parent_parser()
nm_epilog = """Examples:
# Apply a migration
%(prog)s myapp.migrations.v001.CreateUsersTable --host localhost --database mydb --direction up

# Rollback
%(prog)s myapp.migrations.v001.CreateUsersTable --host localhost --database mydb --direction down

# Apply with record store
%(prog)s myapp.migrations.v001.CreateUsersTable --database mydb --record-store ./migrations.json

# Dry-run
%(prog)s myapp.migrations.v001.CreateUsersTable --database mydb --direction up --dry-run

# Apply asynchronously
%(prog)s myapp.migrations.v001.CreateUsersTable --database mydb --direction up --async

# Pass parameters to the migration
%(prog)s myapp.migrations.v001.CreateUsersTable --param table_name=custom_table

# List available migrations
%(prog)s myapp.migrations --list

# Describe a migration
%(prog)s myapp.migrations.v001.CreateUsersTable --describe

# Batch: apply all pending migrations in a module
%(prog)s myapp.migrations --all --record-store ./migrations.json

# Batch: dry-run all pending migrations
%(prog)s myapp.migrations --all --record-store ./migrations.json --dry-run

# Batch: rollback all applied migrations
%(prog)s myapp.migrations --all --record-store ./migrations.json --direction down

# Batch: wrap all migrations in a single transaction
%(prog)s myapp.migrations --all --record-store ./migrations.json --single-transaction

End-to-end demo scripts with UP/DOWN round-trip verification:
https://github.com/rhosocial/python-activerecord-postgres/tree/main/src/rhosocial/activerecord/backend/impl/postgres/examples/named_migrations
PYTHONPATH=src bash demo_basic.sh
PYTHONPATH=src bash demo_chain.sh
PYTHONPATH=src bash demo_all.sh
"""
return create_named_migration_parser(subparsers, local_parent, epilog=nm_epilog)


def handle(args):
"""Handle the named-migration subcommand."""
from rhosocial.activerecord.backend.migration.cli import handle_named_migration as handle_nm

provider = create_provider(args.output, ascii_borders=args.rich_ascii)

backend = None

def backend_factory():
nonlocal backend
config = resolve_connection_config_from_args(args)
backend = PostgresBackend(connection_config=config)
backend.connect()
backend.introspect_and_adapt()
return backend

def disconnect():
if backend and getattr(backend, "_connection", None):
backend.disconnect()

is_async = getattr(args, "is_async", False)
if is_async:
async_backend = None

def backend_async_factory():
nonlocal async_backend
config = resolve_connection_config_from_args(args)
async_backend = AsyncPostgresBackend(connection_config=config)
return async_backend

async def disconnect_async():
if async_backend and getattr(async_backend, "_connection", None):
await async_backend.disconnect()

handle_nm(
args,
provider,
backend_factory=backend_factory,
disconnect=disconnect,
backend_async_factory=backend_async_factory,
disconnect_async=disconnect_async,
)
return

handle_nm(
args,
provider,
backend_factory=backend_factory,
disconnect=disconnect,
)
9 changes: 9 additions & 0 deletions src/rhosocial/activerecord/backend/impl/postgres/dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
IntrospectionSupport,
TransactionControlSupport,
SQLFunctionSupport,
DDLTypeSupport,
)
from .mixins import (
PostgresExtensionMixin,
Expand Down Expand Up @@ -169,6 +170,7 @@
PostgresRangeTypeMixin,
PostgresJSONBEnhancedMixin,
PostgresArrayEnhancedMixin,
PostgresTypeFormatSupportMixin,
# DDL/DML operation mixins (new)
PostgresExtendedStatisticsMixin,
PostgresStoredProcedureMixin,
Expand Down Expand Up @@ -400,6 +402,7 @@ class PostgresDialect(
PostgresFullTextSearchMixin,
PostgresRangeTypeMixin,
PostgresArrayEnhancedMixin,
PostgresTypeFormatSupportMixin,
# DDL/DML operation mixins (new)
PostgresExtendedStatisticsMixin,
PostgresStoredProcedureMixin,
Expand Down Expand Up @@ -524,6 +527,8 @@ class PostgresDialect(
PostgresRangeTypeSupport,
PostgresJSONBEnhancedSupport,
PostgresArrayEnhancedSupport,
# DataType Support Protocol
DDLTypeSupport,
# New feature protocols
PostgresParallelQuerySupport,
PostgresStoredProcedureSupport,
Expand Down Expand Up @@ -689,6 +694,10 @@ def supports_for_update(self) -> bool:
"""
return True

def supports_json_arrow_operators(self) -> bool:
"""PostgreSQL supports -> and ->> operators for JSON/JSONB access."""
return True

def supports_lock_strength(self, strength) -> bool:
"""
Check if a specific lock strength is supported.
Expand Down
Loading
Loading