mainsequence 4.1.19__tar.gz → 4.2.1__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.1}/PKG-INFO +2 -1
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +15 -22
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +34 -19
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/migrations.py +109 -57
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/metatables/core.py +3 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/metatables/migrations.py +2 -5
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/__init__.py +4 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/build_operations.py +6 -5
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/persist_managers.py +9 -7
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/migrations.py +132 -77
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/sqlalchemy_contracts.py +35 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1/mainsequence.egg-info}/PKG-INFO +2 -1
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence.egg-info/requires.txt +1 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/pyproject.toml +2 -1
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_build_operations_hashing.py +6 -23
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_cli_migrations.py +244 -20
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_filter_normalization.py +2 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_meta_table_migrations.py +192 -29
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_meta_tables_client_models.py +37 -2
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_meta_tables_sqlalchemy_contracts.py +39 -41
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_run_configuration.py +27 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/LICENSE +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/README.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/__main__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/base.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/client.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/defaults.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/logconf.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/setup.cfg +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_cli.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_client.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_logconf.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.1.19 → mainsequence-4.2.1}/tests/test_workspace_snapshot.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mainsequence
|
|
3
|
-
Version: 4.1
|
|
3
|
+
Version: 4.2.1
|
|
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.1}/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
|
|
|
@@ -161,15 +161,18 @@ class Account(PlatformManagedMetaTable, Base):
|
|
|
161
161
|
},
|
|
162
162
|
)
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
account_meta_table = Account.register()
|
|
166
164
|
```
|
|
167
165
|
|
|
168
|
-
Registration metadata belongs on the class. Do not
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
Registration metadata belongs on the class. Do not call `Account.register()`
|
|
167
|
+
directly for platform-managed models. Add platform-managed models to the
|
|
168
|
+
selected `AlembicMetaTableMigration.metatable_models` list and let
|
|
169
|
+
`mainsequence migrations upgrade --provider ... --to head` resolve/register and
|
|
170
|
+
bind them.
|
|
171
171
|
|
|
172
|
-
For platform-managed registration, the data source is resolved from
|
|
172
|
+
For platform-managed migration registration, the data source is resolved from
|
|
173
|
+
the active Main Sequence project/session, the same way DataNode does. Do not
|
|
174
|
+
require or thread a `data_source_uid` through normal platform-managed example
|
|
175
|
+
code.
|
|
173
176
|
|
|
174
177
|
Only call `build_registration_request()` when the task explicitly needs to inspect or validate the payload before registration.
|
|
175
178
|
|
|
@@ -183,7 +186,8 @@ Do not add an environment variable for namespace in examples.
|
|
|
183
186
|
|
|
184
187
|
Do not add generic labels such as `"meta-table"` or `"platform-managed"` to examples. Keep labels specific to the example or domain.
|
|
185
188
|
|
|
186
|
-
Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in
|
|
189
|
+
Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in platform-managed
|
|
190
|
+
examples. Platform-managed examples should be migration-first.
|
|
187
191
|
|
|
188
192
|
### 3. Register parent tables before child tables
|
|
189
193
|
|
|
@@ -192,8 +196,8 @@ Foreign-key contracts reference the target `MetaTable` UID.
|
|
|
192
196
|
For `PlatformManagedMetaTable`, define foreign keys with
|
|
193
197
|
`MetaTableForeignKey(TargetModel, column=...)`. Do not write raw SQLAlchemy
|
|
194
198
|
table fullnames, `Parent.__table__.c.<column>` targets, or explicit target UID
|
|
195
|
-
maps in the platform-managed path.
|
|
196
|
-
|
|
199
|
+
maps in the platform-managed path. Migration is the lifecycle path. Migration
|
|
200
|
+
tooling resolves/registers unresolved target model classes, stores each
|
|
197
201
|
returned `MetaTable` in a local process registry keyed by `storage_hash`, and
|
|
198
202
|
uses the target `MetaTable.uid` in the child FK contract.
|
|
199
203
|
|
|
@@ -215,16 +219,19 @@ account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
|
215
219
|
Every participating table must include `__metatable_description__` describing
|
|
216
220
|
both the schema and the table's intention.
|
|
217
221
|
|
|
218
|
-
|
|
222
|
+
Provider scope:
|
|
219
223
|
|
|
220
224
|
```python
|
|
221
|
-
|
|
225
|
+
migration = AlembicMetaTableMigration(
|
|
226
|
+
...,
|
|
227
|
+
metatable_models=[Account, Asset],
|
|
228
|
+
)
|
|
222
229
|
```
|
|
223
230
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
registration attempts for the same `storage_hash` and raises
|
|
227
|
-
recursive registration cycles.
|
|
231
|
+
Migration tooling registers `Account` first if `Asset` depends on it and it has
|
|
232
|
+
not already been bound in the current process. The local registry prevents
|
|
233
|
+
duplicate backend registration attempts for the same `storage_hash` and raises
|
|
234
|
+
a clear error for recursive registration cycles.
|
|
228
235
|
|
|
229
236
|
For `external_registered`, there is no platform-managed parent lookup. Register
|
|
230
237
|
the parent first, then build the child registration request with
|
|
@@ -278,17 +285,25 @@ migration request models, or use SDK helper functions directly. The backend
|
|
|
278
285
|
request shape is reference material in the tutorial; the user-facing path is:
|
|
279
286
|
|
|
280
287
|
```bash
|
|
281
|
-
mainsequence migrations
|
|
282
|
-
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
288
|
+
mainsequence migrations current --provider mainsequence_migrations:migration
|
|
289
|
+
mainsequence migrations revision --provider mainsequence_migrations:migration
|
|
283
290
|
mainsequence migrations render --provider mainsequence_migrations:migration --to head
|
|
284
291
|
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head --dry-run
|
|
285
292
|
mainsequence migrations upgrade --provider mainsequence_migrations:migration --to head
|
|
286
293
|
```
|
|
287
294
|
|
|
295
|
+
`current` and `upgrade` automatically register the provider's
|
|
296
|
+
`AlembicVersionMetaTable` binding when backend migration state is needed.
|
|
297
|
+
`revision` accepts optional `-m/--message`; if omitted, the CLI uses
|
|
298
|
+
`migration`. `revision --autogenerate` is optional and requires an explicit
|
|
299
|
+
`--sqlalchemy-url` for the baseline database.
|
|
300
|
+
|
|
288
301
|
The SQL must be Alembic-rendered from the selected provider. After SQL apply
|
|
289
302
|
succeeds, register or refresh only the application MetaTable catalog bindings
|
|
290
|
-
listed in `migration.metatable_models`.
|
|
291
|
-
|
|
303
|
+
listed in `migration.metatable_models`. Do not pass the Alembic version-table
|
|
304
|
+
data source into those application model registrations; each model uses its
|
|
305
|
+
own normal MetaTable data-source binding. A migration is not complete until
|
|
306
|
+
both backend SQL execution and catalog sync succeed.
|
|
292
307
|
|
|
293
308
|
Do not use SDK-managed migration artifact tables, artifact sync helpers, or custom
|
|
294
309
|
`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(
|
|
@@ -1653,12 +1653,15 @@ class DataNodeUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
1653
1653
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
|
|
1654
1654
|
"uid": ["in", "exact"],
|
|
1655
1655
|
"update_hash": ["exact"],
|
|
1656
|
+
"remote_table__uid": ["exact", "in"],
|
|
1656
1657
|
"remote_table__data_source__uid": ["exact", "in"],
|
|
1657
1658
|
"related_table__namespace": ["contains", "in", "isnull"],
|
|
1658
1659
|
}
|
|
1659
1660
|
FILTER_VALUE_NORMALIZERS: ClassVar[dict[str, str]] = {
|
|
1660
1661
|
"uid": "uid",
|
|
1661
1662
|
"uid__in": "uid",
|
|
1663
|
+
"remote_table__uid": "uid",
|
|
1664
|
+
"remote_table__uid__in": "uid",
|
|
1662
1665
|
"remote_table__data_source__uid": "uid",
|
|
1663
1666
|
"remote_table__data_source__uid__in": "uid",
|
|
1664
1667
|
}
|
|
@@ -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.1}/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.1}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
@@ -97,14 +97,16 @@ 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."
|
|
108
110
|
)
|
|
109
111
|
if storage_table.get_meta_table_uid() in (None, ""):
|
|
110
112
|
raise ValueError(f"{context} storage_table must provide a MetaTable UID.")
|
|
@@ -117,7 +119,7 @@ class BasePersistManager:
|
|
|
117
119
|
UPDATE_CLASS: ClassVar[type[Any] | None] = None
|
|
118
120
|
UPDATE_DETAILS_CLASS: ClassVar[type[Any] | None] = None
|
|
119
121
|
|
|
120
|
-
|
|
122
|
+
UPDATE_GET_OR_NONE_STORAGE_LOOKUP: ClassVar[str] = "remote_table__uid"
|
|
121
123
|
UPDATE_CREATE_STORAGE_LOOKUP: ClassVar[str] = "meta_table_uid"
|
|
122
124
|
TIME_INDEXED_PROFILE_ATTR: ClassVar[str] = "time_indexed_profile"
|
|
123
125
|
|
|
@@ -180,7 +182,7 @@ class BasePersistManager:
|
|
|
180
182
|
"update_hash": self.update_hash,
|
|
181
183
|
"include_relations_detail": include_relations_detail,
|
|
182
184
|
}
|
|
183
|
-
kwargs[self.
|
|
185
|
+
kwargs[self.UPDATE_GET_OR_NONE_STORAGE_LOOKUP] = self.storage_table.get_meta_table_uid()
|
|
184
186
|
return kwargs
|
|
185
187
|
|
|
186
188
|
def _build_update_get_or_create_kwargs(
|
|
@@ -573,7 +575,7 @@ class APIPersistManager:
|
|
|
573
575
|
class PersistManager(BasePersistManager):
|
|
574
576
|
UPDATE_CLASS = DataNodeUpdate
|
|
575
577
|
UPDATE_DETAILS_CLASS = DataNodeUpdateDetails
|
|
576
|
-
|
|
578
|
+
UPDATE_GET_OR_NONE_STORAGE_LOOKUP = "remote_table__uid"
|
|
577
579
|
UPDATE_CREATE_STORAGE_LOOKUP = "meta_table_uid"
|
|
578
580
|
|
|
579
581
|
@classmethod
|