iris-pgwire 1.5.2__tar.gz → 1.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/PKG-INFO +1 -1
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/__init__.py +1 -1
- iris_pgwire-1.5.4/src/iris_pgwire/conversions/vector_syntax.py +156 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/models/vector_query_request.py +15 -9
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/constructs.py +16 -4
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/normalizer.py +25 -7
- iris_pgwire-1.5.2/src/iris_pgwire/conversions/vector_syntax.py +0 -94
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/.gitignore +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/LICENSE +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/README.md +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/pyproject.toml +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/_column_naming.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/_noop_cursor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/_result_processing.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/_type_mapping.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/auth/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/auth/auth_selector.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/auth/gssapi_auth.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/auth/oauth_bridge.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/auth/scram.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/auth/wallet_credentials.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/backend_selector.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/bulk_executor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/catalog_functions.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/catalog_router.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/oid_generator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_attrdef.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_attribute.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_class.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_constraint.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_index.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_namespace.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/catalog/pg_type.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/column_validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/config.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/config_schema.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/constitutional.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/conversions/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/conversions/bulk_insert.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/conversions/date_horolog.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/conversions/ddl_idempotency.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/conversions/ddl_splitter.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/conversions/json_path.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/copy_handler.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/csv_processor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/dbapi_connection_pool.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/dbapi_executor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/debug_tracer.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/health_checker.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/integratedml.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/iris_constructs.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/iris_executor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/iris_log_handler.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/iris_user_management.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/migrations/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/migrations/__main__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/migrations/executor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/models/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/models/backend_config.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/models/connection_pool_state.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/models/dbapi_connection.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/models/ipm_metadata.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/observability.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/performance_monitor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/protocol.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/__main__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/code_quality_validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/documentation_validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/package_metadata_validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/security_validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/schema_mapper.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/server.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/alias_extractor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/api.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/boolean_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/cache.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/confidence_analyzer.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/config.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/constraint_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/copy_parser.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/date_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/ddl_parser.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/ddl_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/debug.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/default_values.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/enum_registry.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/enum_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/error_handler.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/identifier_normalizer.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/index_validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/interceptor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/logging_config.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/datatypes.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/document_filters.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/functions.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/metadata_cache.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/metrics.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/models.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/parser.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/performance_monitor.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/pipeline.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/refiner.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/reserved_words.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/returning_plan.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/skipped_table_set.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/statement_filter.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/transaction_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/type_translator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/validator.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/testing/__init__.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/testing/base_fixture_builder.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/type_mapping.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/vector_metrics.py +0 -0
- {iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/vector_optimizer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iris-pgwire
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.4
|
|
4
4
|
Summary: PostgreSQL Wire Protocol Server for InterSystems IRIS - Connect BI tools, Python frameworks, and PostgreSQL clients to IRIS databases
|
|
5
5
|
Project-URL: Homepage, https://github.com/intersystems-community/iris-pgwire
|
|
6
6
|
Project-URL: Documentation, https://github.com/intersystems-community/iris-pgwire#readme
|
|
@@ -6,7 +6,7 @@ Based on the specification from docs/iris_pgwire_plan.md and proven patterns fro
|
|
|
6
6
|
caretdev/sqlalchemy-iris.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "1.5.
|
|
9
|
+
__version__ = "1.5.4"
|
|
10
10
|
__author__ = "Thomas Dyar <thomas.dyar@intersystems.com>"
|
|
11
11
|
|
|
12
12
|
# Don't import server/protocol in __init__ to avoid sys.modules conflicts
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for translating PostgreSQL vector syntax (pgvector) to IRIS vector syntax.
|
|
3
|
+
|
|
4
|
+
IRIS HNSW index syntax (confirmed against live IRIS instance):
|
|
5
|
+
CREATE INDEX name ON [TABLE] schema.table (col)
|
|
6
|
+
AS HNSW(Distance='Cosine'[, M=16, efConstruction=64])
|
|
7
|
+
|
|
8
|
+
Distance values: 'Cosine' | 'DotProduct'
|
|
9
|
+
M and efConstruction are optional (IRIS defaults: M=16, efConstruction=64).
|
|
10
|
+
Distance is required for correct query-time behaviour.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from typing import Any, Literal, Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class HnswIndexSpec:
|
|
20
|
+
"""Parsed HNSW index specification."""
|
|
21
|
+
|
|
22
|
+
index_name: str
|
|
23
|
+
table_name: str # may include schema, e.g. "SQLUser.MyTable"
|
|
24
|
+
column_name: str
|
|
25
|
+
distance_metric: Literal["Cosine", "DotProduct"]
|
|
26
|
+
m: Optional[int] = None
|
|
27
|
+
ef_construction: Optional[int] = None
|
|
28
|
+
if_not_exists: bool = False
|
|
29
|
+
|
|
30
|
+
# Ignored PostgreSQL options (logged as warnings)
|
|
31
|
+
ignored_options: dict[str, Any] = field(default_factory=dict)
|
|
32
|
+
|
|
33
|
+
def to_iris_sql(self) -> str:
|
|
34
|
+
"""Convert to IRIS SQL.
|
|
35
|
+
|
|
36
|
+
IRIS syntax:
|
|
37
|
+
CREATE INDEX name ON table (col) AS HNSW(Distance='Cosine'[, M=N, efConstruction=N])
|
|
38
|
+
|
|
39
|
+
Distance is always emitted so IRIS uses the right index structure at
|
|
40
|
+
query time (VECTOR_COSINE → Cosine, VECTOR_DOT_PRODUCT → DotProduct).
|
|
41
|
+
"""
|
|
42
|
+
params = [f"Distance='{self.distance_metric}'"]
|
|
43
|
+
if self.m is not None:
|
|
44
|
+
params.append(f"M={self.m}")
|
|
45
|
+
if self.ef_construction is not None:
|
|
46
|
+
params.append(f"efConstruction={self.ef_construction}")
|
|
47
|
+
return (
|
|
48
|
+
f"CREATE INDEX {self.index_name} ON {self.table_name} "
|
|
49
|
+
f"({self.column_name}) AS HNSW({', '.join(params)})"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_postgres_sql(cls, sql: str) -> Optional["HnswIndexSpec"]:
|
|
54
|
+
"""Parse an HNSW index statement in either pgvector or hybrid form.
|
|
55
|
+
|
|
56
|
+
Form 1 — pgvector standard (USING hnsw before column list):
|
|
57
|
+
CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON [schema.]table
|
|
58
|
+
USING hnsw (col vector_cosine_ops) [WITH (m=16, ef_construction=64)]
|
|
59
|
+
|
|
60
|
+
Form 2 — hybrid / user-written (column list before USING):
|
|
61
|
+
CREATE INDEX name ON [schema.]table (col)
|
|
62
|
+
USING HNSW [WITH (M=16, efConstruction=64, Distance='COSINE')]
|
|
63
|
+
|
|
64
|
+
Returns HnswIndexSpec if matched, None if not an HNSW statement.
|
|
65
|
+
Raises ValueError for unsupported operators (e.g. vector_l2_ops).
|
|
66
|
+
"""
|
|
67
|
+
# Form 1: USING hnsw (col ops) [WITH (...)]
|
|
68
|
+
form1 = re.search(
|
|
69
|
+
r"CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?P<ine>IF\s+NOT\s+EXISTS\s+)?"
|
|
70
|
+
r"(?P<name>\w+)\s+ON\s+(?P<table>[\w.]+)\s+"
|
|
71
|
+
r"USING\s+hnsw\s*\(\s*(?P<col>\w+)\s+(?P<op>\w+)\s*\)"
|
|
72
|
+
r"(?:\s+WITH\s*\((?P<with>[^)]*)\))?",
|
|
73
|
+
sql,
|
|
74
|
+
re.IGNORECASE,
|
|
75
|
+
)
|
|
76
|
+
if form1:
|
|
77
|
+
op = form1.group("op").lower()
|
|
78
|
+
if op == "vector_l2_ops":
|
|
79
|
+
raise ValueError(
|
|
80
|
+
"IRIS does not support L2/Euclidean distance for HNSW indexes. "
|
|
81
|
+
"Use vector_cosine_ops or vector_ip_ops."
|
|
82
|
+
)
|
|
83
|
+
if op == "vector_cosine_ops":
|
|
84
|
+
distance: Literal["Cosine", "DotProduct"] = "Cosine"
|
|
85
|
+
elif op == "vector_ip_ops":
|
|
86
|
+
distance = "DotProduct"
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError(f"Unsupported vector operator for HNSW: {op}")
|
|
89
|
+
m, ef = _parse_with_options(form1.group("with") or "")
|
|
90
|
+
return cls(
|
|
91
|
+
index_name=form1.group("name"),
|
|
92
|
+
table_name=form1.group("table"),
|
|
93
|
+
column_name=form1.group("col"),
|
|
94
|
+
distance_metric=distance,
|
|
95
|
+
m=m,
|
|
96
|
+
ef_construction=ef,
|
|
97
|
+
if_not_exists=bool(form1.group("ine")),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Form 2: (col) USING HNSW [WITH (...)]
|
|
101
|
+
form2 = re.search(
|
|
102
|
+
r"CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?P<ine>IF\s+NOT\s+EXISTS\s+)?"
|
|
103
|
+
r"(?P<name>\w+)\s+ON\s+(?P<table>[\w.]+)\s*"
|
|
104
|
+
r"\(\s*(?P<col>\w+)\s*\)\s+"
|
|
105
|
+
r"USING\s+HNSW"
|
|
106
|
+
r"(?:\s+WITH\s*\((?P<with>[^)]*)\))?",
|
|
107
|
+
sql,
|
|
108
|
+
re.IGNORECASE,
|
|
109
|
+
)
|
|
110
|
+
if form2:
|
|
111
|
+
with_str = form2.group("with") or ""
|
|
112
|
+
m, ef = _parse_with_options(with_str)
|
|
113
|
+
distance = _parse_distance_from_with(with_str)
|
|
114
|
+
return cls(
|
|
115
|
+
index_name=form2.group("name"),
|
|
116
|
+
table_name=form2.group("table"),
|
|
117
|
+
column_name=form2.group("col"),
|
|
118
|
+
distance_metric=distance,
|
|
119
|
+
m=m,
|
|
120
|
+
ef_construction=ef,
|
|
121
|
+
if_not_exists=bool(form2.group("ine")),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _parse_with_options(with_str: str) -> tuple[Optional[int], Optional[int]]:
|
|
128
|
+
"""Extract M and efConstruction / ef_construction from a WITH clause body."""
|
|
129
|
+
m: Optional[int] = None
|
|
130
|
+
ef: Optional[int] = None
|
|
131
|
+
for part in with_str.split(","):
|
|
132
|
+
kv = re.match(r"\s*(\w+)\s*=\s*(\d+)", part.strip())
|
|
133
|
+
if not kv:
|
|
134
|
+
continue
|
|
135
|
+
key, val = kv.group(1).lower(), int(kv.group(2))
|
|
136
|
+
if key == "m":
|
|
137
|
+
m = val
|
|
138
|
+
elif key in ("efconstruction", "ef_construction"):
|
|
139
|
+
ef = val
|
|
140
|
+
return m, ef
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _parse_distance_from_with(with_str: str) -> Literal["Cosine", "DotProduct"]:
|
|
144
|
+
"""Extract Distance= value from a WITH clause body, defaulting to Cosine."""
|
|
145
|
+
dist = re.search(r"Distance\s*=\s*['\"]?(\w+)['\"]?", with_str, re.IGNORECASE)
|
|
146
|
+
if not dist:
|
|
147
|
+
return "Cosine"
|
|
148
|
+
val = dist.group(1).lower()
|
|
149
|
+
if val in ("dotproduct", "dot_product", "innerproduct", "inner_product"):
|
|
150
|
+
return "DotProduct"
|
|
151
|
+
return "Cosine"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def normalize_vector(vector: list[float]) -> list[float]:
|
|
155
|
+
"""Ensure vector is in a format IRIS can handle (list of floats)."""
|
|
156
|
+
return [float(x) for x in vector]
|
|
@@ -22,8 +22,10 @@ class VectorQueryRequest(BaseModel):
|
|
|
22
22
|
Vector similarity query after translation from pgvector to IRIS syntax.
|
|
23
23
|
|
|
24
24
|
Lifecycle:
|
|
25
|
-
1. Client sends pgvector query: SELECT * FROM docs ORDER BY embedding
|
|
26
|
-
2. PGWire translates to IRIS: SELECT TOP 5 * FROM docs ORDER BY VECTOR_COSINE(embedding, TO_VECTOR('[0.1,0.2]',
|
|
25
|
+
1. Client sends pgvector query: SELECT * FROM docs ORDER BY embedding <=> '[0.1,0.2]' LIMIT 5
|
|
26
|
+
2. PGWire translates to IRIS: SELECT TOP 5 * FROM docs ORDER BY (1 - VECTOR_COSINE(embedding, TO_VECTOR('[0.1,0.2]', DOUBLE)))
|
|
27
|
+
Note: <=> is cosine *distance* (0=identical); IRIS VECTOR_COSINE is cosine *similarity* (1=identical).
|
|
28
|
+
The (1 - ...) wrapper converts similarity → distance so ORDER BY ASC returns most-similar rows first.
|
|
27
29
|
3. VectorQueryRequest captures both original and translated SQL + telemetry
|
|
28
30
|
4. DBAPI executor uses translated_sql for execution
|
|
29
31
|
5. Response includes translation_time_ms for SLA validation
|
|
@@ -98,15 +100,19 @@ class VectorQueryRequest(BaseModel):
|
|
|
98
100
|
|
|
99
101
|
def operator_to_iris_function(self) -> str:
|
|
100
102
|
"""
|
|
101
|
-
Map pgvector operator to IRIS vector function.
|
|
103
|
+
Map pgvector operator to IRIS vector function name (for telemetry only).
|
|
104
|
+
|
|
105
|
+
Note: <=> maps to VECTOR_COSINE but the actual SQL rewrite wraps it as
|
|
106
|
+
(1 - VECTOR_COSINE(...)) to convert similarity → distance. This method
|
|
107
|
+
returns only the base function name for display/telemetry purposes.
|
|
102
108
|
|
|
103
109
|
Returns:
|
|
104
110
|
IRIS function name (VECTOR_L2, VECTOR_COSINE, VECTOR_DOT_PRODUCT)
|
|
105
111
|
"""
|
|
106
112
|
operator_map = {
|
|
107
|
-
"<->": "VECTOR_L2", # L2 distance (Euclidean)
|
|
108
|
-
"<=>": "VECTOR_COSINE", # Cosine distance
|
|
109
|
-
"<#>": "VECTOR_DOT_PRODUCT", # Inner product (max)
|
|
113
|
+
"<->": "VECTOR_L2", # L2 distance (Euclidean) — unsupported, raises error
|
|
114
|
+
"<=>": "VECTOR_COSINE", # Cosine distance → emitted as (1 - VECTOR_COSINE(...))
|
|
115
|
+
"<#>": "VECTOR_DOT_PRODUCT", # Inner product (max) → emitted as (-VECTOR_DOT_PRODUCT(...))
|
|
110
116
|
}
|
|
111
117
|
return operator_map[self.vector_operator]
|
|
112
118
|
|
|
@@ -145,9 +151,9 @@ class VectorQueryRequest(BaseModel):
|
|
|
145
151
|
json_schema_extra = {
|
|
146
152
|
"example": {
|
|
147
153
|
"request_id": "req-f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
148
|
-
"original_sql": "SELECT * FROM documents ORDER BY embedding
|
|
149
|
-
"translated_sql": "SELECT TOP 5 * FROM documents ORDER BY VECTOR_COSINE(embedding, TO_VECTOR('[0.1,0.2,0.3]',
|
|
150
|
-
"vector_operator": "
|
|
154
|
+
"original_sql": "SELECT * FROM documents ORDER BY embedding <=> '[0.1,0.2,0.3]' LIMIT 5",
|
|
155
|
+
"translated_sql": "SELECT TOP 5 * FROM documents ORDER BY (1 - VECTOR_COSINE(embedding, TO_VECTOR('[0.1,0.2,0.3]', DOUBLE)))",
|
|
156
|
+
"vector_operator": "<=>",
|
|
151
157
|
"vector_column": "embedding",
|
|
152
158
|
"query_vector": [0.1, 0.2, 0.3],
|
|
153
159
|
"vector_dimensions": 3,
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/constructs.py
RENAMED
|
@@ -253,15 +253,27 @@ class IRISSQLConstructRegistry:
|
|
|
253
253
|
notes="Strip IF NOT EXISTS from CREATE INDEX for IRIS compatibility",
|
|
254
254
|
)
|
|
255
255
|
|
|
256
|
-
# HNSW Index
|
|
256
|
+
# HNSW Index — Form 1: pgvector USING hnsw (col ops) [WITH (...)]
|
|
257
|
+
# Handles schema-qualified table names (schema.table)
|
|
257
258
|
self.add_construct(
|
|
258
259
|
name="HNSW_INDEX",
|
|
259
|
-
pattern=r"CREATE\s+INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)
|
|
260
|
-
replacement=None,
|
|
260
|
+
pattern=r"CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?\w+\s+ON\s+[\w.]+\s+USING\s+hnsw\s*\([^)]+\)(?:\s+WITH\s*\([^)]*\))?",
|
|
261
|
+
replacement=None,
|
|
261
262
|
post_process=self._translate_hnsw_index,
|
|
262
263
|
confidence=0.95,
|
|
263
264
|
construct_type=ConstructType.SYNTAX,
|
|
264
|
-
notes="Translate
|
|
265
|
+
notes="Translate pgvector USING hnsw (col ops) to IRIS AS HNSW(Distance=...)",
|
|
266
|
+
)
|
|
267
|
+
# HNSW Index — Form 2: col-first (col) USING HNSW [WITH (...)]
|
|
268
|
+
# e.g. CREATE INDEX i ON t (emb) USING HNSW WITH (M=16, Distance='COSINE')
|
|
269
|
+
self.add_construct(
|
|
270
|
+
name="HNSW_INDEX_COL_FIRST",
|
|
271
|
+
pattern=r"CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?\w+\s+ON\s+[\w.]+\s*\([^)]+\)\s+USING\s+HNSW(?:\s+WITH\s*\([^)]*\))?",
|
|
272
|
+
replacement=None,
|
|
273
|
+
post_process=self._translate_hnsw_index,
|
|
274
|
+
confidence=0.95,
|
|
275
|
+
construct_type=ConstructType.SYNTAX,
|
|
276
|
+
notes="Translate col-first (col) USING HNSW WITH (...) to IRIS AS HNSW(Distance=...)",
|
|
265
277
|
)
|
|
266
278
|
|
|
267
279
|
def _translate_hnsw_index(self, match: re.Match, full_sql: str) -> str:
|
|
@@ -330,20 +330,38 @@ class SQLTranslator:
|
|
|
330
330
|
return result, count
|
|
331
331
|
|
|
332
332
|
def _translate_vector_functions(self, sql: str) -> tuple[str, int]:
|
|
333
|
-
"""Translate pgvector function names to IRIS equivalents
|
|
333
|
+
"""Translate pgvector function names to IRIS equivalents.
|
|
334
|
+
|
|
335
|
+
cosine_distance / vector_cosine_distance are pgvector distance functions
|
|
336
|
+
(0.0 = identical). IRIS VECTOR_COSINE is a *similarity* function (1.0 =
|
|
337
|
+
identical), so we wrap the call as (1 - VECTOR_COSINE(...)) to preserve
|
|
338
|
+
correct distance semantics.
|
|
339
|
+
"""
|
|
334
340
|
count = 0
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
341
|
+
|
|
342
|
+
# Cosine distance functions: wrap as (1 - VECTOR_COSINE(...)) so that
|
|
343
|
+
# ORDER BY ASC still returns most-similar rows first.
|
|
344
|
+
for pg_func in ("vector_cosine_distance", "cosine_distance"):
|
|
345
|
+
pattern = rf"\b{pg_func}\b"
|
|
346
|
+
if re.search(pattern, sql, re.IGNORECASE):
|
|
347
|
+
sql = re.sub(pattern, "VECTOR_COSINE", sql, flags=re.IGNORECASE)
|
|
348
|
+
# Wrap each full VECTOR_COSINE(...) call produced by this substitution.
|
|
349
|
+
sql = re.sub(
|
|
350
|
+
r"VECTOR_COSINE(\s*\([^)]*\))",
|
|
351
|
+
r"(1 - VECTOR_COSINE\1)",
|
|
352
|
+
sql,
|
|
353
|
+
)
|
|
354
|
+
count += 1
|
|
355
|
+
|
|
356
|
+
# Simple renames — no semantic adjustment needed.
|
|
357
|
+
simple_renames = {
|
|
338
358
|
"vector_l2_distance": "VECTOR_L2_DISTANCE", # Error handled later if not supported
|
|
339
359
|
"l2_distance": "VECTOR_L2_DISTANCE",
|
|
340
360
|
"inner_product": "VECTOR_DOT_PRODUCT",
|
|
341
361
|
"vector_dims": "VECTOR_DIM",
|
|
342
362
|
"vector_norm": "VECTOR_NORM",
|
|
343
363
|
}
|
|
344
|
-
|
|
345
|
-
# Case-insensitive replacement
|
|
346
|
-
for pg_func, iris_func in vector_functions.items():
|
|
364
|
+
for pg_func, iris_func in simple_renames.items():
|
|
347
365
|
pattern = rf"\b{pg_func}\b"
|
|
348
366
|
if re.search(pattern, sql, re.IGNORECASE):
|
|
349
367
|
sql = re.sub(pattern, iris_func, sql, flags=re.IGNORECASE)
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Utilities for translating PostgreSQL vector syntax (pgvector) to IRIS vector syntax.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import re
|
|
6
|
-
from dataclasses import dataclass, field
|
|
7
|
-
from typing import Any, Literal, Optional
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class HnswIndexSpec:
|
|
12
|
-
"""Parsed HNSW index specification."""
|
|
13
|
-
|
|
14
|
-
index_name: str
|
|
15
|
-
table_name: str
|
|
16
|
-
column_name: str
|
|
17
|
-
distance_metric: Literal["COSINE", "DOT_PRODUCT"]
|
|
18
|
-
if_not_exists: bool = False
|
|
19
|
-
|
|
20
|
-
# Ignored PostgreSQL options (logged as warnings)
|
|
21
|
-
ignored_options: dict[str, Any] = field(default_factory=dict)
|
|
22
|
-
|
|
23
|
-
def to_iris_sql(self) -> str:
|
|
24
|
-
"""
|
|
25
|
-
Convert to IRIS SQL index creation syntax.
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
IRIS SQL string (e.g., "CREATE INDEX idx ON table (col) AS HNSW")
|
|
29
|
-
"""
|
|
30
|
-
# Note: IRIS doesn't specify distance metric in the CREATE INDEX statement
|
|
31
|
-
# It's inferred from usage, but we validate support here.
|
|
32
|
-
# Actually, IRIS HNSW index syntax is simply:
|
|
33
|
-
# CREATE INDEX idxname ON table (col) AS HNSW
|
|
34
|
-
return f"CREATE INDEX {self.index_name} ON {self.table_name} ({self.column_name}) AS HNSW"
|
|
35
|
-
|
|
36
|
-
@classmethod
|
|
37
|
-
def from_postgres_sql(cls, sql: str) -> Optional["HnswIndexSpec"]:
|
|
38
|
-
"""
|
|
39
|
-
Parse a PostgreSQL HNSW index creation statement.
|
|
40
|
-
|
|
41
|
-
Example: CREATE INDEX IF NOT EXISTS idx ON documents USING hnsw (embedding vector_cosine_ops)
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
sql: PostgreSQL CREATE INDEX statement
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
HnswIndexSpec if matched, else None
|
|
48
|
-
"""
|
|
49
|
-
# Regex to capture parts
|
|
50
|
-
pattern = (
|
|
51
|
-
r"CREATE\s+INDEX\s+(?P<if_not_exists>IF\s+NOT\s+EXISTS\s+)?(?P<index_name>\w+)\s+ON\s+"
|
|
52
|
-
r"(?P<table_name>\w+)\s+USING\s+hnsw\s*\((?P<column_name>\w+)\s+(?P<operator>\w+)\)"
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
match = re.search(pattern, sql, re.IGNORECASE)
|
|
56
|
-
if not match:
|
|
57
|
-
return None
|
|
58
|
-
|
|
59
|
-
operator = match.group("operator").lower()
|
|
60
|
-
|
|
61
|
-
if operator == "vector_l2_ops":
|
|
62
|
-
raise ValueError(
|
|
63
|
-
"IRIS does not support L2/Euclidean distance for HNSW indexes. "
|
|
64
|
-
"Use vector_cosine_ops or vector_ip_ops."
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
distance_metric: Literal["COSINE", "DOT_PRODUCT"]
|
|
68
|
-
if operator == "vector_cosine_ops":
|
|
69
|
-
distance_metric = "COSINE"
|
|
70
|
-
elif operator == "vector_ip_ops":
|
|
71
|
-
distance_metric = "DOT_PRODUCT"
|
|
72
|
-
else:
|
|
73
|
-
raise ValueError(f"Unsupported vector operator for HNSW: {operator}")
|
|
74
|
-
|
|
75
|
-
return cls(
|
|
76
|
-
index_name=match.group("index_name"),
|
|
77
|
-
table_name=match.group("table_name"),
|
|
78
|
-
column_name=match.group("column_name"),
|
|
79
|
-
distance_metric=distance_metric,
|
|
80
|
-
if_not_exists=bool(match.group("if_not_exists")),
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def normalize_vector(vector: list[float]) -> list[float]:
|
|
85
|
-
"""
|
|
86
|
-
Ensure vector is in a format IRIS can handle (list of floats).
|
|
87
|
-
|
|
88
|
-
Args:
|
|
89
|
-
vector: List of floats
|
|
90
|
-
|
|
91
|
-
Returns:
|
|
92
|
-
Normalized vector
|
|
93
|
-
"""
|
|
94
|
-
return [float(x) for x in vector]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/quality/package_metadata_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/boolean_translator.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/confidence_analyzer.py
RENAMED
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/constraint_translator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/identifier_normalizer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/datatypes.py
RENAMED
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/document_filters.py
RENAMED
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/mappings/functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/performance_monitor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iris_pgwire-1.5.2 → iris_pgwire-1.5.4}/src/iris_pgwire/sql_translator/transaction_translator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|