weaverstack 0.1.1__py3-none-any.whl

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 (127) hide show
  1. weaver/__init__.py +59 -0
  2. weaver/build_bundle/__init__.py +109 -0
  3. weaver/build_bundle/aliases.py +325 -0
  4. weaver/build_bundle/bundle.py +359 -0
  5. weaver/build_bundle/catalogue_actions.py +275 -0
  6. weaver/build_bundle/changes.py +186 -0
  7. weaver/build_bundle/endpoints.py +83 -0
  8. weaver/build_bundle/executors/__init__.py +69 -0
  9. weaver/build_bundle/executors/alias.py +202 -0
  10. weaver/build_bundle/executors/base.py +132 -0
  11. weaver/build_bundle/executors/folder.py +71 -0
  12. weaver/build_bundle/executors/load_file.py +205 -0
  13. weaver/build_bundle/executors/spark_case.py +26 -0
  14. weaver/build_bundle/executors/spark_schema.py +60 -0
  15. weaver/build_bundle/executors/spark_sql.py +59 -0
  16. weaver/build_bundle/executors/spark_sql_batch.py +57 -0
  17. weaver/build_bundle/executors/spark_table.py +213 -0
  18. weaver/build_bundle/executors/sql_endpoint_refresh.py +34 -0
  19. weaver/build_bundle/executors/tsql.py +81 -0
  20. weaver/build_bundle/incremental.py +288 -0
  21. weaver/build_bundle/installer.py +384 -0
  22. weaver/build_bundle/models.py +288 -0
  23. weaver/build_bundle/payloads.py +34 -0
  24. weaver/build_bundle/physical.py +625 -0
  25. weaver/build_bundle/planner.py +389 -0
  26. weaver/build_bundle/prune.py +620 -0
  27. weaver/build_bundle/report.py +108 -0
  28. weaver/build_bundle/stages.py +196 -0
  29. weaver/build_bundle/targets.py +272 -0
  30. weaver/build_bundle/workflow.py +585 -0
  31. weaver/catalogue/__init__.py +73 -0
  32. weaver/catalogue/builtin.py +238 -0
  33. weaver/catalogue/claims.py +121 -0
  34. weaver/catalogue/projection.py +437 -0
  35. weaver/catalogue/reader.py +152 -0
  36. weaver/catalogue/reconcile.py +231 -0
  37. weaver/catalogue/render.py +410 -0
  38. weaver/catalogue/state.py +660 -0
  39. weaver/catalogue/tables.py +648 -0
  40. weaver/config.py +178 -0
  41. weaver/declaration/__init__.py +171 -0
  42. weaver/declaration/columns.py +223 -0
  43. weaver/declaration/ddl.py +266 -0
  44. weaver/declaration/dependencies.py +544 -0
  45. weaver/declaration/graph.py +240 -0
  46. weaver/declaration/item_dependencies.py +292 -0
  47. weaver/declaration/load.py +191 -0
  48. weaver/declaration/metadata.py +1405 -0
  49. weaver/declaration/model.py +448 -0
  50. weaver/declaration/references.py +294 -0
  51. weaver/declaration/repository.py +959 -0
  52. weaver/declaration/schemas.py +135 -0
  53. weaver/declaration/source.py +674 -0
  54. weaver/declaration/spark_load.py +759 -0
  55. weaver/declaration/sql_shaping.py +591 -0
  56. weaver/declaration/templates/ddl/declared_create_table.sql +64 -0
  57. weaver/declaration/templates/ddl/infer_create_table.sql +97 -0
  58. weaver/declaration/templates/ddl/metadata_column_validation.sql +30 -0
  59. weaver/declaration/templates/load/column_metadata.sql +40 -0
  60. weaver/declaration/templates/load/full_replace_body.sql +21 -0
  61. weaver/declaration/templates/load/install_load_procedure.sql +27 -0
  62. weaver/declaration/templates/load/load_procedure.sql +48 -0
  63. weaver/declaration/templates/load/primary_key_body.sql +113 -0
  64. weaver/declaration/tsql_ddl.py +468 -0
  65. weaver/declaration/tsql_load.py +417 -0
  66. weaver/declaration/warehouse_type_mapping.yml +93 -0
  67. weaver/diagnostics.py +247 -0
  68. weaver/errors.py +61 -0
  69. weaver/etl.py +469 -0
  70. weaver/fabric/__init__.py +107 -0
  71. weaver/fabric/auth.py +137 -0
  72. weaver/fabric/capacity.py +143 -0
  73. weaver/fabric/client.py +147 -0
  74. weaver/fabric/environment.py +460 -0
  75. weaver/fabric/livy.py +478 -0
  76. weaver/fabric/notebooks.py +201 -0
  77. weaver/fabric/onelake.py +263 -0
  78. weaver/fabric/resolution.py +344 -0
  79. weaver/fabric/resources.py +245 -0
  80. weaver/fabric/session.py +148 -0
  81. weaver/fabric/shortcuts.py +120 -0
  82. weaver/fabric/sql.py +118 -0
  83. weaver/fabric/store.py +198 -0
  84. weaver/initialise.py +209 -0
  85. weaver/lakehouse.py +386 -0
  86. weaver/load.py +474 -0
  87. weaver/load_execution.py +483 -0
  88. weaver/load_plan.py +912 -0
  89. weaver/load_report.py +330 -0
  90. weaver/load_resolution.py +386 -0
  91. weaver/locations.py +164 -0
  92. weaver/objects.py +392 -0
  93. weaver/operations.py +757 -0
  94. weaver/physical_wipe.py +369 -0
  95. weaver/push.py +76 -0
  96. weaver/resolution.py +292 -0
  97. weaver/runtime/__init__.py +30 -0
  98. weaver/runtime/folder_load.py +402 -0
  99. weaver/runtime/load_contract.py +245 -0
  100. weaver/runtime/load_result.py +104 -0
  101. weaver/runtime/spark_load.py +152 -0
  102. weaver/runtime/table_load.py +497 -0
  103. weaver/spark/__init__.py +49 -0
  104. weaver/spark/catalogue.py +245 -0
  105. weaver/spark/destination.py +195 -0
  106. weaver/spark/session.py +84 -0
  107. weaver/spark/tokens.py +138 -0
  108. weaver/sql/__init__.py +40 -0
  109. weaver/sql/authentication.py +38 -0
  110. weaver/sql/connection.py +90 -0
  111. weaver/sql/errors.py +25 -0
  112. weaver/sql/execution.py +123 -0
  113. weaver/sql/pool.py +174 -0
  114. weaver/sql/wipe.py +156 -0
  115. weaver/store.py +209 -0
  116. weaver/targets.py +257 -0
  117. weaver/task_logging.py +215 -0
  118. weaver/unbind.py +74 -0
  119. weaver/workspaces.py +175 -0
  120. weaver_cli/__init__.py +12 -0
  121. weaver_cli/__main__.py +7 -0
  122. weaver_cli/main.py +626 -0
  123. weaverstack-0.1.1.dist-info/METADATA +113 -0
  124. weaverstack-0.1.1.dist-info/RECORD +127 -0
  125. weaverstack-0.1.1.dist-info/WHEEL +4 -0
  126. weaverstack-0.1.1.dist-info/entry_points.txt +2 -0
  127. weaverstack-0.1.1.dist-info/licenses/LICENSE +201 -0
weaver/sql/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ """Driver-neutral Warehouse SQL execution backed by ``mssql-python``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .authentication import AccessTokenAuthentication, SqlAuthentication
6
+ from .connection import SqlEndpoint, build_connection_string, connect
7
+ from .errors import (
8
+ SqlConnectionError,
9
+ SqlError,
10
+ SqlExecutionError,
11
+ SqlPoolClosedError,
12
+ )
13
+ from .execution import PooledSqlExecutor, SqlExecutor, SqlRow
14
+ from .pool import (
15
+ DEFAULT_MAX_CONNECTIONS,
16
+ SqlConnectionLease,
17
+ SqlConnectionPool,
18
+ SqlPoolRegistry,
19
+ )
20
+ from .wipe import generate_warehouse_wipe_sql
21
+
22
+ __all__ = [
23
+ "AccessTokenAuthentication",
24
+ "SqlAuthentication",
25
+ "SqlEndpoint",
26
+ "build_connection_string",
27
+ "connect",
28
+ "SqlError",
29
+ "SqlConnectionError",
30
+ "SqlExecutionError",
31
+ "SqlPoolClosedError",
32
+ "SqlExecutor",
33
+ "SqlRow",
34
+ "PooledSqlExecutor",
35
+ "SqlConnectionLease",
36
+ "SqlConnectionPool",
37
+ "SqlPoolRegistry",
38
+ "DEFAULT_MAX_CONNECTIONS",
39
+ "generate_warehouse_wipe_sql",
40
+ ]
@@ -0,0 +1,38 @@
1
+ """Authentication records consumed by the common SQL connector.
2
+
3
+ Identity policy stays at the caller boundary. This module only converts a
4
+ fresh access token into the connection argument understood by
5
+ ``mssql-python``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import struct
11
+ from collections.abc import Callable, Mapping
12
+ from dataclasses import dataclass
13
+ from typing import Protocol
14
+
15
+ # SQL_COPT_SS_ACCESS_TOKEN from msodbcsql.h. mssql-python accepts the same
16
+ # attrs_before shape as pyodbc and exposes this value as
17
+ # ConstantsDDBC.SQL_COPT_SS_ACCESS_TOKEN.
18
+ SQL_ACCESS_TOKEN_ATTRIBUTE = 1256
19
+
20
+
21
+ class SqlAuthentication(Protocol):
22
+ """Authentication material for one new physical connection."""
23
+
24
+ def connection_arguments(self) -> Mapping[str, object]:
25
+ """Return current driver keyword arguments."""
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class AccessTokenAuthentication:
30
+ """Acquire and encode a fresh Entra access token per connection."""
31
+
32
+ token_provider: Callable[[], str]
33
+
34
+ def connection_arguments(self) -> Mapping[str, object]:
35
+ token = self.token_provider()
36
+ token_bytes = token.encode("utf-16-le")
37
+ packed = struct.pack(f"<I{len(token_bytes)}s", len(token_bytes), token_bytes)
38
+ return {"attrs_before": {SQL_ACCESS_TOKEN_ATTRIBUTE: packed}}
@@ -0,0 +1,90 @@
1
+ """Resolved SQL endpoints and common ``mssql-python`` connection creation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any, Callable
7
+
8
+ from .authentication import SqlAuthentication
9
+ from .errors import SqlConnectionError
10
+
11
+ DEFAULT_SQL_PORT = 1433
12
+ DEFAULT_CONNECT_TIMEOUT = 60
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class SqlEndpoint:
17
+ """One resolved Warehouse SQL endpoint.
18
+
19
+ The optional Fabric identities make the pool key stable without treating a
20
+ display name or a credential-bearing connection string as identity.
21
+ """
22
+
23
+ server: str
24
+ database: str
25
+ port: int = DEFAULT_SQL_PORT
26
+ workspace_id: str | None = None
27
+ warehouse_id: str | None = None
28
+ warehouse_name: str | None = None
29
+
30
+ def __post_init__(self) -> None:
31
+ if not self.server.strip():
32
+ raise ValueError("SQL server must not be empty")
33
+ if not self.database.strip():
34
+ raise ValueError("SQL database must not be empty")
35
+
36
+ @property
37
+ def pool_key(self) -> tuple[str, str, str, str, int]:
38
+ return (
39
+ self.workspace_id or "",
40
+ self.warehouse_id or "",
41
+ self.server.lower(),
42
+ self.database.lower(),
43
+ self.port,
44
+ )
45
+
46
+ def __str__(self) -> str:
47
+ return f"{self.server}:{self.port}/{self.database}"
48
+
49
+
50
+ def build_connection_string(endpoint: SqlEndpoint) -> str:
51
+ """The validated Fabric Warehouse connection-string shape."""
52
+
53
+ return (
54
+ f"Server={endpoint.server},{endpoint.port};"
55
+ f"Database={endpoint.database};"
56
+ "Encrypt=yes;"
57
+ "TrustServerCertificate=no;"
58
+ )
59
+
60
+
61
+ DriverConnector = Callable[..., Any]
62
+
63
+
64
+ def connect(
65
+ endpoint: SqlEndpoint,
66
+ authentication: SqlAuthentication,
67
+ *,
68
+ timeout: int = DEFAULT_CONNECT_TIMEOUT,
69
+ connector: DriverConnector | None = None,
70
+ ):
71
+ """Open one physical connection using current authentication material."""
72
+
73
+ if connector is None:
74
+ try:
75
+ from mssql_python import connect as connector
76
+ except ImportError as exc: # pragma: no cover - installation dependent
77
+ raise SqlConnectionError(
78
+ "mssql-python is required for Warehouse SQL execution"
79
+ ) from exc
80
+
81
+ try:
82
+ return connector(
83
+ build_connection_string(endpoint),
84
+ timeout=timeout,
85
+ **dict(authentication.connection_arguments()),
86
+ )
87
+ except SqlConnectionError:
88
+ raise
89
+ except Exception as exc:
90
+ raise SqlConnectionError(f"failed to connect to {endpoint}: {exc}") from exc
weaver/sql/errors.py ADDED
@@ -0,0 +1,25 @@
1
+ """Weaver-facing SQL errors.
2
+
3
+ Driver exceptions do not escape this package. The normalised errors retain the
4
+ selected endpoint and chain the original exception for diagnosis.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from ..errors import WeaverError
10
+
11
+
12
+ class SqlError(WeaverError):
13
+ """Base class for SQL connection and execution failures."""
14
+
15
+
16
+ class SqlConnectionError(SqlError):
17
+ """Raised when a physical SQL connection cannot be opened."""
18
+
19
+
20
+ class SqlExecutionError(SqlError):
21
+ """Raised when a SQL statement, script, or query fails."""
22
+
23
+
24
+ class SqlPoolClosedError(SqlError):
25
+ """Raised when a caller tries to lease from a closed pool."""
@@ -0,0 +1,123 @@
1
+ """Shared SQL statement, script, query, and transaction handling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from typing import Any, Protocol
7
+
8
+ from .errors import SqlError, SqlExecutionError
9
+ from .pool import SqlConnectionPool
10
+
11
+ SqlRow = dict[str, Any]
12
+
13
+
14
+ class SqlExecutor(Protocol):
15
+ """The SQL surface used by Weaver operations."""
16
+
17
+ def execute(
18
+ self, statement: str, parameters: Sequence[object] | None = None
19
+ ) -> None: ...
20
+
21
+ def execute_script(self, script: str) -> None: ...
22
+
23
+ def query(
24
+ self, statement: str, parameters: Sequence[object] | None = None
25
+ ) -> Sequence[SqlRow]: ...
26
+
27
+
28
+ class PooledSqlExecutor:
29
+ """Execute through one owned or injected bounded connection pool."""
30
+
31
+ def __init__(self, pool: SqlConnectionPool, *, owns_pool: bool = False) -> None:
32
+ self.pool = pool
33
+ self.owns_pool = owns_pool
34
+
35
+ def execute(
36
+ self, statement: str, parameters: Sequence[object] | None = None
37
+ ) -> None:
38
+ self._run(statement, parameters=parameters, query=False, drain=True)
39
+
40
+ def execute_script(self, script: str) -> None:
41
+ self._run(script, parameters=None, query=False, drain=True)
42
+
43
+ def query(
44
+ self, statement: str, parameters: Sequence[object] | None = None
45
+ ) -> Sequence[SqlRow]:
46
+ return self._run(statement, parameters=parameters, query=True, drain=False)
47
+
48
+ def _run(
49
+ self,
50
+ statement: str,
51
+ *,
52
+ parameters: Sequence[object] | None,
53
+ query: bool,
54
+ drain: bool,
55
+ ):
56
+ with self.pool.lease() as lease:
57
+ connection = lease.connection
58
+ cursor = None
59
+ try:
60
+ cursor = connection.cursor()
61
+ if parameters is None:
62
+ cursor.execute(statement)
63
+ else:
64
+ cursor.execute(statement, tuple(parameters))
65
+
66
+ if query:
67
+ if cursor.description is None:
68
+ rows = []
69
+ else:
70
+ columns = [column[0] for column in cursor.description]
71
+ rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
72
+ connection.commit()
73
+ return rows
74
+
75
+ if drain:
76
+ _drain(cursor)
77
+ connection.commit()
78
+ return None
79
+ except SqlError:
80
+ lease.discard()
81
+ _rollback(connection)
82
+ raise
83
+ except Exception as exc:
84
+ lease.discard()
85
+ _rollback(connection)
86
+ operation = "query" if query else "SQL execution"
87
+ raise SqlExecutionError(
88
+ f"{operation} failed on {self.pool.endpoint}: {exc}"
89
+ ) from exc
90
+ finally:
91
+ if cursor is not None:
92
+ try:
93
+ cursor.close()
94
+ except Exception:
95
+ lease.discard()
96
+
97
+ def close(self) -> None:
98
+ if self.owns_pool:
99
+ self.pool.close()
100
+
101
+ def __enter__(self) -> "PooledSqlExecutor":
102
+ return self
103
+
104
+ def __exit__(self, *exc) -> bool:
105
+ self.close()
106
+ return False
107
+
108
+
109
+ def _drain(cursor) -> None:
110
+ """Consume all result sets so multi-statement T-SQL can commit reliably."""
111
+
112
+ while True:
113
+ if cursor.description is not None:
114
+ cursor.fetchall()
115
+ if not cursor.nextset():
116
+ return
117
+
118
+
119
+ def _rollback(connection) -> None:
120
+ try:
121
+ connection.rollback()
122
+ except Exception:
123
+ pass
weaver/sql/pool.py ADDED
@@ -0,0 +1,174 @@
1
+ """Small, bounded, thread-safe SQL connection pools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ from contextlib import contextmanager
7
+ from dataclasses import dataclass
8
+ from typing import Any, Callable, Iterator
9
+
10
+ from .authentication import SqlAuthentication
11
+ from .connection import SqlEndpoint, connect
12
+ from .errors import SqlPoolClosedError
13
+
14
+ DEFAULT_MAX_CONNECTIONS = 4
15
+ ConnectionFactory = Callable[[SqlEndpoint, SqlAuthentication], Any]
16
+
17
+
18
+ @dataclass
19
+ class SqlConnectionLease:
20
+ """One exclusively leased physical connection."""
21
+
22
+ connection: Any
23
+ usable: bool = True
24
+
25
+ def discard(self) -> None:
26
+ """Prevent this connection from returning to the idle pool."""
27
+
28
+ self.usable = False
29
+
30
+
31
+ class SqlConnectionPool:
32
+ """Bounded connection reuse owned by one workflow or Fabric session."""
33
+
34
+ def __init__(
35
+ self,
36
+ endpoint: SqlEndpoint,
37
+ authentication: SqlAuthentication,
38
+ *,
39
+ max_connections: int = DEFAULT_MAX_CONNECTIONS,
40
+ connection_factory: ConnectionFactory = connect,
41
+ ) -> None:
42
+ if max_connections < 1:
43
+ raise ValueError("max_connections must be at least one")
44
+ self.endpoint = endpoint
45
+ self.authentication = authentication
46
+ self.max_connections = max_connections
47
+ self._connection_factory = connection_factory
48
+ self._condition = threading.Condition()
49
+ self._idle: list[Any] = []
50
+ self._physical_count = 0
51
+ self._closed = False
52
+
53
+ @contextmanager
54
+ def lease(self) -> Iterator[SqlConnectionLease]:
55
+ """Lease one connection exclusively, waiting when the pool is full."""
56
+
57
+ connection = self._acquire()
58
+ lease = SqlConnectionLease(connection)
59
+ try:
60
+ yield lease
61
+ finally:
62
+ self._release(lease)
63
+
64
+ def _acquire(self):
65
+ create = False
66
+ with self._condition:
67
+ while True:
68
+ if self._closed:
69
+ raise SqlPoolClosedError(f"SQL pool for {self.endpoint} is closed")
70
+ if self._idle:
71
+ return self._idle.pop()
72
+ if self._physical_count < self.max_connections:
73
+ # Reserve the slot before opening outside the lock.
74
+ self._physical_count += 1
75
+ create = True
76
+ break
77
+ self._condition.wait()
78
+
79
+ if create:
80
+ try:
81
+ return self._connection_factory(self.endpoint, self.authentication)
82
+ except Exception:
83
+ with self._condition:
84
+ self._physical_count -= 1
85
+ self._condition.notify()
86
+ raise
87
+ raise AssertionError("unreachable")
88
+
89
+ def _release(self, lease: SqlConnectionLease) -> None:
90
+ close = False
91
+ with self._condition:
92
+ if self._closed or not lease.usable:
93
+ self._physical_count -= 1
94
+ close = True
95
+ else:
96
+ self._idle.append(lease.connection)
97
+ self._condition.notify()
98
+ if close:
99
+ _close(lease.connection)
100
+
101
+ def close(self) -> None:
102
+ """Close idle connections; leased connections close when returned."""
103
+
104
+ with self._condition:
105
+ if self._closed:
106
+ return
107
+ self._closed = True
108
+ idle, self._idle = self._idle, []
109
+ self._physical_count -= len(idle)
110
+ self._condition.notify_all()
111
+ for connection in idle:
112
+ _close(connection)
113
+
114
+ def __enter__(self) -> "SqlConnectionPool":
115
+ return self
116
+
117
+ def __exit__(self, *exc) -> bool:
118
+ self.close()
119
+ return False
120
+
121
+
122
+ class SqlPoolRegistry:
123
+ """One endpoint-keyed pool registry for an owning execution context."""
124
+
125
+ def __init__(
126
+ self,
127
+ *,
128
+ max_connections: int = DEFAULT_MAX_CONNECTIONS,
129
+ connection_factory: ConnectionFactory = connect,
130
+ ) -> None:
131
+ self.max_connections = max_connections
132
+ self.connection_factory = connection_factory
133
+ self._lock = threading.Lock()
134
+ self._pools: dict[tuple[str, str, str, str, int], SqlConnectionPool] = {}
135
+ self._closed = False
136
+
137
+ def pool(
138
+ self, endpoint: SqlEndpoint, authentication: SqlAuthentication
139
+ ) -> SqlConnectionPool:
140
+ with self._lock:
141
+ if self._closed:
142
+ raise SqlPoolClosedError("SQL pool registry is closed")
143
+ key = endpoint.pool_key
144
+ if key not in self._pools:
145
+ self._pools[key] = SqlConnectionPool(
146
+ endpoint,
147
+ authentication,
148
+ max_connections=self.max_connections,
149
+ connection_factory=self.connection_factory,
150
+ )
151
+ return self._pools[key]
152
+
153
+ def close(self) -> None:
154
+ with self._lock:
155
+ if self._closed:
156
+ return
157
+ self._closed = True
158
+ pools, self._pools = tuple(self._pools.values()), {}
159
+ for pool in pools:
160
+ pool.close()
161
+
162
+ def __enter__(self) -> "SqlPoolRegistry":
163
+ return self
164
+
165
+ def __exit__(self, *exc) -> bool:
166
+ self.close()
167
+ return False
168
+
169
+
170
+ def _close(connection: Any) -> None:
171
+ try:
172
+ connection.close()
173
+ except Exception:
174
+ pass
weaver/sql/wipe.py ADDED
@@ -0,0 +1,156 @@
1
+ """Pure generation of the proven Fabric Warehouse wipe batch."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ def generate_warehouse_wipe_sql() -> str:
7
+ """Return deterministic SQL that removes supported user-created objects.
8
+
9
+ This is ported from the legacy Weaver Warehouse implementation:
10
+ ``src/weaver_runtime/dbrep/sql/templates/admin/wipe.sql`` for object
11
+ enumeration and ordering, plus ``_drop_user_schemas`` in
12
+ ``src/weaver_runtime/dbrep/sql/backend.py`` for schema cleanup.
13
+ """
14
+
15
+ return _WAREHOUSE_WIPE_SQL
16
+
17
+
18
+ _WAREHOUSE_WIPE_SQL = """\
19
+ set nocount on;
20
+
21
+ declare @weaver_sql nvarchar(max);
22
+
23
+ /* Foreign keys first so dependent tables can be dropped. */
24
+ select
25
+ @weaver_sql = string_agg(
26
+ convert(
27
+ nvarchar(max),
28
+ N'alter table '
29
+ + quotename(object_schema_name(fk.parent_object_id))
30
+ + N'.'
31
+ + quotename(object_name(fk.parent_object_id))
32
+ + N' drop constraint '
33
+ + quotename(fk.name)
34
+ + N';'
35
+ ),
36
+ char(10)
37
+ ) within group (order by fk.object_id)
38
+ from sys.foreign_keys as fk
39
+ where lower(schema_name(schema_id)) not in
40
+ (N'guest', N'information_schema', N'sys', N'queryinsights');
41
+
42
+ if @weaver_sql is not null
43
+ begin
44
+ exec sys.sp_executesql @weaver_sql;
45
+ end;
46
+
47
+ /* Views before the objects and tables they can depend on. */
48
+ set @weaver_sql = null;
49
+ select
50
+ @weaver_sql = string_agg(
51
+ convert(
52
+ nvarchar(max),
53
+ N'drop view '
54
+ + quotename(schema_name(schema_id))
55
+ + N'.'
56
+ + quotename(name)
57
+ + N';'
58
+ ),
59
+ char(10)
60
+ ) within group (order by object_id)
61
+ from sys.views
62
+ where lower(schema_name(schema_id)) not in
63
+ (N'guest', N'information_schema', N'sys', N'queryinsights');
64
+
65
+ if @weaver_sql is not null
66
+ begin
67
+ exec sys.sp_executesql @weaver_sql;
68
+ end;
69
+
70
+ /* Stored procedures. */
71
+ set @weaver_sql = null;
72
+ select
73
+ @weaver_sql = string_agg(
74
+ convert(
75
+ nvarchar(max),
76
+ N'drop procedure '
77
+ + quotename(schema_name(schema_id))
78
+ + N'.'
79
+ + quotename(name)
80
+ + N';'
81
+ ),
82
+ char(10)
83
+ ) within group (order by object_id)
84
+ from sys.procedures
85
+ where lower(schema_name(schema_id)) not in
86
+ (N'guest', N'information_schema', N'sys', N'queryinsights');
87
+
88
+ if @weaver_sql is not null
89
+ begin
90
+ exec sys.sp_executesql @weaver_sql;
91
+ end;
92
+
93
+ /* Functions (scalar, inline table-valued, multi-statement table-valued). */
94
+ set @weaver_sql = null;
95
+ select
96
+ @weaver_sql = string_agg(
97
+ convert(
98
+ nvarchar(max),
99
+ N'drop function '
100
+ + quotename(schema_name(schema_id))
101
+ + N'.'
102
+ + quotename(name)
103
+ + N';'
104
+ ),
105
+ char(10)
106
+ ) within group (order by object_id)
107
+ from sys.objects
108
+ where type in (N'FN', N'IF', N'TF', N'FS', N'FT')
109
+ and lower(schema_name(schema_id)) not in
110
+ (N'guest', N'information_schema', N'sys', N'queryinsights');
111
+
112
+ if @weaver_sql is not null
113
+ begin
114
+ exec sys.sp_executesql @weaver_sql;
115
+ end;
116
+
117
+ /* Tables. */
118
+ set @weaver_sql = null;
119
+ select
120
+ @weaver_sql = string_agg(
121
+ convert(
122
+ nvarchar(max),
123
+ N'drop table '
124
+ + quotename(schema_name(schema_id))
125
+ + N'.'
126
+ + quotename(name)
127
+ + N';'
128
+ ),
129
+ char(10)
130
+ ) within group (order by object_id)
131
+ from sys.tables
132
+ where lower(schema_name(schema_id)) not in
133
+ (N'guest', N'information_schema', N'sys', N'queryinsights');
134
+
135
+ if @weaver_sql is not null
136
+ begin
137
+ exec sys.sp_executesql @weaver_sql;
138
+ end;
139
+
140
+ /* User schemas last; the built-in and Fabric-owned schemas survive. */
141
+ set @weaver_sql = null;
142
+ select
143
+ @weaver_sql = string_agg(
144
+ convert(nvarchar(max), N'drop schema ' + quotename(name) + N';'),
145
+ char(10)
146
+ ) within group (order by schema_id)
147
+ from sys.schemas
148
+ where lower(name) not in
149
+ (N'dbo', N'guest', N'information_schema', N'sys', N'queryinsights', N'_rsc')
150
+ and schema_id < 16384;
151
+
152
+ if @weaver_sql is not null
153
+ begin
154
+ exec sys.sp_executesql @weaver_sql;
155
+ end;
156
+ """