iris-pgwire 1.5.2__tar.gz → 1.5.3__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.
Files changed (119) hide show
  1. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/PKG-INFO +1 -1
  2. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/__init__.py +1 -1
  3. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/models/vector_query_request.py +15 -9
  4. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/normalizer.py +25 -7
  5. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/.gitignore +0 -0
  6. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/LICENSE +0 -0
  7. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/README.md +0 -0
  8. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/pyproject.toml +0 -0
  9. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/_column_naming.py +0 -0
  10. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/_noop_cursor.py +0 -0
  11. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/_result_processing.py +0 -0
  12. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/_type_mapping.py +0 -0
  13. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/auth/__init__.py +0 -0
  14. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/auth/auth_selector.py +0 -0
  15. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/auth/gssapi_auth.py +0 -0
  16. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/auth/oauth_bridge.py +0 -0
  17. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/auth/scram.py +0 -0
  18. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/auth/wallet_credentials.py +0 -0
  19. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/backend_selector.py +0 -0
  20. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/bulk_executor.py +0 -0
  21. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/__init__.py +0 -0
  22. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/catalog_functions.py +0 -0
  23. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/catalog_router.py +0 -0
  24. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/oid_generator.py +0 -0
  25. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_attrdef.py +0 -0
  26. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_attribute.py +0 -0
  27. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_class.py +0 -0
  28. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_constraint.py +0 -0
  29. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_index.py +0 -0
  30. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_namespace.py +0 -0
  31. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/catalog/pg_type.py +0 -0
  32. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/column_validator.py +0 -0
  33. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/config.py +0 -0
  34. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/config_schema.py +0 -0
  35. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/constitutional.py +0 -0
  36. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/__init__.py +0 -0
  37. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/bulk_insert.py +0 -0
  38. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/date_horolog.py +0 -0
  39. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/ddl_idempotency.py +0 -0
  40. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/ddl_splitter.py +0 -0
  41. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/json_path.py +0 -0
  42. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/conversions/vector_syntax.py +0 -0
  43. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/copy_handler.py +0 -0
  44. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/csv_processor.py +0 -0
  45. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/dbapi_connection_pool.py +0 -0
  46. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/dbapi_executor.py +0 -0
  47. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/debug_tracer.py +0 -0
  48. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/health_checker.py +0 -0
  49. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/integratedml.py +0 -0
  50. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/iris_constructs.py +0 -0
  51. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/iris_executor.py +0 -0
  52. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/iris_log_handler.py +0 -0
  53. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/iris_user_management.py +0 -0
  54. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/migrations/__init__.py +0 -0
  55. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/migrations/__main__.py +0 -0
  56. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/migrations/executor.py +0 -0
  57. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/models/__init__.py +0 -0
  58. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/models/backend_config.py +0 -0
  59. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/models/connection_pool_state.py +0 -0
  60. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/models/dbapi_connection.py +0 -0
  61. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/models/ipm_metadata.py +0 -0
  62. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/observability.py +0 -0
  63. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/performance_monitor.py +0 -0
  64. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/protocol.py +0 -0
  65. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/__init__.py +0 -0
  66. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/__main__.py +0 -0
  67. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/code_quality_validator.py +0 -0
  68. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/documentation_validator.py +0 -0
  69. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/package_metadata_validator.py +0 -0
  70. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/security_validator.py +0 -0
  71. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/quality/validator.py +0 -0
  72. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/schema_mapper.py +0 -0
  73. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/server.py +0 -0
  74. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/__init__.py +0 -0
  75. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/alias_extractor.py +0 -0
  76. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/api.py +0 -0
  77. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/boolean_translator.py +0 -0
  78. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/cache.py +0 -0
  79. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/confidence_analyzer.py +0 -0
  80. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/config.py +0 -0
  81. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/constraint_translator.py +0 -0
  82. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/copy_parser.py +0 -0
  83. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/date_translator.py +0 -0
  84. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/ddl_parser.py +0 -0
  85. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/ddl_translator.py +0 -0
  86. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/debug.py +0 -0
  87. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/default_values.py +0 -0
  88. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/enum_registry.py +0 -0
  89. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/enum_translator.py +0 -0
  90. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/error_handler.py +0 -0
  91. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/identifier_normalizer.py +0 -0
  92. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/index_validator.py +0 -0
  93. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/interceptor.py +0 -0
  94. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/logging_config.py +0 -0
  95. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/mappings/__init__.py +0 -0
  96. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/mappings/constructs.py +0 -0
  97. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/mappings/datatypes.py +0 -0
  98. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/mappings/document_filters.py +0 -0
  99. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/mappings/functions.py +0 -0
  100. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/metadata_cache.py +0 -0
  101. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/metrics.py +0 -0
  102. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/models.py +0 -0
  103. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/parser.py +0 -0
  104. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/performance_monitor.py +0 -0
  105. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/pipeline.py +0 -0
  106. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/refiner.py +0 -0
  107. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/reserved_words.py +0 -0
  108. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/returning_plan.py +0 -0
  109. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/skipped_table_set.py +0 -0
  110. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/statement_filter.py +0 -0
  111. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/transaction_translator.py +0 -0
  112. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/translator.py +0 -0
  113. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/type_translator.py +0 -0
  114. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/sql_translator/validator.py +0 -0
  115. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/testing/__init__.py +0 -0
  116. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/testing/base_fixture_builder.py +0 -0
  117. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/type_mapping.py +0 -0
  118. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/src/iris_pgwire/vector_metrics.py +0 -0
  119. {iris_pgwire-1.5.2 → iris_pgwire-1.5.3}/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.2
3
+ Version: 1.5.3
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.2"
9
+ __version__ = "1.5.3"
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
@@ -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 <-> '[0.1,0.2]' LIMIT 5
26
- 2. PGWire translates to IRIS: SELECT TOP 5 * FROM docs ORDER BY VECTOR_COSINE(embedding, TO_VECTOR('[0.1,0.2]', 'DECIMAL'))
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 <-> '[0.1,0.2,0.3]' LIMIT 5",
149
- "translated_sql": "SELECT TOP 5 * FROM documents ORDER BY VECTOR_COSINE(embedding, TO_VECTOR('[0.1,0.2,0.3]', 'DECIMAL'))",
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,
@@ -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
- vector_functions = {
336
- "vector_cosine_distance": "VECTOR_COSINE",
337
- "cosine_distance": "VECTOR_COSINE",
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)
File without changes
File without changes
File without changes
File without changes