Skip to content

Commit c69d8b4

Browse files
committed
Complete DDL Compatibility Enhancements (Feature 036) - Implementation, Tests, and Documentation
1 parent c3d2b96 commit c69d8b4

File tree

139 files changed

+1132
-521
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

139 files changed

+1132
-521
lines changed

AGENTS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# iris-pgwire-gh Development Guidelines
22

3-
Auto-generated from all feature plans. Last updated: 2026-01-02
3+
Auto-generated from all feature plans. Last updated: 2026-01-17
44

55
## Active Technologies
66
- Python 3.11 + python>=3.11, psycopg[binary], iris-devtester, intersystems-irispython (026-address-gaps-in)
@@ -10,6 +10,7 @@ Auto-generated from all feature plans. Last updated: 2026-01-02
1010
- Python 3.11 + intersystems-irispython, psycopg[binary], iris-devtester (035-number-1-short)
1111
- Python 3.11 + psycopg[binary], intersystems-irispython, iris-devtester (036-address-all-6)
1212
- InterSystems IRIS (via pgwire) (036-address-all-6)
13+
- Python 3.11 + psycopg[binary], intersystems-irispython, iris-devtester (036-address-all-6)
1314

1415
- Python 3.11+ + `iris-devtester`, `intersystems-irispython`, `psycopg[binary]` (033-devtester-skills)
1516

@@ -30,8 +31,8 @@ Python 3.11+: Follow standard conventions
3031

3132
## Recent Changes
3233
- 036-address-all-6: Added Python 3.11 + psycopg[binary], intersystems-irispython, iris-devtester
34+
- 036-address-all-6: Added Python 3.11 + psycopg[binary], intersystems-irispython, iris-devtester
3335
- 035-number-1-short: Added Python 3.11 + intersystems-irispython, psycopg[binary], iris-devtester
34-
- 034-issues-that-likely: Added Python 3.11 + intersystems-irispython, psycopg[binary], iris-devtester
3536

3637

3738
<!-- MANUAL ADDITIONS START -->

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.0.7] - 2026-01-17
9+
10+
### Added
11+
- **PostgreSQL DDL Compatibility Enhancement**: Implemented automatic interception and transformation of PostgreSQL-specific DDL constructs to enable seamless migrations.
12+
- **Generated Column Stripping**: Added support for automatically removing `GENERATED ALWAYS AS ... STORED` column definitions from `CREATE TABLE` statements.
13+
- **Enum Type Registration**: Added logic to skip `CREATE TYPE ... AS ENUM`, register the type name, and automatically map subsequent columns using that type to `VARCHAR(64)`.
14+
- **Index Dependency Tracking**: Implemented `SkippedTableSet` to track tables whose creation was skipped, ensuring dependent `CREATE INDEX` statements are also skipped.
15+
- **Strict DDL Mode**: Added a configurable `strict_ddl` flag (default `false`) to control whether unsupported constructs should be skipped with a warning or raise an error.
16+
- **Construct Stripping**: Added automatic stripping of `USING btree`, PostgreSQL type casts (`::type`), and `WITH (fillfactor)` from DDL statements.
17+
818
## [1.0.6] - 2026-01-16
919

1020
### Added

KNOWN_LIMITATIONS.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,30 @@ Based on comprehensive research of 9 major implementations (November 2025):
4949

5050
---
5151

52+
## 🟢 PostgreSQL DDL Compatibility
53+
54+
**Status**: Enhanced Compatibility ✅
55+
**Feature**: Automated transformations for PostgreSQL-specific DDL
56+
57+
### Capabilities
58+
59+
IRIS PGWire automatically handles several common PostgreSQL DDL constructs that are natively unsupported by IRIS:
60+
61+
-**Generated Columns**: `GENERATED ALWAYS AS ... STORED` are automatically stripped.
62+
-**Enum Types**: `CREATE TYPE ... ENUM` are registered and columns mapped to `VARCHAR(64)`.
63+
-**Storage Parameters**: `WITH (fillfactor = ...)` and `SET (fillfactor)` are stripped/skipped.
64+
-**Index Methods**: `USING btree` is automatically removed.
65+
-**Casts**: `'value'::text` syntax in defaults is stripped.
66+
-**CHECK Constraints**: `ADD CONSTRAINT ... CHECK` are skipped with a warning.
67+
68+
### Configuration
69+
70+
Use the `strict_ddl` flag to control this behavior:
71+
- `strict_ddl = false` (default): Skip/transform with a warning.
72+
- `strict_ddl = true`: Raise an error for unsupported DDL.
73+
74+
---
75+
5276
## 🟡 INFORMATION_SCHEMA Compatibility
5377

5478
**Severity**: Medium

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ See [Client Compatibility Guide](https://github.com/intersystems-community/iris-
108108

109109
- **pgvector Syntax**: Use familiar `<=>` and `<#>` operators - auto-translated to IRIS VECTOR_COSINE/DOT_PRODUCT. HNSW indexes provide 5× speedup on 100K+ vectors. See [Vector Operations Guide](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/VECTOR_PARAMETER_BINDING.md)
110110

111-
- **ORM Compatibility**: Automatic `public``SQLUser` schema mapping for Prisma, SQLAlchemy, Drizzle, Hibernate introspection. Catalog emulation with 6 pg_catalog tables + 5 functions. See [pg_catalog Documentation](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/PG_CATALOG.md)
111+
- **ORM & DDL Compatibility**: Automatic `public``SQLUser` schema mapping and PostgreSQL DDL transformations (stripping `fillfactor`, `GENERATED` columns, `USING btree`, etc.) for seamless migrations. See [DDL Compatibility Guide](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/DDL_COMPATIBILITY.md)
112112

113113
- **Enterprise Security**: SCRAM-SHA-256, OAuth 2.0, IRIS Wallet authentication. Industry-standard security matching PgBouncer, YugabyteDB. See [Deployment Guide](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/DEPLOYMENT.md)
114114

@@ -201,7 +201,8 @@ async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
201201
- **[BI Tools Setup](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/BI_TOOLS.md)** - Superset, Metabase, Grafana integration
202202

203203
### Features & Capabilities
204-
- **[Features Overview](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/FEATURES_OVERVIEW.md)** - pgvector, ORM compatibility, authentication
204+
- **[Features Overview](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/FEATURES_OVERVIEW.md)** - pgvector, ORM compatibility, DDL transformations, authentication
205+
- **[DDL Compatibility](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/DDL_COMPATIBILITY.md)** - Automatic handling of PostgreSQL-specific DDL (fillfactor, generated columns, enums)
205206
- **[pg_catalog Support](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/PG_CATALOG.md)** - 6 catalog tables + 5 functions for ORM introspection
206207
- **[Vector Operations](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/VECTOR_PARAMETER_BINDING.md)** - High-dimensional vectors, parameter binding
207208
- **[Client Compatibility](https://github.com/intersystems-community/iris-pgwire/blob/main/docs/CLIENT_RECOMMENDATIONS.md)** - 171 tests across 8 languages

benchmarks/3way_comparison.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,31 @@
1111
Per FR-006: Aborts on connection failure.
1212
"""
1313

14-
import sys
1514
import argparse
15+
import sys
1616
from pathlib import Path
17-
from typing import Dict, List
1817

1918
# Add benchmarks to path
2019
sys.path.insert(0, str(Path(__file__).parent.parent))
2120

2221
from benchmarks.config import BenchmarkConfiguration, ConnectionConfig
23-
from benchmarks.runner import BenchmarkRunner
22+
from benchmarks.executors.dbapi_executor import DbapiExecutor
2423
from benchmarks.executors.pgwire_executor import PGWireExecutor
2524
from benchmarks.executors.postgres_executor import PostgresExecutor
26-
from benchmarks.executors.dbapi_executor import DbapiExecutor
25+
from benchmarks.output.json_exporter import export_json
26+
from benchmarks.output.table_exporter import export_table
27+
from benchmarks.runner import BenchmarkRunner
2728
from benchmarks.test_data.query_templates import (
29+
COMPLEX_QUERIES,
2830
SIMPLE_QUERIES,
2931
VECTOR_QUERIES,
30-
COMPLEX_QUERIES,
31-
format_query_for_method
32+
format_query_for_method,
3233
)
3334
from benchmarks.test_data.vector_generator import generate_query_vector, vector_to_text
34-
from benchmarks.output.json_exporter import export_json
35-
from benchmarks.output.table_exporter import export_table
3635
from benchmarks.validate_connections import validate_all_connections
3736

3837

39-
def create_test_queries(method: str, dimensions: int = 1024) -> Dict[str, List[str]]:
38+
def create_test_queries(method: str, dimensions: int = 1024) -> dict[str, list[str]]:
4039
"""
4140
Create test queries for a specific database method.
4241

benchmarks/async_sqlalchemy_stress_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616
python3 benchmarks/async_sqlalchemy_stress_test.py
1717
"""
1818

19+
import asyncio
1920
from random import randint, random
2021
from time import time_ns
21-
import asyncio
22-
import psycopg
2322

24-
from sqlalchemy import MetaData, Table, Column, Integer, String, text, bindparam
23+
import psycopg
24+
from sqlalchemy import Column, Integer, MetaData, String, Table, bindparam, text
2525
from sqlalchemy.ext.asyncio import create_async_engine
2626

2727
# Test configuration

benchmarks/config.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"""
77

88
from dataclasses import dataclass
9-
from typing import Dict, List, Optional
109
from datetime import datetime
1110
from enum import Enum
1211

@@ -34,11 +33,11 @@ class ConnectionConfig:
3433
host: str
3534
port: int
3635
database: str
37-
username: Optional[str] = None
38-
password: Optional[str] = None
36+
username: str | None = None
37+
password: str | None = None
3938
connection_timeout: float = 10.0
4039

41-
def validate(self) -> List[str]:
40+
def validate(self) -> list[str]:
4241
"""
4342
Validate configuration parameters.
4443
@@ -69,9 +68,9 @@ class BenchmarkConfiguration:
6968
concurrent_connections: int = 1
7069
warmup_queries: int = 100
7170
random_seed: int = 42
72-
connection_configs: Optional[Dict[str, ConnectionConfig]] = None
71+
connection_configs: dict[str, ConnectionConfig] | None = None
7372

74-
def validate(self) -> List[str]:
73+
def validate(self) -> list[str]:
7574
"""
7675
Validate configuration per functional requirements.
7776
@@ -120,11 +119,11 @@ class PerformanceResult:
120119
timestamp: datetime
121120
elapsed_ms: float
122121
success: bool
123-
error_message: Optional[str] = None
122+
error_message: str | None = None
124123
row_count: int = 0
125-
resource_usage: Optional[Dict] = None
124+
resource_usage: dict | None = None
126125

127-
def validate(self) -> List[str]:
126+
def validate(self) -> list[str]:
128127
"""
129128
Validate result integrity.
130129
@@ -165,9 +164,9 @@ class MethodResults:
165164
latency_p50_ms: float
166165
latency_p95_ms: float
167166
latency_p99_ms: float
168-
by_category: Dict[str, CategoryMetrics]
167+
by_category: dict[str, CategoryMetrics]
169168

170-
def validate(self) -> List[str]:
169+
def validate(self) -> list[str]:
171170
"""
172171
Validate aggregated results.
173172
@@ -205,12 +204,12 @@ class BenchmarkReport:
205204
start_time: datetime
206205
end_time: datetime
207206
total_duration_seconds: float
208-
method_results: Dict[str, MethodResults]
209-
raw_results: List[PerformanceResult]
210-
validation_errors: List[str]
207+
method_results: dict[str, MethodResults]
208+
raw_results: list[PerformanceResult]
209+
validation_errors: list[str]
211210
state: BenchmarkState = BenchmarkState.INITIALIZING
212211

213-
def to_json(self) -> Dict:
212+
def to_json(self) -> dict:
214213
"""
215214
Export report as JSON (FR-010).
216215
@@ -239,7 +238,7 @@ def to_json(self) -> Dict:
239238
}
240239
}
241240

242-
def to_table_rows(self) -> List[List]:
241+
def to_table_rows(self) -> list[list]:
243242
"""
244243
Export report as table rows for console display (FR-010).
245244

benchmarks/executors/dbapi_executor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Pattern from: /Users/tdyar/ws/rag-templates/common/iris_connection_manager.py
66
"""
77

8-
from typing import Any, Optional
8+
from typing import Any
99

1010

1111
class DbapiExecutor:
@@ -34,7 +34,7 @@ def __init__(
3434
self.namespace = namespace
3535
self.username = username
3636
self.password = password
37-
self.connection: Optional[Any] = None
37+
self.connection: Any | None = None
3838

3939
def connect(self):
4040
"""Establish DBAPI connection to IRIS."""
@@ -73,7 +73,7 @@ def execute(self, query: str) -> Any:
7373
results = cursor.fetchall()
7474
cursor.close()
7575
return results
76-
except Exception as e:
76+
except Exception:
7777
cursor.close()
7878
# Re-raise without printing (error logging handled by runner if needed)
7979
raise

benchmarks/executors/pgwire_executor.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
Executes queries via psycopg3 to PGWire server.
55
"""
66

7+
from typing import Any
8+
79
import psycopg
8-
from typing import Any, Optional
910

1011

1112
class PGWireExecutor:
@@ -31,7 +32,7 @@ def __init__(
3132
self.port = port
3233
self.database = database
3334
self.timeout_seconds = timeout_seconds
34-
self.connection: Optional[psycopg.Connection] = None
35+
self.connection: psycopg.Connection | None = None
3536

3637
def connect(self):
3738
"""Establish connection to PGWire server."""
@@ -68,7 +69,7 @@ def execute(self, query: str) -> Any:
6869
results = cursor.fetchall()
6970
cursor.close()
7071
return results
71-
except Exception as e:
72+
except Exception:
7273
cursor.close()
7374
raise
7475

benchmarks/executors/postgres_executor.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
Executes queries via psycopg3 against native PostgreSQL with pgvector.
55
"""
66

7+
from typing import Any
8+
79
import psycopg
8-
from typing import Any, Optional
910

1011

1112
class PostgresExecutor:
@@ -34,7 +35,7 @@ def __init__(
3435
self.database = database
3536
self.username = username
3637
self.password = password
37-
self.connection: Optional[psycopg.Connection] = None
38+
self.connection: psycopg.Connection | None = None
3839

3940
def connect(self):
4041
"""Establish connection to PostgreSQL."""
@@ -76,7 +77,7 @@ def execute(self, query: str) -> Any:
7677
results = cursor.fetchall()
7778
cursor.close()
7879
return results
79-
except Exception as e:
80+
except Exception:
8081
cursor.close()
8182
raise
8283

0 commit comments

Comments
 (0)