mainsequence 4.1.19__tar.gz → 4.2.2__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.
- {mainsequence-4.1.19/mainsequence.egg-info → mainsequence-4.2.2}/PKG-INFO +2 -1
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +15 -22
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +57 -19
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/migrations.py +109 -57
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/metatables/core.py +22 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/metatables/migrations.py +2 -5
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/__init__.py +4 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/build_operations.py +6 -5
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/persist_managers.py +16 -7
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/migrations.py +196 -77
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/sqlalchemy_contracts.py +75 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2/mainsequence.egg-info}/PKG-INFO +2 -1
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence.egg-info/requires.txt +1 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/pyproject.toml +2 -1
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_build_operations_hashing.py +6 -23
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_cli_migrations.py +244 -20
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_filter_normalization.py +2 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_meta_table_migrations.py +264 -30
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_meta_tables_client_models.py +37 -2
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_meta_tables_sqlalchemy_contracts.py +90 -40
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_run_configuration.py +33 -2
- {mainsequence-4.1.19 → mainsequence-4.2.2}/LICENSE +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/README.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/__main__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/base.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/client.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/defaults.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/logconf.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/setup.cfg +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_cli.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_client.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_logconf.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.2}/tests/test_workspace_snapshot.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mainsequence
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.2
|
|
4
4
|
Summary: Main Sequence SDK
|
|
5
5
|
Author-email: Main Sequence GmbH <dev@main-sequence.io>
|
|
6
6
|
License: MainSequence GmbH SDK License Agreement
|
|
@@ -63,6 +63,7 @@ Requires-Dist: opentelemetry-exporter-otlp
|
|
|
63
63
|
Requires-Dist: opentelemetry-sdk
|
|
64
64
|
Requires-Dist: pandas
|
|
65
65
|
Requires-Dist: psutil
|
|
66
|
+
Requires-Dist: psycopg2>=2.9.12
|
|
66
67
|
Requires-Dist: pydantic
|
|
67
68
|
Requires-Dist: pytz
|
|
68
69
|
Requires-Dist: pyyaml
|
{mainsequence-4.1.19 → mainsequence-4.2.2}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md
RENAMED
|
@@ -182,20 +182,14 @@ class PricesTable(PlatformTimeIndexMetaData, Base):
|
|
|
182
182
|
)
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Storage registration is
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
Storage registration is migration-first. Add the storage model to the
|
|
186
|
+
MetaTable migration provider and run `mainsequence migrations upgrade --provider
|
|
187
|
+
... --to head`. Do not call `PricesTable.register()` directly and do not rely on
|
|
188
|
+
DataNode construction to register storage tables.
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`PlatformTimeIndexMetaData.register()` is the storage lifecycle path.
|
|
195
|
-
Treat them as idempotent get-or-create operations: the platform returns the
|
|
196
|
-
registered metadata and UID, and the SDK records that metadata on the class. Do
|
|
197
|
-
not manually attach an existing UID, reconstruct a generic `MetaTable`, or use
|
|
198
|
-
manual bind helpers as an authoring step.
|
|
190
|
+
`PlatformTimeIndexMetaData.register()` remains SDK plumbing for the migration
|
|
191
|
+
workflow. Do not manually attach an existing UID, reconstruct a generic
|
|
192
|
+
`MetaTable`, or use manual bind helpers as an authoring step.
|
|
199
193
|
|
|
200
194
|
### 2. Keep DataNode As Update Logic
|
|
201
195
|
|
|
@@ -206,17 +200,16 @@ The DataNode constructor should accept:
|
|
|
206
200
|
- optional `hash_namespace`
|
|
207
201
|
|
|
208
202
|
The constructor `storage_table` is the output storage contract. Keep it out of
|
|
209
|
-
`DataNodeConfiguration`.
|
|
210
|
-
|
|
211
|
-
lifecycle automatically when the class is not yet bound.
|
|
203
|
+
`DataNodeConfiguration`. The storage class must already be registered by the
|
|
204
|
+
migration workflow before the node is constructed or run.
|
|
212
205
|
|
|
213
206
|
If the DataNode needs to select another DataNode's storage table as a
|
|
214
207
|
dependency, put that dependency storage reference in the config as
|
|
215
208
|
`type[PlatformTimeIndexMetaData]`. Do not add an extra constructor argument for
|
|
216
209
|
dependency storage tables. Config values of this type are hashed by the bound
|
|
217
210
|
`TimeIndexMetaData.uid` from `StorageClass.__time_index_metadata__`. If the
|
|
218
|
-
class is not yet bound, the
|
|
219
|
-
|
|
211
|
+
class is not yet bound, config serialization must fail and tell the user to run
|
|
212
|
+
the migration workflow.
|
|
220
213
|
|
|
221
214
|
Do not accept `test_node`. It has been removed. Use explicit
|
|
222
215
|
`hash_namespace(...)` or `hash_namespace="..."`.
|
|
@@ -398,10 +391,10 @@ Do not ask users to name these foreign keys. `MetaTableForeignKey(...)` derives
|
|
|
398
391
|
a stable contract name when `name` is omitted; `name=...` is only for deliberate
|
|
399
392
|
overrides.
|
|
400
393
|
|
|
401
|
-
Registration of the storage class follows the MetaTable lifecycle
|
|
402
|
-
|
|
403
|
-
local process registry keyed by `storage_hash`, and writes the
|
|
404
|
-
`MetaTable.uid` into the FK contract.
|
|
394
|
+
Registration of the storage class follows the MetaTable migration lifecycle.
|
|
395
|
+
Migration tooling recursively resolves/registers unresolved FK target model
|
|
396
|
+
classes, uses the local process registry keyed by `storage_hash`, and writes the
|
|
397
|
+
target `MetaTable.uid` into the FK contract.
|
|
405
398
|
|
|
406
399
|
Do not add DataNode configuration fields just to mutate storage metadata.
|
|
407
400
|
|
|
@@ -95,6 +95,29 @@ For every non-trivial task, decide:
|
|
|
95
95
|
|
|
96
96
|
## Build Rules
|
|
97
97
|
|
|
98
|
+
### 0. Creation And Deletion Are SDK-Managed
|
|
99
|
+
|
|
100
|
+
Do not build custom migrations for creating or deleting MetaTables.
|
|
101
|
+
|
|
102
|
+
MetaTable creation and deletion are handled through `mainsequence-sdk` and the
|
|
103
|
+
Main Sequence CLI. Assistants should define the SQLAlchemy MetaTable model,
|
|
104
|
+
select the SDK migration provider when schema evolution is needed, and use the
|
|
105
|
+
documented `mainsequence migrations ...` commands.
|
|
106
|
+
|
|
107
|
+
Do not hand-author create/delete migration files, backend migration payloads,
|
|
108
|
+
manifest rows, registry rows, or low-level SDK migration requests for MetaTable
|
|
109
|
+
creation or deletion.
|
|
110
|
+
|
|
111
|
+
The only migration workflow to recommend is the Main Sequence CLI lifecycle:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
mainsequence migrations current --provider mainsequence_migrations:migration
|
|
115
|
+
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
116
|
+
mainsequence migrations render --provider mainsequence_migrations:migration --to head
|
|
117
|
+
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head --dry-run
|
|
118
|
+
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head
|
|
119
|
+
```
|
|
120
|
+
|
|
98
121
|
### 1. SQLAlchemy metadata is the authoring source
|
|
99
122
|
|
|
100
123
|
Keep the application table model as the authoring source for the neutral table contract.
|
|
@@ -161,15 +184,18 @@ class Account(PlatformManagedMetaTable, Base):
|
|
|
161
184
|
},
|
|
162
185
|
)
|
|
163
186
|
|
|
164
|
-
|
|
165
|
-
account_meta_table = Account.register()
|
|
166
187
|
```
|
|
167
188
|
|
|
168
|
-
Registration metadata belongs on the class. Do not
|
|
169
|
-
|
|
170
|
-
|
|
189
|
+
Registration metadata belongs on the class. Do not call `Account.register()`
|
|
190
|
+
directly for platform-managed models. Add platform-managed models to the
|
|
191
|
+
selected `AlembicMetaTableMigration.metatable_models` list and let
|
|
192
|
+
`mainsequence migrations upgrade --provider ... --to head` resolve/register and
|
|
193
|
+
bind them.
|
|
171
194
|
|
|
172
|
-
For platform-managed registration, the data source is resolved from
|
|
195
|
+
For platform-managed migration registration, the data source is resolved from
|
|
196
|
+
the active Main Sequence project/session, the same way DataNode does. Do not
|
|
197
|
+
require or thread a `data_source_uid` through normal platform-managed example
|
|
198
|
+
code.
|
|
173
199
|
|
|
174
200
|
Only call `build_registration_request()` when the task explicitly needs to inspect or validate the payload before registration.
|
|
175
201
|
|
|
@@ -183,7 +209,8 @@ Do not add an environment variable for namespace in examples.
|
|
|
183
209
|
|
|
184
210
|
Do not add generic labels such as `"meta-table"` or `"platform-managed"` to examples. Keep labels specific to the example or domain.
|
|
185
211
|
|
|
186
|
-
Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in
|
|
212
|
+
Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in platform-managed
|
|
213
|
+
examples. Platform-managed examples should be migration-first.
|
|
187
214
|
|
|
188
215
|
### 3. Register parent tables before child tables
|
|
189
216
|
|
|
@@ -192,8 +219,8 @@ Foreign-key contracts reference the target `MetaTable` UID.
|
|
|
192
219
|
For `PlatformManagedMetaTable`, define foreign keys with
|
|
193
220
|
`MetaTableForeignKey(TargetModel, column=...)`. Do not write raw SQLAlchemy
|
|
194
221
|
table fullnames, `Parent.__table__.c.<column>` targets, or explicit target UID
|
|
195
|
-
maps in the platform-managed path.
|
|
196
|
-
|
|
222
|
+
maps in the platform-managed path. Migration is the lifecycle path. Migration
|
|
223
|
+
tooling resolves/registers unresolved target model classes, stores each
|
|
197
224
|
returned `MetaTable` in a local process registry keyed by `storage_hash`, and
|
|
198
225
|
uses the target `MetaTable.uid` in the child FK contract.
|
|
199
226
|
|
|
@@ -215,16 +242,19 @@ account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
|
215
242
|
Every participating table must include `__metatable_description__` describing
|
|
216
243
|
both the schema and the table's intention.
|
|
217
244
|
|
|
218
|
-
|
|
245
|
+
Provider scope:
|
|
219
246
|
|
|
220
247
|
```python
|
|
221
|
-
|
|
248
|
+
migration = AlembicMetaTableMigration(
|
|
249
|
+
...,
|
|
250
|
+
metatable_models=[Account, Asset],
|
|
251
|
+
)
|
|
222
252
|
```
|
|
223
253
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
registration attempts for the same `storage_hash` and raises
|
|
227
|
-
recursive registration cycles.
|
|
254
|
+
Migration tooling registers `Account` first if `Asset` depends on it and it has
|
|
255
|
+
not already been bound in the current process. The local registry prevents
|
|
256
|
+
duplicate backend registration attempts for the same `storage_hash` and raises
|
|
257
|
+
a clear error for recursive registration cycles.
|
|
228
258
|
|
|
229
259
|
For `external_registered`, there is no platform-managed parent lookup. Register
|
|
230
260
|
the parent first, then build the child registration request with
|
|
@@ -278,17 +308,25 @@ migration request models, or use SDK helper functions directly. The backend
|
|
|
278
308
|
request shape is reference material in the tutorial; the user-facing path is:
|
|
279
309
|
|
|
280
310
|
```bash
|
|
281
|
-
mainsequence migrations
|
|
282
|
-
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
311
|
+
mainsequence migrations current --provider mainsequence_migrations:migration
|
|
312
|
+
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
283
313
|
mainsequence migrations render --provider mainsequence_migrations:migration --to head
|
|
284
314
|
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head --dry-run
|
|
285
315
|
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head
|
|
286
316
|
```
|
|
287
317
|
|
|
318
|
+
`current` and `upgrade` automatically register the provider's
|
|
319
|
+
`AlembicVersionMetaTable` binding when backend migration state is needed.
|
|
320
|
+
`revision` accepts optional `-m/--message`; if omitted, the CLI uses
|
|
321
|
+
`migration`. `revision --autogenerate` is optional and requires an explicit
|
|
322
|
+
`--sqlalchemy-url` for the baseline database.
|
|
323
|
+
|
|
288
324
|
The SQL must be Alembic-rendered from the selected provider. After SQL apply
|
|
289
325
|
succeeds, register or refresh only the application MetaTable catalog bindings
|
|
290
|
-
listed in `migration.metatable_models`.
|
|
291
|
-
|
|
326
|
+
listed in `migration.metatable_models`. Do not pass the Alembic version-table
|
|
327
|
+
data source into those application model registrations; each model uses its
|
|
328
|
+
own normal MetaTable data-source binding. A migration is not complete until
|
|
329
|
+
both backend SQL execution and catalog sync succeed.
|
|
292
330
|
|
|
293
331
|
Do not use SDK-managed migration artifact tables, artifact sync helpers, or custom
|
|
294
332
|
`operations()` migration modules.
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
4
|
import json
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Callable, Mapping
|
|
5
7
|
from typing import Any
|
|
6
8
|
|
|
7
9
|
import click
|
|
@@ -66,26 +68,67 @@ def _registry_uid(migration: AlembicMetaTableMigration) -> str:
|
|
|
66
68
|
uid = migration.alembic_registry.get_meta_table_uid()
|
|
67
69
|
if uid in (None, ""):
|
|
68
70
|
raise typer.BadParameter(
|
|
69
|
-
"Alembic registry MetaTable UID is not bound. Run
|
|
70
|
-
"or set __metatable_uid__ on the registry class.",
|
|
71
|
+
"Alembic registry MetaTable UID is not bound. Run current or upgrade "
|
|
72
|
+
"to auto-register the registry, or set __metatable_uid__ on the registry class.",
|
|
71
73
|
param_hint="--provider",
|
|
72
74
|
)
|
|
73
75
|
return str(uid)
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
def _model_reference(model: type[Any]) -> str:
|
|
79
|
+
module = getattr(model, "__module__", None)
|
|
80
|
+
qualname = getattr(model, "__qualname__", None)
|
|
81
|
+
if module and qualname:
|
|
82
|
+
return f"{module}.{qualname}"
|
|
83
|
+
return repr(model)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _meta_table_value(meta_table: Any, *names: str) -> Any:
|
|
87
|
+
if isinstance(meta_table, Mapping):
|
|
88
|
+
for name in names:
|
|
89
|
+
value = meta_table.get(name)
|
|
90
|
+
if value not in (None, ""):
|
|
91
|
+
return value
|
|
92
|
+
return None
|
|
93
|
+
for name in names:
|
|
94
|
+
value = getattr(meta_table, name, None)
|
|
95
|
+
if value not in (None, ""):
|
|
96
|
+
return value
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _print_metatable_resolution_callback(
|
|
101
|
+
migration: AlembicMetaTableMigration,
|
|
102
|
+
) -> Callable[[type[Any], str, str, Any | None], None]:
|
|
103
|
+
def _print(model: type[Any], identifier: str, status: str, meta_table: Any | None) -> None:
|
|
104
|
+
fields = [
|
|
105
|
+
f"identifier={identifier}",
|
|
106
|
+
f"model={_model_reference(model)}",
|
|
107
|
+
f"package={migration.package}",
|
|
108
|
+
f"migration_namespace={migration.migration_namespace}",
|
|
109
|
+
]
|
|
110
|
+
uid = _meta_table_value(meta_table, "uid", "meta_table_uid")
|
|
111
|
+
if uid is not None:
|
|
112
|
+
fields.append(f"uid={uid}")
|
|
113
|
+
physical_table_name = _meta_table_value(meta_table, "physical_table_name")
|
|
114
|
+
if physical_table_name is not None:
|
|
115
|
+
fields.append(f"physical_table_name={physical_table_name}")
|
|
116
|
+
typer.echo(f"migration MetaTable {status}: " + " ".join(fields), err=True)
|
|
117
|
+
|
|
118
|
+
return _print
|
|
119
|
+
|
|
120
|
+
|
|
76
121
|
def _ensure_registry(
|
|
77
122
|
migration: AlembicMetaTableMigration,
|
|
78
123
|
*,
|
|
79
|
-
data_source_uid: str | None,
|
|
80
124
|
timeout: float | None,
|
|
81
125
|
) -> None:
|
|
82
|
-
migration.ensure_alembic_registry(
|
|
126
|
+
migration.ensure_alembic_registry(timeout=timeout)
|
|
83
127
|
|
|
84
128
|
|
|
85
129
|
def _status_request(migration: AlembicMetaTableMigration) -> AlembicMigrationStatusRequest:
|
|
86
130
|
return AlembicMigrationStatusRequest(
|
|
87
131
|
alembic_version_meta_table_uid=_registry_uid(migration),
|
|
88
|
-
data_source_uid=migration.resolve_data_source_uid(),
|
|
89
132
|
package=migration.package,
|
|
90
133
|
migration_namespace=migration.migration_namespace,
|
|
91
134
|
)
|
|
@@ -117,7 +160,6 @@ def _operation(
|
|
|
117
160
|
) -> AlembicMigrationOperation:
|
|
118
161
|
return AlembicMigrationOperation(
|
|
119
162
|
alembic_version_meta_table_uid=_registry_uid(migration),
|
|
120
|
-
data_source_uid=migration.resolve_data_source_uid(),
|
|
121
163
|
package=migration.package,
|
|
122
164
|
migration_namespace=migration.migration_namespace,
|
|
123
165
|
revision=str(artifact.manifest["revision"]),
|
|
@@ -152,39 +194,35 @@ def _alembic_config(
|
|
|
152
194
|
return config
|
|
153
195
|
|
|
154
196
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
help="Migration provider reference, for example msm.migrations:migration.",
|
|
161
|
-
),
|
|
162
|
-
data_source_uid: str | None = typer.Option(
|
|
163
|
-
None,
|
|
164
|
-
"--data-source-uid",
|
|
165
|
-
help="Explicit override for cross-data-source workflows.",
|
|
166
|
-
),
|
|
167
|
-
timeout: float | None = typer.Option(None, "--timeout"),
|
|
168
|
-
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
169
|
-
) -> None:
|
|
170
|
-
"""Register the provider's Alembic version table as an external MetaTable."""
|
|
197
|
+
def _next_sequential_revision_id(migration: AlembicMetaTableMigration) -> str:
|
|
198
|
+
try:
|
|
199
|
+
from alembic.script import ScriptDirectory
|
|
200
|
+
except ImportError as exc:
|
|
201
|
+
raise RuntimeError("Alembic is required for revision generation.") from exc
|
|
171
202
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
data_source_uid=data_source_uid,
|
|
175
|
-
timeout=timeout,
|
|
176
|
-
)
|
|
177
|
-
_emit(
|
|
178
|
-
{
|
|
179
|
-
"uid": migration.alembic_registry.get_meta_table_uid()
|
|
180
|
-
or getattr(meta_table, "uid", None),
|
|
181
|
-
"data_source_uid": migration.alembic_registry.get_data_source_uid(),
|
|
182
|
-
"alembic_version_table": migration.alembic_version_table,
|
|
183
|
-
"package": migration.package,
|
|
184
|
-
"migration_namespace": migration.migration_namespace,
|
|
185
|
-
},
|
|
186
|
-
json_output=json_output,
|
|
203
|
+
script = ScriptDirectory.from_config(
|
|
204
|
+
_alembic_config(migration, sqlalchemy_url="postgresql://")
|
|
187
205
|
)
|
|
206
|
+
heads = list(script.get_heads())
|
|
207
|
+
if len(heads) > 1:
|
|
208
|
+
raise typer.BadParameter(
|
|
209
|
+
"Sequential revision IDs require a single Alembic head. Pass --rev-id "
|
|
210
|
+
"explicitly for branched histories.",
|
|
211
|
+
param_hint="--rev-id",
|
|
212
|
+
)
|
|
213
|
+
if heads and not re.fullmatch(r"\d{4,}", str(heads[0])):
|
|
214
|
+
raise typer.BadParameter(
|
|
215
|
+
"Sequential revision IDs require the current Alembic head to be numeric. "
|
|
216
|
+
"Pass --rev-id explicitly for non-numeric histories.",
|
|
217
|
+
param_hint="--rev-id",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
numeric_revisions: list[int] = []
|
|
221
|
+
for revision in script.walk_revisions():
|
|
222
|
+
revision_id = str(revision.revision)
|
|
223
|
+
if re.fullmatch(r"\d{4,}", revision_id):
|
|
224
|
+
numeric_revisions.append(int(revision_id))
|
|
225
|
+
return f"{max(numeric_revisions, default=0) + 1:04d}"
|
|
188
226
|
|
|
189
227
|
|
|
190
228
|
@migrations.command("current")
|
|
@@ -194,26 +232,30 @@ def current(
|
|
|
194
232
|
"--provider",
|
|
195
233
|
help="Migration provider reference, for example msm.migrations:migration.",
|
|
196
234
|
),
|
|
197
|
-
data_source_uid: str | None = typer.Option(
|
|
198
|
-
None,
|
|
199
|
-
"--data-source-uid",
|
|
200
|
-
help="Explicit override for cross-data-source workflows.",
|
|
201
|
-
),
|
|
202
235
|
timeout: float | None = typer.Option(None, "--timeout"),
|
|
203
236
|
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
204
237
|
) -> None:
|
|
205
238
|
"""Read current Alembic revision through the provider's registry MetaTable."""
|
|
206
239
|
|
|
207
240
|
migration = _load_migration(provider)
|
|
208
|
-
_ensure_registry(migration,
|
|
241
|
+
_ensure_registry(migration, timeout=timeout)
|
|
209
242
|
status = MetaTable.get_migration_status(_status_request(migration), timeout=timeout)
|
|
210
243
|
_emit(status, json_output=json_output)
|
|
211
244
|
|
|
212
245
|
|
|
213
246
|
@migrations.command("revision")
|
|
214
247
|
def revision(
|
|
215
|
-
message: str = typer.Option(
|
|
216
|
-
|
|
248
|
+
message: str | None = typer.Option(
|
|
249
|
+
None,
|
|
250
|
+
"--message",
|
|
251
|
+
"-m",
|
|
252
|
+
help="Alembic revision message. Defaults to 'migration'.",
|
|
253
|
+
),
|
|
254
|
+
autogenerate: bool = typer.Option(
|
|
255
|
+
False,
|
|
256
|
+
"--autogenerate",
|
|
257
|
+
help="Use Alembic autogenerate. Requires --sqlalchemy-url.",
|
|
258
|
+
),
|
|
217
259
|
provider: str | None = typer.Option(
|
|
218
260
|
None,
|
|
219
261
|
"--provider",
|
|
@@ -221,10 +263,10 @@ def revision(
|
|
|
221
263
|
),
|
|
222
264
|
rev_id: str | None = typer.Option(None, "--rev-id", help="Explicit Alembic revision id."),
|
|
223
265
|
head: str = typer.Option("head", "--head", help="Alembic head to base the revision on."),
|
|
224
|
-
sqlalchemy_url: str = typer.Option(
|
|
225
|
-
|
|
266
|
+
sqlalchemy_url: str | None = typer.Option(
|
|
267
|
+
None,
|
|
226
268
|
"--sqlalchemy-url",
|
|
227
|
-
help="SQLAlchemy URL passed to the Alembic environment.",
|
|
269
|
+
help="SQLAlchemy URL passed to the Alembic environment. Required for --autogenerate.",
|
|
228
270
|
),
|
|
229
271
|
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
230
272
|
) -> None:
|
|
@@ -236,11 +278,19 @@ def revision(
|
|
|
236
278
|
raise typer.BadParameter("Alembic is required for revision generation.") from exc
|
|
237
279
|
|
|
238
280
|
migration = _load_migration(provider)
|
|
281
|
+
resolved_message = (message or "").strip() or "migration"
|
|
282
|
+
if autogenerate and not sqlalchemy_url:
|
|
283
|
+
raise typer.BadParameter(
|
|
284
|
+
"--sqlalchemy-url is required with --autogenerate because Alembic "
|
|
285
|
+
"must connect to a baseline database to compute the diff.",
|
|
286
|
+
param_hint="--sqlalchemy-url",
|
|
287
|
+
)
|
|
288
|
+
resolved_rev_id = rev_id or _next_sequential_revision_id(migration)
|
|
239
289
|
script = command.revision(
|
|
240
|
-
_alembic_config(migration, sqlalchemy_url=sqlalchemy_url),
|
|
241
|
-
message=
|
|
290
|
+
_alembic_config(migration, sqlalchemy_url=sqlalchemy_url or "postgresql://"),
|
|
291
|
+
message=resolved_message,
|
|
242
292
|
autogenerate=autogenerate,
|
|
243
|
-
rev_id=
|
|
293
|
+
rev_id=resolved_rev_id,
|
|
244
294
|
head=head,
|
|
245
295
|
)
|
|
246
296
|
_emit(
|
|
@@ -284,6 +334,9 @@ def render(
|
|
|
284
334
|
migration = _load_migration(provider)
|
|
285
335
|
if direction not in {"upgrade", "downgrade"}:
|
|
286
336
|
raise typer.BadParameter("direction must be 'upgrade' or 'downgrade'.")
|
|
337
|
+
migration.resolve_or_register_metatable_models(
|
|
338
|
+
on_metatable_resolution=_print_metatable_resolution_callback(migration),
|
|
339
|
+
)
|
|
287
340
|
artifact = _render_artifact(
|
|
288
341
|
migration,
|
|
289
342
|
target_revision=target_revision,
|
|
@@ -305,11 +358,6 @@ def upgrade(
|
|
|
305
358
|
"--provider",
|
|
306
359
|
help="Migration provider reference, for example msm.migrations:migration.",
|
|
307
360
|
),
|
|
308
|
-
data_source_uid: str | None = typer.Option(
|
|
309
|
-
None,
|
|
310
|
-
"--data-source-uid",
|
|
311
|
-
help="Explicit override for cross-data-source workflows.",
|
|
312
|
-
),
|
|
313
361
|
dry_run: bool = typer.Option(False, "--dry-run", help="Validate without executing SQL."),
|
|
314
362
|
sqlalchemy_url: str = typer.Option(
|
|
315
363
|
"postgresql://",
|
|
@@ -322,7 +370,11 @@ def upgrade(
|
|
|
322
370
|
"""Dry-run or apply an Alembic-rendered SQL artifact through the backend."""
|
|
323
371
|
|
|
324
372
|
migration = _load_migration(provider)
|
|
325
|
-
_ensure_registry(migration,
|
|
373
|
+
_ensure_registry(migration, timeout=timeout)
|
|
374
|
+
migration.resolve_or_register_metatable_models(
|
|
375
|
+
timeout=timeout,
|
|
376
|
+
on_metatable_resolution=_print_metatable_resolution_callback(migration),
|
|
377
|
+
)
|
|
326
378
|
status = MetaTable.get_migration_status(_status_request(migration), timeout=timeout)
|
|
327
379
|
current_revision = status.current_revision
|
|
328
380
|
artifact = _render_artifact(
|
|
@@ -1102,6 +1102,25 @@ class MetaTable(BasePydanticModel, LabelableObjectMixin, ShareableObjectMixin, B
|
|
|
1102
1102
|
*,
|
|
1103
1103
|
timeout: int | float | tuple[float, float] | None = None,
|
|
1104
1104
|
) -> dict[str, Any]:
|
|
1105
|
+
"""
|
|
1106
|
+
Refresh this MetaTable's physical database shape snapshot.
|
|
1107
|
+
|
|
1108
|
+
This calls the backend ``POST /meta_table/<uid>/introspect/`` action.
|
|
1109
|
+
The backend reads the real physical table through the MetaTable's data
|
|
1110
|
+
source, reflects columns, indexes, and constraints, stores that data on
|
|
1111
|
+
``MetaTable.introspection_snapshot``, and returns the full response.
|
|
1112
|
+
|
|
1113
|
+
This method is intended for admin, debugging, and reconciliation
|
|
1114
|
+
workflows. Use it when a client needs to inspect what the database
|
|
1115
|
+
currently has, diagnose catalog/physical drift, or refresh metadata
|
|
1116
|
+
after an out-of-band DDL change. It is not required for normal reads,
|
|
1117
|
+
writes, registration, or migration-first application startup.
|
|
1118
|
+
|
|
1119
|
+
Returns:
|
|
1120
|
+
Backend response containing ``ok``, ``meta_table_uid``, and
|
|
1121
|
+
``introspection_snapshot``. When the snapshot is an object, this
|
|
1122
|
+
instance's ``introspection_snapshot`` attribute is updated in place.
|
|
1123
|
+
"""
|
|
1105
1124
|
response_json = self._post_detail_action("introspect", timeout=timeout)
|
|
1106
1125
|
snapshot = response_json.get("introspection_snapshot")
|
|
1107
1126
|
if isinstance(snapshot, dict):
|
|
@@ -1653,12 +1672,15 @@ class DataNodeUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
1653
1672
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
|
|
1654
1673
|
"uid": ["in", "exact"],
|
|
1655
1674
|
"update_hash": ["exact"],
|
|
1675
|
+
"remote_table__uid": ["exact", "in"],
|
|
1656
1676
|
"remote_table__data_source__uid": ["exact", "in"],
|
|
1657
1677
|
"related_table__namespace": ["contains", "in", "isnull"],
|
|
1658
1678
|
}
|
|
1659
1679
|
FILTER_VALUE_NORMALIZERS: ClassVar[dict[str, str]] = {
|
|
1660
1680
|
"uid": "uid",
|
|
1661
1681
|
"uid__in": "uid",
|
|
1682
|
+
"remote_table__uid": "uid",
|
|
1683
|
+
"remote_table__uid__in": "uid",
|
|
1662
1684
|
"remote_table__data_source__uid": "uid",
|
|
1663
1685
|
"remote_table__data_source__uid__in": "uid",
|
|
1664
1686
|
}
|
|
@@ -25,7 +25,6 @@ class AlembicMigrationError(BasePydanticModel):
|
|
|
25
25
|
class AlembicMigrationOperation(BasePydanticModel):
|
|
26
26
|
version: AlembicMigrationVersion = ALEMBIC_MIGRATION_V1
|
|
27
27
|
alembic_version_meta_table_uid: str
|
|
28
|
-
data_source_uid: str
|
|
29
28
|
package: str = ""
|
|
30
29
|
migration_namespace: str = ""
|
|
31
30
|
revision: str
|
|
@@ -42,7 +41,6 @@ class AlembicMigrationOperation(BasePydanticModel):
|
|
|
42
41
|
|
|
43
42
|
class AlembicMigrationStatusRequest(BasePydanticModel):
|
|
44
43
|
alembic_version_meta_table_uid: str
|
|
45
|
-
data_source_uid: str
|
|
46
44
|
package: str = ""
|
|
47
45
|
migration_namespace: str = ""
|
|
48
46
|
|
|
@@ -56,7 +54,7 @@ class AlembicMigrationApplyResponse(BasePydanticModel):
|
|
|
56
54
|
dry_run: bool = False
|
|
57
55
|
alembic_version_meta_table_uid: str
|
|
58
56
|
alembic_version_table: str
|
|
59
|
-
data_source_uid: str
|
|
57
|
+
data_source_uid: str | None = None
|
|
60
58
|
package: str = ""
|
|
61
59
|
migration_namespace: str = ""
|
|
62
60
|
revision: str
|
|
@@ -76,7 +74,7 @@ class AlembicMigrationStatusResponse(BasePydanticModel):
|
|
|
76
74
|
version: AlembicMigrationVersion = ALEMBIC_MIGRATION_V1
|
|
77
75
|
alembic_version_meta_table_uid: str
|
|
78
76
|
alembic_version_table: str
|
|
79
|
-
data_source_uid: str
|
|
77
|
+
data_source_uid: str | None = None
|
|
80
78
|
package: str = ""
|
|
81
79
|
migration_namespace: str = ""
|
|
82
80
|
current_revision: str | None = None
|
|
@@ -127,7 +125,6 @@ def _get_migration_status(
|
|
|
127
125
|
"alembic_version_meta_table_uid",
|
|
128
126
|
payload.alembic_version_meta_table_uid,
|
|
129
127
|
)
|
|
130
|
-
response_payload.setdefault("data_source_uid", payload.data_source_uid)
|
|
131
128
|
response_payload.setdefault("package", payload.package)
|
|
132
129
|
response_payload.setdefault("migration_namespace", payload.migration_namespace)
|
|
133
130
|
return AlembicMigrationStatusResponse(**response_payload)
|
|
@@ -65,6 +65,10 @@ _LAZY_IMPORTS = {
|
|
|
65
65
|
".sqlalchemy_contracts",
|
|
66
66
|
"platform_managed_registration_request_from_sqlalchemy_model",
|
|
67
67
|
),
|
|
68
|
+
"platform_managed_migration_registration_context": (
|
|
69
|
+
".sqlalchemy_contracts",
|
|
70
|
+
"platform_managed_migration_registration_context",
|
|
71
|
+
),
|
|
68
72
|
"register_external_sqlalchemy_model": (
|
|
69
73
|
".sqlalchemy_contracts",
|
|
70
74
|
"register_external_sqlalchemy_model",
|
{mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
@@ -95,14 +95,15 @@ def _(value: type[Any]) -> Any:
|
|
|
95
95
|
time_index_metadata = value.get_time_index_metadata()
|
|
96
96
|
uid = getattr(time_index_metadata, "uid", None)
|
|
97
97
|
if uid in (None, ""):
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
raise ValueError(
|
|
99
|
+
"PlatformTimeIndexMetaData config value is not registered. Run "
|
|
100
|
+
"`mainsequence migrations upgrade --provider <provider> --to head` "
|
|
101
|
+
"before using it in DataNode configuration."
|
|
102
|
+
)
|
|
101
103
|
|
|
102
104
|
if uid in (None, ""):
|
|
103
105
|
raise ValueError(
|
|
104
|
-
"PlatformTimeIndexMetaData config value
|
|
105
|
-
"TimeIndexMetaData metadata before hashing."
|
|
106
|
+
"PlatformTimeIndexMetaData config value is missing TimeIndexMetaData metadata."
|
|
106
107
|
)
|
|
107
108
|
return {
|
|
108
109
|
"__type__": "platform_time_index_metadata",
|
{mainsequence-4.1.19 → mainsequence-4.2.2}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
@@ -97,14 +97,23 @@ def ensure_registered_storage_table(
|
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
if storage_table.get_time_index_metadata() is None:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"{context} storage_table is not registered. Run "
|
|
102
|
+
"`mainsequence migrations upgrade --provider <provider> --to head` "
|
|
103
|
+
"before using this DataNode storage table."
|
|
104
|
+
)
|
|
103
105
|
|
|
104
106
|
storage_metadata = storage_table.get_time_index_metadata()
|
|
105
107
|
if storage_metadata is None:
|
|
106
108
|
raise ValueError(
|
|
107
|
-
f"{context} storage_table
|
|
109
|
+
f"{context} storage_table is missing TimeIndexMetaData metadata."
|
|
110
|
+
)
|
|
111
|
+
from mainsequence.client.metatables import TimeIndexMetaData
|
|
112
|
+
|
|
113
|
+
if not isinstance(storage_metadata, TimeIndexMetaData):
|
|
114
|
+
raise TypeError(
|
|
115
|
+
f"{context} storage_table must bind TimeIndexMetaData metadata; "
|
|
116
|
+
f"got {type(storage_metadata).__name__}."
|
|
108
117
|
)
|
|
109
118
|
if storage_table.get_meta_table_uid() in (None, ""):
|
|
110
119
|
raise ValueError(f"{context} storage_table must provide a MetaTable UID.")
|
|
@@ -117,7 +126,7 @@ class BasePersistManager:
|
|
|
117
126
|
UPDATE_CLASS: ClassVar[type[Any] | None] = None
|
|
118
127
|
UPDATE_DETAILS_CLASS: ClassVar[type[Any] | None] = None
|
|
119
128
|
|
|
120
|
-
|
|
129
|
+
UPDATE_GET_OR_NONE_STORAGE_LOOKUP: ClassVar[str] = "remote_table__uid"
|
|
121
130
|
UPDATE_CREATE_STORAGE_LOOKUP: ClassVar[str] = "meta_table_uid"
|
|
122
131
|
TIME_INDEXED_PROFILE_ATTR: ClassVar[str] = "time_indexed_profile"
|
|
123
132
|
|
|
@@ -180,7 +189,7 @@ class BasePersistManager:
|
|
|
180
189
|
"update_hash": self.update_hash,
|
|
181
190
|
"include_relations_detail": include_relations_detail,
|
|
182
191
|
}
|
|
183
|
-
kwargs[self.
|
|
192
|
+
kwargs[self.UPDATE_GET_OR_NONE_STORAGE_LOOKUP] = self.storage_table.get_meta_table_uid()
|
|
184
193
|
return kwargs
|
|
185
194
|
|
|
186
195
|
def _build_update_get_or_create_kwargs(
|
|
@@ -573,7 +582,7 @@ class APIPersistManager:
|
|
|
573
582
|
class PersistManager(BasePersistManager):
|
|
574
583
|
UPDATE_CLASS = DataNodeUpdate
|
|
575
584
|
UPDATE_DETAILS_CLASS = DataNodeUpdateDetails
|
|
576
|
-
|
|
585
|
+
UPDATE_GET_OR_NONE_STORAGE_LOOKUP = "remote_table__uid"
|
|
577
586
|
UPDATE_CREATE_STORAGE_LOOKUP = "meta_table_uid"
|
|
578
587
|
|
|
579
588
|
@classmethod
|