Repository navigation
feat(postgres): add named query/procedure CLI, extension support, and ConcurrencyAware - #32
Merged
Merged
Conversation
Adds support for executing named queries via CLI using the shared named_query module from core library. Features: - --describe: show function signature - --dry-run: preview SQL without execution - --list: list queries in a module - --param: pass parameters - --force: allow DML/DDL execution
- Register named-procedure parser and handler - Add async support for named-query and named-procedure
This adds test coverage for PostgreSQL backend CLI: - TestCLINamedQueryArgs: 6 tests for named-query args parsing - TestCLINamedProcedureArgs: 9 tests for named-procedure args parsing These tests mirror the MySQL CLI tests for consistency.
Provide PostgreSQL-specific connection parameter examples in help text instead of SQLite's --db-file examples.
- Add PostgresConcurrencyMixin in base.py - Uses connection pool size as concurrency limit - Both sync and async backends implement ConcurrencyAware protocol
- Replace pytest_asyncio.mark.asyncio with pytest.mark.asyncio - This fixes test collection error in pytest-asyncio 1.3.0
- Add CreateExtensionExpression for CREATE EXTENSION SQL generation - Add DropExtensionExpression for DROP EXTENSION SQL generation - Update PostgresExtensionSupport protocol with format methods - Add format_create_extension and format_drop_extension to mixin - Add comprehensive tests for extension DDL expressions - Auto-quote extension names containing hyphens or underscores
- Add Dockerfile for PostgreSQL 14-18 with postgis, pgvector, earthdistance - Add docker-compose.yml for easy deployment - Add build.sh for multi-version builds - Add init-extensions.sh for automatic extension creation - Add comprehensive README documentation Supports: amd64, arm64, ppc64le, s390x
- Add test_postgres_protocol_conformance.py with tests for: - PostgresDialect implements all declared protocols - All protocols have at least one member - No overlap between protocols - Skip non-runtime_checkable protocols in isinstance tests
AsyncIntrospectionSupport was removed from python-activerecord, so the test exclusion is no longer needed.
PostgresBackendMixin now provides threadsafety property (returns 2). Removed duplicate implementation from sync.py and async_backend.py.
…ests Refactor handle_info() to use _resolve_postgres_config() instead of inline named connection logic, ensuring consistency across all subcommands. Add test suite with PostgreSQL 18 example connections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pytest does not allow __init__.py in test directories. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ility status - Add 'available' field to PostgresExtensionInfo to track installable extensions - Add is_extension_available() method to protocol and mixin - Expand KNOWN_EXTENSIONS from 11 to 35+ extensions - Query pg_available_extensions for complete extension status - Add 'extensions' introspection command for CLI - Update info command to display installed vs available extensions
…ions Previously _detect_extensions() only queried pg_extension for installed extensions and used hardcoded KNOWN_EXTENSIONS as fallback. This missed pre-installed extensions that are available but not enabled. Now the method: 1. Queries pg_extension for installed extensions (installed=True, available=True) 2. Queries pg_available_extensions for all available extensions 3. Marks installed extensions as available 4. Marks not-installed-but-available extensions as available=True
- Sort extensions alphabetically by name
…ions Implemented support for the following extensions: - pg_cron: job scheduling - citext: case-insensitive text - bloom: bloom filter index - pg_partman: partition management - orafce: Oracle compatibility - pglogical: logical replication - pgaudit: audit logging - pg_repack: online rebuild - hypopg: hypothetical indexes - address_standardizer: address parsing - pg_surgery: data repair - pg_walinspect: WAL inspection - fuzzystrmatch: fuzzy string matching - cube: multidimensional cube - pgcrypto: cryptographic functions - btree_gin: GIN index for btree types - btree_gist: GiST index for btree types - uuid-ossp: UUID generation
- Follow the examples README structure (Setup/Business Logic/Execution/Teardown) - Use expressions (CreateTableExpression, InsertExpression, QueryExpression) - Check extension availability before execution - Auto-create extension if needed
- Use CreateExtensionExpression for extension creation - Follow examples README structure (Setup/Business Logic/Execution/Teardown) - Check extension availability before execution
- Use CreateExtensionExpression for extension creation - Re-detect extensions after creation with introspect_and_adapt() - Follow examples README structure
- Remove backend_cls parameter requirement from named connection factories - Update CLI config resolution to use new API - Add CLI parameter priority tests (5 tests pass)
- Add named-connection subcommand for managing named connections - Support --list, --show, --describe options - Allows users to list and inspect named connection configs
Add examples for development and production PostgreSQL connections: - local_dev(): local development database - local_dev_no_password(): local database without password - test_db(): test database - prod_db(): production database with SSL - prod_db_ssl(): production with full SSL verification - prod_replica(): read replica for read-heavy workloads
- Remove erroneous reset_pg_stat_statements() from pg_stat_statements Protocol (violates Dialect layer "generate SQL, don't execute" principle) - Add supports_pg_cron_run capability to pg_cron Protocol/Mixin - Add format_auto_partition_config to pg_partman Mixin/Protocol (aligns with existing supports_pg_partman_auto_partition declaration) - Add format_pglogical_show_subscription_status and format_pglogical_alter_subscription_synchronize to pglogical Mixin/Protocol (aligns with existing supports_pglogical_replication declaration) - Update KNOWN_EXTENSIONS: add 'cancel' and 'run' features for pg_cron
… for 29 extensions Add missing format_* methods to Mixins where supports_* declared a capability but no SQL generation method existed: - hstore: format_hstore_delete_key, format_hstore_each, format_hstore_keys/values - ltree: format_ltree_concat, format_ltree_lca - intarray: format_intarray_index_statement - citext: format_citext_literal - cube: format_cube_contains, format_cube_distance, format_cube_size - pgcrypto: format_digest, format_pgp_encrypt, format_pgp_decrypt - postgis: format_geography_literal, format_st_area/transform/as_geojson/buffer - pgvector: format_create_hnsw_index_statement, format_vector_l2_distance/inner_product - fuzzystrmatch: format_difference, format_metaphone - btree_gin/gist: format_btree_gin/gist_operator_class - hypopg: format_hypopg_estimate_size - orafce: format_instr, format_substr Add format_* method signatures to all 29 Protocol files (previously only supports_* was declared). Register new Mixins in PostgresDialect inheritance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sions Add requires_extension() and requires_extension_available() decorators to conftest.py for skipping integration tests when extensions are not installed on the test database. Add unit tests for all 20 extensions that previously lacked test coverage: citext, pgcrypto, fuzzystrmatch, cube, uuid-ossp, bloom, btree_gin, btree_gist, tablefunc, pg_stat_statements, pg_cron, orafce, pg_partman, hypopg, pglogical, pgaudit, pg_repack, address_standardizer, pg_surgery, pg_walinspect. All tests are pure in-memory (no database connection required).
Integration tests that connect to real PostgreSQL database: - hstore: CRUD, operators (@>/?/||), functions (each/akeys/avals) - pg_trgm: similarity, % operator, GIN index, show_trgm - uuid-ossp: uuid_generate_v1/v4, UUID column default - citext: case-insensitive comparison, unique constraint, LIKE, JOIN - ltree: path operators (@>/<@/~), ancestor/descendant, GiST index - pgcrypto: crypt+gen_salt, digest, hmac, pgp_sym_encrypt/decrypt - intarray: @>/&&/<@ operators, idx() function, GIN index - fuzzystrmatch: levenshtein, soundex, difference, metaphone/dmetaphone All tests use requires_extension() guard to skip when not installed.
- postgis: spatial data (geometry), ST_Distance/Contains, GIST index - pgvector: vector column, cosine similarity search, HNSW index - ltree: hierarchical paths, ancestor/descendant queries, GiST index - pgcrypto: crypt+gen_salt password hashing, digest, hmac - pg_trgm: similarity fuzzy search, % operator, GIN index - intarray: @>/&& operators, idx/sort/uniq functions - earthdistance: earth_distance calculation, range queries - cube: @> contains, <-> distance, nearest-neighbor search All examples follow Setup → Business Logic → Execution → Teardown structure with CreateExtensionExpression and introspect_and_adapt().
…date KNOWN_EXTENSIONS - Add PostgresPostgisRasterSupport protocol and PostgresPostgisRasterMixin for PostGIS raster type operations (ST_AsRaster, ST_RastFromText, etc.) - Add PostgresPgRoutingSupport protocol and PostgresPgRoutingMixin for pgRouting path finding operations (pgr_dijkstra, pgr_astar, etc.) - Update KNOWN_EXTENSIONS: add raster_type feature for postgis_raster, dijkstra feature for pgrouting, cancel/run features for pg_cron - Mark 8 extensions as requires_dialect: False that don't need Protocol/Mixin adaptation (postgis_topology, postgis_sfcgal, postgis_tiger_geocoder, amcheck, pageinspect, pgtap, pg_logicalinspect, address_standardizer_data_us)
…examples
Replace raw SQL strings with expression-based API calls across all 12
extension example files. Key changes:
- Use DropTableExpression instead of raw "DROP TABLE IF EXISTS" SQL
- Use FunctionCall for all SQL function invocations (similarity, crypt,
digest, ST_Distance, levenshtein, etc.)
- Use BinaryExpression for PostgreSQL-specific operators (@>, <@, <->,
<=>, ?, ||, ~, %, &&)
- Use Subquery wrapping for BinaryExpression aliases (BinaryExpression
lacks AliasableMixin)
- Use QueryExpression for all SELECT queries instead of raw SQL
- Simplify RawSQLPredicate workaround: pass BinaryExpression directly
as WHERE condition (format_where_clause calls to_sql() generically)
Remaining RawSQLExpression usage is for extension-specific literals
that have no expression support: hstore key=>value syntax, pgvector
[x,y,z] syntax, cube (x,y,z) syntax, intarray {1,3} syntax, and
PostgreSQL ::type cast notation.
…x expression system gaps
- Override PostgresDialect.format_binary_operator to escape % as %% for
psycopg compatibility (pg_trgm similarity operator)
- Add PostgresIndexMixin.format_create_index_statement for CREATE INDEX
with operator classes via dialect_options["opclasses"]
- Move PostgresIndexMixin before IndexMixin in PostgresDialect MRO to
ensure override takes effect
- Remove blind sql.replace("?", "%s") in PostgresBackendMixin that broke
hstore/jsonb ? operators
- Add uuid_ns_dns/url/oid/x500 helper functions returning FunctionCall
- Replace all RawSQLExpression usage in 12 extension examples with
proper expression API (Literal.cast(), FunctionCall, Subquery, etc.)
- Fix cube range data format from invalid (a,b),(c,d),(e,f) to correct
(min_point),(max_point)
- Fix result data access from integer index to dict key access
- Fix uuid_generate_v5 namespace parameter to use uuid_ns_dns() instead
of string literal
- Remove uuid_max() call (not available in current PG versions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…/ subpackage Split the 1779-line __main__.py into a thin entry point and 9 focused modules under cli/, following the same pattern as SQLite and MySQL: - __init__.py: subcommand registration and dynamic dispatch - output.py: provider factory and RICH_AVAILABLE - connection.py: connection args, config resolution, backend creation - info.py: info subcommand with protocol display (26 Postgres Protocols) - query.py: query subcommand (sync + async) - introspect.py: introspect subcommand with 'extensions' type - status.py: status subcommand with WAL/Replication/Archive/Security sections - named_query.py: named-query adapter using closures - named_procedure.py: named-procedure adapter using closures - named_connection.py: named-connection adapter with empty parent parser Key changes: - Remove global _default_backend variable, use closure-based approach - Move -v/--verbose from main parser to info/status subcommands only - Each subcommand independently decides its own arguments - Rename test_cli.py to test_named_connection_cli.py for clarity - Update mock paths from __main__ to cli.connection module
…YTHONPATH, and enable autocommit 1. Remove `tests.` prefix from module paths in integration tests. The `tests.rhosocial...` import paths only worked with `python -m pytest` (CWD in sys.path makes `tests` an implicit namespace package). Direct `pytest` does not add CWD to sys.path, causing ModuleNotFoundError. Fix by using `rhosocial.xxx` paths that work via PYTHONPATH entry. 2. Add `tests` to PYTHONPATH in CI to ensure `tests/rhosocial/` is searched as part of the namespace package path. 3. Enable autocommit on psycopg connections to prevent psycopg 3 from auto-managing transactions (which conflicts with manual BEGIN/COMMIT statements in the ActiveRecord layer).
…rinciples - Derive 5 PG protocols from generic counterparts: PostgresTableSupport(TableSupport), PostgresIndexSupport(IndexSupport), PostgresLockingSupport(LockingSupport), PostgresTriggerSupport(TriggerSupport), PostgresConstraintSupport(ConstraintSupport) - Remove 4 duplicate generic protocols from PostgresDialect base class list - Rename 22 PG expression classes with Postgres prefix: AdvisoryLockExpression → PostgresAdvisoryLockExpression, RefreshMaterializedViewPgExpression → PostgresRefreshMaterializedViewExpression, CreateEnumTypeExpression → PostgresCreateEnumTypeExpression, etc. - Fix Expression-Dialect separation for 3 expression classes: PostgresAlterEnumTypeAddValueExpression, PostgresAlterEnumTypeRenameValueExpression, PostgresCreateRangeTypeExpression - Make PostgresRefreshMaterializedViewExpression derive from RefreshMaterializedViewExpression with schema/name/concurrently aliases - Rename format_postgres_for_update_clause → format_for_update_clause in PostgresLockingMixin and PostgresLockingSupport protocol - Add TestPostgresProtocolDerivation and TestPostgresExpressionDialectSeparation test classes for protocol coverage validation - Update test_postgres_protocol_conformance.py excluded_overlaps Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…2 format
The third segment of UUID v1 was incorrectly matched with {3} hex chars
instead of {4}, causing test_uuid_generate_v1 to always fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostgresIndexSupport, PostgresTableSupport, and PostgresConstraintSupport now derive from their generic counterparts, so method overlap is expected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and PostgresMultirangeSupport These two protocols were missing the decorator, causing them to be skipped in protocol conformance isinstance() checks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l_and_params
ActiveQuery layer uses ? as parameter placeholders (e.g. where("name = ?",
params)), but psycopg v3 requires %s format. Previously the conversion
was not performed, causing "the query has N placeholders but M parameters
were passed" errors for any query using RawSQLPredicate/RawSQLExpression.
The new _convert_qmark_to_format() method handles the conversion while
preserving PostgreSQL's hstore/jsonb operators (?, ?|, ?&) by:
- Keeping ? followed by | or & as-is (operator context)
- Keeping ? preceded by alphanumeric/underscore as-is (identifier context)
- Replacing all other ? with %s (parameter placeholder context)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…greSQL backend Placeholder conversion is now handled by the ActiveRecord layer via convert_qmark_placeholder(). The backend no longer needs to do any ? → %s conversion in _prepare_sql_and_params. Remove _convert_qmark_to_format static method and _prepare_sql_and_params override, restoring base class pass-through behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…in format_for_update_clause PostgreSQL dialect's format_for_update_clause assumed clause.strength always exists, but the base ForUpdateClause class (used by Query.for_update()) does not have this attribute — only PostgresForUpdateClause does. Use getattr() with a default of LockStrength.UPDATE for compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch from PyPI package to git+https source for rhosocial-activerecord-testsuite to track the new dev12 branch.
…nsaction_manager access Replace direct `_transaction_manager.is_active` checks with the `in_transaction` property in both sync and async backends. The property already handles the None case for `_transaction_manager`, making the code simpler and consistent with the project's abstraction conventions.
Add a second PostgreSQL service using vistart/postgres-exts image alongside the official postgres container, allowing tests against databases with additional extensions installed. - postgres service: port 5432 (official image) - postgres-exts service: port 5433 (vistart/postgres-exts image) - Both use same version tag from matrix, same credentials - Added postgres_exts scenario to CI config - Wait step now checks both services are ready Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## release/v1.0.0.dev12 #32 +/- ##
========================================================
- Coverage 73.12% 66.31% -6.82%
========================================================
Files 136 188 +52
Lines 7365 9474 +2109
Branches 1218 1468 +250
========================================================
+ Hits 5386 6283 +897
- Misses 1546 2750 +1204
- Partials 433 441 +8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
vistart
marked this pull request as ready for review
April 27, 2026 03:59
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
11 of 14 tasks
vistart
added a commit
that referenced
this pull request
May 1, 2026
* feat(postgres): add named-query CLI subcommand
Adds support for executing named queries via CLI using the shared
named_query module from core library.
Features:
- --describe: show function signature
- --dry-run: preview SQL without execution
- --list: list queries in a module
- --param: pass parameters
- --force: allow DML/DDL execution
* feat(backend): add named-procedure CLI subcommand
- Register named-procedure parser and handler
- Add async support for named-query and named-procedure
* Add CLI tests for named-query and named-procedure subcommands
This adds test coverage for PostgreSQL backend CLI:
- TestCLINamedQueryArgs: 6 tests for named-query args parsing
- TestCLINamedProcedureArgs: 9 tests for named-procedure args parsing
These tests mirror the MySQL CLI tests for consistency.
* feat(postgres): use custom epilog for named-query/procedure CLI
Provide PostgreSQL-specific connection parameter examples in help text
instead of SQLite's --db-file examples.
* feat(backend): add ConcurrencyAware for PostgreSQL
- Add PostgresConcurrencyMixin in base.py
- Uses connection pool size as concurrency limit
- Both sync and async backends implement ConcurrencyAware protocol
* test(backend): add ConcurrencyAware protocol tests for PostgreSQL
* fix: update async marker for pytest-asyncio 1.3.0 compatibility
- Replace pytest_asyncio.mark.asyncio with pytest.mark.asyncio
- This fixes test collection error in pytest-asyncio 1.3.0
* Add CreateExtensionExpression and DropExtensionExpression DDL classes
- Add CreateExtensionExpression for CREATE EXTENSION SQL generation
- Add DropExtensionExpression for DROP EXTENSION SQL generation
- Update PostgresExtensionSupport protocol with format methods
- Add format_create_extension and format_drop_extension to mixin
- Add comprehensive tests for extension DDL expressions
- Auto-quote extension names containing hyphens or underscores
* Add multi-arch PostgreSQL image with all extensions
- Add Dockerfile for PostgreSQL 14-18 with postgis, pgvector, earthdistance
- Add docker-compose.yml for easy deployment
- Add build.sh for multi-version builds
- Add init-extensions.sh for automatic extension creation
- Add comprehensive README documentation
Supports: amd64, arm64, ppc64le, s390x
* test(postgres): add protocol conformance tests
- Add test_postgres_protocol_conformance.py with tests for:
- PostgresDialect implements all declared protocols
- All protocols have at least one member
- No overlap between protocols
- Skip non-runtime_checkable protocols in isinstance tests
* fix(tests): remove AsyncIntrospectionSupport exclusion
AsyncIntrospectionSupport was removed from python-activerecord,
so the test exclusion is no longer needed.
* refactor(backend): move threadsafety to PostgresBackendMixin
PostgresBackendMixin now provides threadsafety property (returns 2).
Removed duplicate implementation from sync.py and async_backend.py.
* feat(named-connection): unify handle_info config resolution and add tests
Refactor handle_info() to use _resolve_postgres_config() instead of
inline named connection logic, ensuring consistency across all
subcommands. Add test suite with PostgreSQL 18 example connections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): remove __init__.py from named_connection test directory
pytest does not allow __init__.py in test directories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(extensions): enhance PostgreSQL extension detection with availability status
- Add 'available' field to PostgresExtensionInfo to track installable extensions
- Add is_extension_available() method to protocol and mixin
- Expand KNOWN_EXTENSIONS from 11 to 35+ extensions
- Query pg_available_extensions for complete extension status
- Add 'extensions' introspection command for CLI
- Update info command to display installed vs available extensions
* fix(extensions): detect available extensions from pg_available_extensions
Previously _detect_extensions() only queried pg_extension for installed
extensions and used hardcoded KNOWN_EXTENSIONS as fallback. This missed
pre-installed extensions that are available but not enabled.
Now the method:
1. Queries pg_extension for installed extensions (installed=True, available=True)
2. Queries pg_available_extensions for all available extensions
3. Marks installed extensions as available
4. Marks not-installed-but-available extensions as available=True
* feat(introspect): sort extensions by name
- Sort extensions alphabetically by name
* feat(extensions): add Protocol and Mixin for 17 new PostgreSQL extensions
Implemented support for the following extensions:
- pg_cron: job scheduling
- citext: case-insensitive text
- bloom: bloom filter index
- pg_partman: partition management
- orafce: Oracle compatibility
- pglogical: logical replication
- pgaudit: audit logging
- pg_repack: online rebuild
- hypopg: hypothetical indexes
- address_standardizer: address parsing
- pg_surgery: data repair
- pg_walinspect: WAL inspection
- fuzzystrmatch: fuzzy string matching
- cube: multidimensional cube
- pgcrypto: cryptographic functions
- btree_gin: GIN index for btree types
- btree_gist: GiST index for btree types
- uuid-ossp: UUID generation
* feat(examples): add citext extension example
- Follow the examples README structure (Setup/Business Logic/Execution/Teardown)
- Use expressions (CreateTableExpression, InsertExpression, QueryExpression)
- Check extension availability before execution
- Auto-create extension if needed
* feat(examples): add fuzzystrmatch and citext extension examples
- Use CreateExtensionExpression for extension creation
- Follow examples README structure (Setup/Business Logic/Execution/Teardown)
- Check extension availability before execution
* feat(examples): add uuid_ossp and hstore extension examples
- Use CreateExtensionExpression for extension creation
- Re-detect extensions after creation with introspect_and_adapt()
- Follow examples README structure
* feat: simplify named connection API and add PostgreSQL CLI tests
- Remove backend_cls parameter requirement from named connection factories
- Update CLI config resolution to use new API
- Add CLI parameter priority tests (5 tests pass)
* feat: integrate named-connection subcommand into PostgreSQL CLI
- Add named-connection subcommand for managing named connections
- Support --list, --show, --describe options
- Allows users to list and inspect named connection configs
* feat: add PostgreSQL named connection examples
Add examples for development and production PostgreSQL connections:
- local_dev(): local development database
- local_dev_no_password(): local database without password
- test_db(): test database
- prod_db(): production database with SSL
- prod_db_ssl(): production with full SSL verification
- prod_replica(): read replica for read-heavy workloads
* fix(extensions): align Protocol and Mixin interfaces for 4 extensions
- Remove erroneous reset_pg_stat_statements() from pg_stat_statements Protocol
(violates Dialect layer "generate SQL, don't execute" principle)
- Add supports_pg_cron_run capability to pg_cron Protocol/Mixin
- Add format_auto_partition_config to pg_partman Mixin/Protocol
(aligns with existing supports_pg_partman_auto_partition declaration)
- Add format_pglogical_show_subscription_status and
format_pglogical_alter_subscription_synchronize to pglogical Mixin/Protocol
(aligns with existing supports_pglogical_replication declaration)
- Update KNOWN_EXTENSIONS: add 'cancel' and 'run' features for pg_cron
* feat(extensions): supplement format_* methods and Protocol signatures for 29 extensions
Add missing format_* methods to Mixins where supports_* declared a capability
but no SQL generation method existed:
- hstore: format_hstore_delete_key, format_hstore_each, format_hstore_keys/values
- ltree: format_ltree_concat, format_ltree_lca
- intarray: format_intarray_index_statement
- citext: format_citext_literal
- cube: format_cube_contains, format_cube_distance, format_cube_size
- pgcrypto: format_digest, format_pgp_encrypt, format_pgp_decrypt
- postgis: format_geography_literal, format_st_area/transform/as_geojson/buffer
- pgvector: format_create_hnsw_index_statement, format_vector_l2_distance/inner_product
- fuzzystrmatch: format_difference, format_metaphone
- btree_gin/gist: format_btree_gin/gist_operator_class
- hypopg: format_hypopg_estimate_size
- orafce: format_instr, format_substr
Add format_* method signatures to all 29 Protocol files (previously only
supports_* was declared). Register new Mixins in PostgresDialect inheritance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(extensions): add test skip mechanism and unit tests for 20 extensions
Add requires_extension() and requires_extension_available() decorators
to conftest.py for skipping integration tests when extensions are not
installed on the test database.
Add unit tests for all 20 extensions that previously lacked test coverage:
citext, pgcrypto, fuzzystrmatch, cube, uuid-ossp, bloom, btree_gin,
btree_gist, tablefunc, pg_stat_statements, pg_cron, orafce, pg_partman,
hypopg, pglogical, pgaudit, pg_repack, address_standardizer, pg_surgery,
pg_walinspect.
All tests are pure in-memory (no database connection required).
* test(extensions): add integration tests for 8 extensions
Integration tests that connect to real PostgreSQL database:
- hstore: CRUD, operators (@>/?/||), functions (each/akeys/avals)
- pg_trgm: similarity, % operator, GIN index, show_trgm
- uuid-ossp: uuid_generate_v1/v4, UUID column default
- citext: case-insensitive comparison, unique constraint, LIKE, JOIN
- ltree: path operators (@>/<@/~), ancestor/descendant, GiST index
- pgcrypto: crypt+gen_salt, digest, hmac, pgp_sym_encrypt/decrypt
- intarray: @>/&&/<@ operators, idx() function, GIN index
- fuzzystrmatch: levenshtein, soundex, difference, metaphone/dmetaphone
All tests use requires_extension() guard to skip when not installed.
* feat(examples): add 8 extension usage examples
- postgis: spatial data (geometry), ST_Distance/Contains, GIST index
- pgvector: vector column, cosine similarity search, HNSW index
- ltree: hierarchical paths, ancestor/descendant queries, GiST index
- pgcrypto: crypt+gen_salt password hashing, digest, hmac
- pg_trgm: similarity fuzzy search, % operator, GIN index
- intarray: @>/&& operators, idx/sort/uniq functions
- earthdistance: earth_distance calculation, range queries
- cube: @> contains, <-> distance, nearest-neighbor search
All examples follow Setup → Business Logic → Execution → Teardown
structure with CreateExtensionExpression and introspect_and_adapt().
* feat(extensions): add postgis_raster and pgrouting Protocol/Mixin, update KNOWN_EXTENSIONS
- Add PostgresPostgisRasterSupport protocol and PostgresPostgisRasterMixin
for PostGIS raster type operations (ST_AsRaster, ST_RastFromText, etc.)
- Add PostgresPgRoutingSupport protocol and PostgresPgRoutingMixin
for pgRouting path finding operations (pgr_dijkstra, pgr_astar, etc.)
- Update KNOWN_EXTENSIONS: add raster_type feature for postgis_raster,
dijkstra feature for pgrouting, cancel/run features for pg_cron
- Mark 8 extensions as requires_dialect: False that don't need
Protocol/Mixin adaptation (postgis_topology, postgis_sfcgal,
postgis_tiger_geocoder, amcheck, pageinspect, pgtap,
pg_logicalinspect, address_standardizer_data_us)
* refactor(examples): replace raw SQL with expression API in extension examples
Replace raw SQL strings with expression-based API calls across all 12
extension example files. Key changes:
- Use DropTableExpression instead of raw "DROP TABLE IF EXISTS" SQL
- Use FunctionCall for all SQL function invocations (similarity, crypt,
digest, ST_Distance, levenshtein, etc.)
- Use BinaryExpression for PostgreSQL-specific operators (@>, <@, <->,
<=>, ?, ||, ~, %, &&)
- Use Subquery wrapping for BinaryExpression aliases (BinaryExpression
lacks AliasableMixin)
- Use QueryExpression for all SELECT queries instead of raw SQL
- Simplify RawSQLPredicate workaround: pass BinaryExpression directly
as WHERE condition (format_where_clause calls to_sql() generically)
Remaining RawSQLExpression usage is for extension-specific literals
that have no expression support: hstore key=>value syntax, pgvector
[x,y,z] syntax, cube (x,y,z) syntax, intarray {1,3} syntax, and
PostgreSQL ::type cast notation.
* fix(postgres): remove RawSQLExpression from extension examples and fix expression system gaps
- Override PostgresDialect.format_binary_operator to escape % as %% for
psycopg compatibility (pg_trgm similarity operator)
- Add PostgresIndexMixin.format_create_index_statement for CREATE INDEX
with operator classes via dialect_options["opclasses"]
- Move PostgresIndexMixin before IndexMixin in PostgresDialect MRO to
ensure override takes effect
- Remove blind sql.replace("?", "%s") in PostgresBackendMixin that broke
hstore/jsonb ? operators
- Add uuid_ns_dns/url/oid/x500 helper functions returning FunctionCall
- Replace all RawSQLExpression usage in 12 extension examples with
proper expression API (Literal.cast(), FunctionCall, Subquery, etc.)
- Fix cube range data format from invalid (a,b),(c,d),(e,f) to correct
(min_point),(max_point)
- Fix result data access from integer index to dict key access
- Fix uuid_generate_v5 namespace parameter to use uuid_ns_dns() instead
of string literal
- Remove uuid_max() call (not available in current PG versions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(postgres): modularize CLI from monolithic __main__.py to cli/ subpackage
Split the 1779-line __main__.py into a thin entry point and 9 focused
modules under cli/, following the same pattern as SQLite and MySQL:
- __init__.py: subcommand registration and dynamic dispatch
- output.py: provider factory and RICH_AVAILABLE
- connection.py: connection args, config resolution, backend creation
- info.py: info subcommand with protocol display (26 Postgres Protocols)
- query.py: query subcommand (sync + async)
- introspect.py: introspect subcommand with 'extensions' type
- status.py: status subcommand with WAL/Replication/Archive/Security sections
- named_query.py: named-query adapter using closures
- named_procedure.py: named-procedure adapter using closures
- named_connection.py: named-connection adapter with empty parent parser
Key changes:
- Remove global _default_backend variable, use closure-based approach
- Move -v/--verbose from main parser to info/status subcommands only
- Each subcommand independently decides its own arguments
- Rename test_cli.py to test_named_connection_cli.py for clarity
- Update mock paths from __main__ to cli.connection module
* fix(postgres): remove tests. prefix from module paths, add tests to PYTHONPATH, and enable autocommit
1. Remove `tests.` prefix from module paths in integration tests.
The `tests.rhosocial...` import paths only worked with `python -m
pytest` (CWD in sys.path makes `tests` an implicit namespace package).
Direct `pytest` does not add CWD to sys.path, causing ModuleNotFoundError.
Fix by using `rhosocial.xxx` paths that work via PYTHONPATH entry.
2. Add `tests` to PYTHONPATH in CI to ensure `tests/rhosocial/` is
searched as part of the namespace package path.
3. Enable autocommit on psycopg connections to prevent psycopg 3 from
auto-managing transactions (which conflicts with manual BEGIN/COMMIT
statements in the ActiveRecord layer).
* refactor(backend): align expression system with three architectural principles
- Derive 5 PG protocols from generic counterparts:
PostgresTableSupport(TableSupport),
PostgresIndexSupport(IndexSupport),
PostgresLockingSupport(LockingSupport),
PostgresTriggerSupport(TriggerSupport),
PostgresConstraintSupport(ConstraintSupport)
- Remove 4 duplicate generic protocols from PostgresDialect base class list
- Rename 22 PG expression classes with Postgres prefix:
AdvisoryLockExpression → PostgresAdvisoryLockExpression,
RefreshMaterializedViewPgExpression → PostgresRefreshMaterializedViewExpression,
CreateEnumTypeExpression → PostgresCreateEnumTypeExpression, etc.
- Fix Expression-Dialect separation for 3 expression classes:
PostgresAlterEnumTypeAddValueExpression,
PostgresAlterEnumTypeRenameValueExpression,
PostgresCreateRangeTypeExpression
- Make PostgresRefreshMaterializedViewExpression derive from
RefreshMaterializedViewExpression with schema/name/concurrently aliases
- Rename format_postgres_for_update_clause → format_for_update_clause
in PostgresLockingMixin and PostgresLockingSupport protocol
- Add TestPostgresProtocolDerivation and TestPostgresExpressionDialectSeparation
test classes for protocol coverage validation
- Update test_postgres_protocol_conformance.py excluded_overlaps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): correct UUID v1 regex pattern to match standard 8-4-4-4-12 format
The third segment of UUID v1 was incorrectly matched with {3} hex chars
instead of {4}, causing test_uuid_generate_v1 to always fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): add derivation pairs to protocol non-overlap exclusions
PostgresIndexSupport, PostgresTableSupport, and PostgresConstraintSupport
now derive from their generic counterparts, so method overlap is expected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(protocols): add @runtime_checkable to PostgresAdvisoryLockSupport and PostgresMultirangeSupport
These two protocols were missing the decorator, causing them to be
skipped in protocol conformance isinstance() checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(backend): convert ? placeholders to %s for psycopg in _prepare_sql_and_params
ActiveQuery layer uses ? as parameter placeholders (e.g. where("name = ?",
params)), but psycopg v3 requires %s format. Previously the conversion
was not performed, causing "the query has N placeholders but M parameters
were passed" errors for any query using RawSQLPredicate/RawSQLExpression.
The new _convert_qmark_to_format() method handles the conversion while
preserving PostgreSQL's hstore/jsonb operators (?, ?|, ?&) by:
- Keeping ? followed by | or & as-is (operator context)
- Keeping ? preceded by alphanumeric/underscore as-is (identifier context)
- Replacing all other ? with %s (parameter placeholder context)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(backend): remove _convert_qmark_to_format fallback from PostgreSQL backend
Placeholder conversion is now handled by the ActiveRecord layer via
convert_qmark_placeholder(). The backend no longer needs to do any
? → %s conversion in _prepare_sql_and_params.
Remove _convert_qmark_to_format static method and _prepare_sql_and_params
override, restoring base class pass-through behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(dialect): handle base ForUpdateClause without strength attribute in format_for_update_clause
PostgreSQL dialect's format_for_update_clause assumed clause.strength
always exists, but the base ForUpdateClause class (used by
Query.for_update()) does not have this attribute — only
PostgresForUpdateClause does. Use getattr() with a default of
LockStrength.UPDATE for compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: install testsuite from git branch release/v1.0.0.dev12
Switch from PyPI package to git+https source for
rhosocial-activerecord-testsuite to track the new dev12 branch.
* refactor(backend): use in_transaction property instead of direct _transaction_manager access
Replace direct `_transaction_manager.is_active` checks with the
`in_transaction` property in both sync and async backends. The property
already handles the None case for `_transaction_manager`, making the
code simpler and consistent with the project's abstraction conventions.
* ci: install rhosocial-activerecord from git branch release/v1.0.0.dev25
* ci: add postgres-exts service container for extension testing
Add a second PostgreSQL service using vistart/postgres-exts image
alongside the official postgres container, allowing tests against
databases with additional extensions installed.
- postgres service: port 5432 (official image)
- postgres-exts service: port 5433 (vistart/postgres-exts image)
- Both use same version tag from matrix, same credentials
- Added postgres_exts scenario to CI config
- Wait step now checks both services are ready
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add changelog fragment for PR #32
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(backend): extract shared non-I/O methods into mixins for sync/async parity
Move identical non-I/O methods from sync and async implementations into
shared mixin classes to enforce proper sync/async parity:
- PostgresBackendMixin: log, _register_postgres_adapters,
_is_connection_error, _is_connection_error_message,
_parse_explain_result, CONNECTION_ERROR_SQLSTATES
- PostgresTransactionMixin (new): _ISOLATION_LEVELS, _build_begin_sql,
set_deferrable, get_deferred_constraints, get_active_savepoint
Also fix AsyncPostgresTransactionManager.set_deferrable being incorrectly
marked async (no I/O in method body), and fix member order inconsistency
in async_backend.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(pool): add PostgreSQL connection pool stress test examples
* fix: MRO ordering for ExplainBackendMixin and Windows event loop compatibility
Changes:
- Fix MRO ordering: PostgresBackendMixin must come before ExplainBackendMixin
to ensure _parse_explain_result returns PostgresExplainResult instead of
BaseExplainResult
- Add WindowsSelectorEventLoopPolicy setup in tests/conftest.py for Windows
compatibility with async tests
- Fix test: set_deferrable() is sync, not async (PostgresTransactionManager)
* refactor(named_procedure): update CLI to use simplified runner.run() API
- Updated PostgreSQL CLI to match new runner.run(backend) signature
- Removed execute_query, get_dialect parameters from handle_named_procedure
- Updated docs examples to use new API
* chore: resolve ruff warnings and add F401 fixes
- Add F401 fixes to __all__ in named_connections/__init__.py, mixins/__init__.py, protocols/__init__.py
- Fix unused imports in various files (from ruff --fix)
- Update extension examples to use 4-section format consistently
* feat(postgres): add schema namespace support for PostgreSQL backend (#33)
* test(schema): add schema namespace tests for PostgresBackend
Add test_schema_namespace.py covering:
- Dialect layer: schema capability declarations, Column/TableExpression/
WildcardExpression with schema_name, CreateSchemaExpression/
DropSchemaExpression SQL generation (including IF NOT EXISTS/IF EXISTS
CASCADE)
- Backend layer: get_default_schema(), CREATE/DROP schema, CRUD in
custom schema, information_schema introspection with schema
- Async Backend layer: async equivalents of backend operations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(schema): add get_default_schema() and schema config to PostgresBackend
- PostgresBackendMixin.get_default_schema(): returns configured
default_schema or 'public' as PostgreSQL standard default
- PostgresConnectionConfig: add search_path and default_schema fields
- Pass search_path to psycopg connection params
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(postgres): add UUID type adapter and gen_random_uuid function factory
Add PostgresUUIDAdapter for bidirectional conversion between Python
UUID objects and database values. Add gen_random_uuid() and
uuid-ossp extension function factories that return Expression objects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(postgres): add pgvector type, adapter, and function factories
Add PostgresVector dataclass, PostgresVectorAdapter, and pgvector
function factories (l2_distance, cosine_distance, inner_product,
cosine_similarity, literal) that return Expression objects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(postgres): add PostGIS type, adapter, and function factories
Add PostgresGeometry dataclass, PostgresGeometryAdapter, and PostGIS
function factories (ST_MakePoint, ST_Contains, ST_Distance, etc.)
that return Expression objects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(postgres): rewrite hstore module with Expression/Dialect architecture
Replace raw string returns with Expression objects (FunctionCall,
BinaryExpression) in hstore function factories. Add PostgresHstore
dataclass, PostgresHstoreAdapter, and simplify hstore extension mixin
to only keep supports_*() methods.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(postgres): add extension-aware function version system
Replace flat _POSTGRES_FUNCTION_VERSIONS dict with categorized
FunctionVersionRequirement definitions in function_versions.py.
Functions from extensions (hstore, uuid-ossp, pgvector, PostGIS) now
require extension installation check via introspect_and_adapt().
Key changes:
- FunctionVersionRequirement: structured version req with extension awareness
- FunctionSupportInfo: detailed support status with reason for unavailability
- supports_functions() returns Dict[str, FunctionSupportInfo]
- Extension functions return False when _extensions not probed (no guessing)
- _POSTGRES_FUNCTION_VERSIONS moved to function_versions.py, 15 categories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(tests): move transaction tests into transaction/ subdirectory
Reorganize transaction-related test files into a dedicated transaction/
subdirectory under feature/backend/ for better test structure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(postgres): align Expression-Dialect architecture across all function modules
Implement all 6 phases of the expression-dialect alignment plan:
Phase 1: Rewrite core native type function factories (geometric, bit_string,
enum, array, json, text_search, xml, network) to return Expression objects
instead of raw SQL strings.
Phase 2-3: Create new function factory modules for all extension types
(pgcrypto, ltree, pg_trgm, fuzzystrmatch, earthdistance, intarray, cube,
citext, pg_stat_statements, postgis_raster, pglogical, pgrouting, tablefunc,
pg_cron, pgaudit, hypopg, pg_surgery, pg_walinspect, address_standardizer,
pg_partman, pg_repack, orafce) and add data_type function factories
(xid8_literal, array_literal).
Phase 4: Migrate multirange/data_type format_* methods from mixins to
function factories, retaining only supports_* and DDL methods in mixins.
Phase 5: Update all tests to use Expression-based assertions (to_sql(),
isinstance checks) instead of raw string comparison.
Phase 6: Add 4 new protocol definitions (PostgresFullTextSearchSupport,
PostgresRangeTypeSupport, PostgresJSONBEnhancedSupport,
PostgresArrayEnhancedSupport) with corresponding supports_* methods in
PostgresDialect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(backend): add protocol implementation completeness tests for PostgreSQL
Add three test classes to verify bidirectional consistency between
Protocol declarations and Mixin/Dialect implementations:
- TestProtocolMethodSignatureConformance: verifies method signatures
on PostgresDialect match Protocol declarations
- TestProtocolMixinForwardCoverage: verifies Protocol-declared methods
exist in corresponding Mixins (42 Protocol-Mixin pairs)
- TestProtocolMixinReverseCoverage: verifies Mixin-implemented methods
are declared in corresponding Protocols
Also fix protocol implementation gaps discovered by the tests:
New Mixins (5):
- PostgresFullTextSearchMixin: 4 methods migrated from Dialect
- PostgresRangeTypeMixin: 4 methods migrated from Dialect
- PostgresJSONBEnhancedMixin: 5 methods migrated + 1 new
(supports_jsonb_subscript)
- PostgresArrayEnhancedMixin: 5 methods migrated + 1 new
(supports_domain_arrays)
- PostgresParallelQueryMixin: 6 new methods (all previously missing)
Protocol declarations completed (8):
- PostgresExtensionSupport: +2 methods
- PostgresPartitionSupport: +1 method
- PostgresIndexSupport: +6 methods
- PostgresDataTypeSupport: +1 method
- PostgresMultirangeSupport: +2 methods
- PostgresEnumTypeSupport: +10 methods
- PostgresQueryOptimizationSupport: +6 methods
- PostgresSQLSyntaxSupport: +9 methods
Upgrade get_all_protocol_methods() to walk MRO for Python < 3.12,
and add get_own_protocol_methods() for forward coverage testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve all ruff lint errors across postgres backend
- Rename PostgresGeometryAdapter (postgis) to PostgresPostGISAdapter
to avoid name collision with geometric adapter (F811)
- Rename orafce trunc/round to orafce_trunc/orafce_round to avoid
collision with math_enhanced module (F811)
- Add TYPE_CHECKING imports for FunctionSupportInfo and
FunctionVersionRequirement in dialect.py (F821)
- Remove unused imports: List, Optional, Dict, re, field (F401)
- Remove unused variable assignment for provider in cli/info.py (F841)
- Add `from None` to raise inside except in types/postgis.py (B904)
- Fix f-string without placeholders in examples/types/uuid.py (F541)
- Break long lines across adapters, cli, introspection, functions,
mixins, and protocols modules (E501)
- Configure ruff per-file-ignores for examples/ (E402, E501) and
migrate select/ignore to [tool.ruff.lint] section
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restore quoted type annotations and update test references
- Add quotes back around FunctionSupportInfo and
FunctionVersionRequirement in dialect.py type annotations,
since they are imported under TYPE_CHECKING and not available
at runtime (Python 3.8+ evaluates annotations eagerly)
- Update test_postgis_types.py to use PostgresPostGISAdapter
instead of the old PostgresGeometryAdapter name
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): update function support tests for FunctionSupportInfo return type
supports_functions() returns Dict[str, FunctionSupportInfo] (not bool),
so tests must check .supported attribute instead of using `is True`/`is False`.
- Replace `result[func] is True` with `result[func].supported`
- Replace `result[func] is False` with `not result[func].supported`
- Update hstore extension tests: without database connection,
extension functions return extension_not_probed instead of True
- Add FunctionSupportInfo import and type check test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(postgres): override format_column() with alias-aware schema handling
PostgreSQL rejects three-segment column references (schema.table.column)
when the table has an alias in the FROM/JOIN clause. Override
format_column() to implement PostgreSQL-specific rules:
- When table has an alias, use alias.column (ignore schema_name)
- When table has no alias but has schema, use schema.table.column
- Otherwise use standard table.column or column form
This fixes the JOIN bug where ON conditions generated
"public"."users"."id" instead of "users"."id" when tables had aliases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(postgres): add async integration tests and expression-based fixtures for all 29 extensions
Add async integration tests matching existing sync tests for all 29
extension test files, including async fixtures using
async_postgres_backend_single. Convert raw SQL strings in fixtures and
test methods to expression objects (CreateTableExpression,
InsertExpression, QueryExpression, FunctionCall, etc.).
Key changes:
- Add async_ensure_extension_installed to test utils
- Add async_postgres_backend_single fixture to conftest
- Add 29 async test classes with _async table suffix convention
- Replace raw SQL with expression objects across all integration tests
- Use Literal.cast("type") for PostgreSQL type casts (cube, hstore,
ltree, vector, uuid, integer[], geography) instead of RawSQLExpression
- Use Subquery wrapper for ComparisonPredicate aliasing
- Use specific exception types (IntegrityError, DatabaseError) instead
of bare Exception in pytest.raises
- Fix earth_distance() assertion (returns meters, not miles)
- Fix hypopg_estimate_size assertion (maps to hypopg_relation_size)
Remaining RawSQLExpression usage (with TODO comments):
- COUNT(*) where Literal("*") gets parameterized
- PostgreSQL operators with aliases where ComparisonPredicate lacks .as_()
- Index opclass specifications (no structural expression support)
All 316 tests pass, 13 skipped (extensions not available/permissions).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): skip pg_stat_statements and pg_walinspect tests on CI env constraints
- pg_stat_statements: skip when extension requires shared_preload_libraries
- pg_walinspect: skip when LSN is rejected as future start LSN (Postgres 15)
Both failures are environment configuration issues, not code bugs.
* chore: add changelog fragment for PR #33
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(postgres): add SQL injection security fixes for PostgreSQL dialect
Add comprehensive SQL injection protection for the PostgreSQL backend:
- Add validation for EXCLUDE constraint index methods (using allowlist)
- Add validation for EXCLUDE constraint operators (using allowlist)
- Add _escape_sql_string() for DEFAULT constraint values
- Add _validate_data_type() for column data types
- Add security unit tests for constraint validation
- Add integration tests with real PostgreSQL database execution
Security fixes prevent SQL injection through:
- Index method allowlist validation (gist, btree, spgist, etc.)
- Operator allowlist validation (&&, =, @>, etc.)
- String escaping for DEFAULT values
- Type validation for column definitions
Fixes: #sql-injection-audit-remediation
* ci: use PyPI source for testsuite dependency
* ci: use PyPI source for activerecord dependency
* fix(postgres): support array data types in _validate_data_type
PostgreSQL array types like TEXT[] and INTEGER[] were rejected because
the base dialect's allowlist pattern didn't include brackets [].
Override _validate_data_type in PostgresDialect to support array types,
with a note that this can be removed once rhosocial-activerecord base
dialect includes this change in a future release.
* docs: update CHANGELOG for v1.0.0.dev12
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Add PostgreSQL backend support for named query/procedure, named connection, ConcurrencyAware protocol, extensive extension support (29 extensions), and align with core package architectural changes. Update CI to install rhosocial-activerecord from git branch release/v1.0.0.dev25 and add postgres-exts service container for extension testing.
This branch contains 43 commits covering:
New Features
backend execute named-queryandnamed-proceduresubcommands for PostgreSQL with custom epilogCreateExtensionExpressionandDropExtensionExpressionDDL classespg_available_extensionsvistart/postgres-extsservice container for extension testing in CIBug Fixes
? → %splaceholder conversion for psycopg in_prepare_sql_and_paramsForUpdateClausehandling without strength attribute@runtime_checkablemissing onPostgresAdvisoryLockSupportandPostgresMultirangeSupportRawSQLExpressionusage in extension examples (replaced with expression API)AsyncIntrospectionSupportexclusion in testsRefactoring
__main__.pytocli/subpackagePostgresBackendMixinin_transactionproperty instead of direct_transaction_manageraccess_convert_qmark_to_formatfallback (now uses proper dialect placeholder conversion)CI Changes
rhosocial-activerecordfromgit+https://github.com/rhosocial/python-activerecord.git@release/v1.0.0.dev25postgres-extsservice container for extension testingType of change
feat: A new featuredocs: Documentation only changes (if related to the new feature)test: Adding missing tests or correcting existing tests (for the new feature)chore: Changes to the build process or auxiliary tools and libraries such as documentation generation (if related to the new feature)ci: Changes to our CI configuration files and scripts (if related to the new feature)Breaking Change
Impact on End-Users:
No backward-incompatible changes are expected for typical end-user applications. All new features are additive.
Impact on Backend Developers (Custom Implementations):
PostgresBackendMixinnow includes threadsafety logic (moved fromPostgreSQLBackend)_convert_qmark_to_formatfallback has been removed; proper placeholder conversion via dialect is required__main__.pytocli/subpackageRelated Repositories/Packages
python-activerecord: Core package changes (PR #79 already merged to release/v1.0.0.dev25)python-activerecord-testsuite: Test suite installed from git branch release/v1.0.0.dev12python-activerecord-mysql: Sister PR fix(security): resolve SQL injection risks in PostgreSQL backend #35Testing
Test Plan:
tests/rhosocial/activerecord_postgres_test/feature/extensions/tests/rhosocial/activerecord_postgres_test/feature/connection/tests/rhosocial/activerecord_postgres_test/feature/protocol/tests/rhosocial/activerecord_postgres_test/feature/cli/Checklist
README.md, etc.).changelog.d/<issue_number>.<type>.md).