@memberjunction/sqlglot-ts 6.1.0-edge.5 → 6.1.0-edge.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/sqlglot-ts",
3
- "version": "6.1.0-edge.5",
3
+ "version": "6.1.0-edge.7",
4
4
  "description": "TypeScript wrapper for Python's sqlglot SQL transpiler. Manages a local Python FastAPI microservice to provide deterministic SQL dialect conversion.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1997,7 +1997,30 @@ def _transpile_if_exists_begin(m: _IfExistsMatch) -> tuple[str, list[dict], list
1997
1997
  body_sql, u2, d2 = _transpile_extprop_segment(raw_body)
1998
1998
  if not cond_full or not body_sql.strip():
1999
1999
  return "", (u1 + u2 + [{"kind": "IF-EXISTS-BEGIN", "snippet": m.group(0)[:80]}]), (d1 + d2)
2000
+ # PostgreSQL refuses CREATE INDEX on a table carrying pending (deferred) trigger events:
2001
+ # cannot CREATE INDEX "Entity" because it has pending trigger events
2002
+ # A migration that seeds FK-bearing rows and then indexes the REFERENCED table hits this,
2003
+ # because the FK checks queued by those inserts are still outstanding for COMMIT. SQL Server
2004
+ # has no equivalent restriction, so the T-SQL original is perfectly legal and the failure
2005
+ # appears only after conversion — and only at apply time, never in the converter's summary.
2006
+ # Flushing first is semantically free: SET CONSTRAINTS ALL IMMEDIATE runs the deferred checks
2007
+ # now instead of at COMMIT, so anything that would have failed still fails, just earlier and
2008
+ # attached to a clearer statement. Emitted only when the guarded body actually creates an
2009
+ # index, so ordinary guards are unchanged.
2010
+ # The flush is a TOP-LEVEL statement, deliberately outside the DO block. SET CONSTRAINTS is
2011
+ # transaction-scoped, and issuing it inside plpgsql does not clear events already queued by
2012
+ # earlier statements in the migration — verified live: emitting it inside the block left the
2013
+ # apply still failing with the same error. The committed ledger already uses the statement-level
2014
+ # form for exactly this purpose (see V202608042204__APIKey_Scope_RowFilterID.pg.sql), so this
2015
+ # matches shipped precedent rather than inventing a second convention.
2016
+ flush = (
2017
+ "-- Flush any pending deferred trigger events from prior DML so the index DDL below can proceed.\n"
2018
+ "SET CONSTRAINTS ALL IMMEDIATE;\n"
2019
+ if _re.search(r"\bCREATE\s+(?:UNIQUE\s+)?INDEX\b", body_sql, _re.IGNORECASE)
2020
+ else ""
2021
+ )
2000
2022
  do = (
2023
+ f"{flush}"
2001
2024
  "DO $$\nBEGIN\n"
2002
2025
  f" IF {cond_full} THEN\n"
2003
2026
  f" {body_sql.strip()}\n"
@@ -596,6 +596,34 @@ check("block-less IF NOT EXISTS(sys.indexes) CREATE INDEX → pg_indexes DO bloc
596
596
  must_not_contain=["sys.", "OBJECT_ID"],
597
597
  expect_unhandled=0)
598
598
 
599
+ # A guarded CREATE INDEX must flush deferred FK triggers first. PostgreSQL refuses
600
+ # `CREATE INDEX` on a table with pending trigger events ("cannot CREATE INDEX \"Entity\"
601
+ # because it has pending trigger events"), which a migration hits whenever it seeds
602
+ # FK-bearing rows and then indexes the REFERENCED table. SQL Server has no such rule, so
603
+ # the T-SQL is legal and the breakage appears only after conversion, at apply time. -----
604
+ check("guarded CREATE INDEX flushes deferred FK triggers",
605
+ "IF NOT EXISTS (\n"
606
+ " SELECT 1 FROM sys.indexes\n"
607
+ " WHERE name = 'IDX_AUTO_MJ_FKEY_Entity_ParentID'\n"
608
+ " AND object_id = OBJECT_ID('${flyway:defaultSchema}.Entity'))\n"
609
+ " CREATE INDEX IDX_AUTO_MJ_FKEY_Entity_ParentID\n"
610
+ " ON ${flyway:defaultSchema}.Entity ([ParentID]);",
611
+ must_contain=["SET CONSTRAINTS ALL IMMEDIATE;", "CREATE INDEX", "pg_indexes"],
612
+ expect_unhandled=0)
613
+
614
+ # ...and a guard that does NOT create an index must be left exactly as it was — the flush
615
+ # is emitted only where it is needed, not sprayed across every guard. -------------------
616
+ check("guard without CREATE INDEX emits no constraint flush",
617
+ "IF NOT EXISTS (\n"
618
+ " SELECT 1 FROM sys.columns\n"
619
+ " WHERE name = 'Foo' AND object_id = OBJECT_ID('${flyway:defaultSchema}.Entity'))\n"
620
+ "BEGIN\n"
621
+ " ALTER TABLE ${flyway:defaultSchema}.Entity ADD [Foo] INT NULL;\n"
622
+ "END",
623
+ must_contain=["DO $$", "ALTER TABLE"],
624
+ must_not_contain=["SET CONSTRAINTS"],
625
+ expect_unhandled=0)
626
+
599
627
  # Inline named DEFAULT constraint (issue #3252 RC3): T-SQL allows a name on a column
600
628
  # default (`CONSTRAINT [DF_x] DEFAULT (75)`); PG does NOT — it is a `syntax error at or
601
629
  # near "CONSTRAINT"`. The name must be stripped, leaving a bare (unnamed) DEFAULT.