mainsequence 4.2.38__tar.gz → 4.2.40__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.2.38/mainsequence.egg-info → mainsequence-4.2.40}/PKG-INFO +1 -1
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +6 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +6 -2
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/migrations.py +28 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/sqlalchemy_contracts.py +122 -10
- {mainsequence-4.2.38 → mainsequence-4.2.40/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-4.2.38 → mainsequence-4.2.40}/pyproject.toml +1 -1
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_meta_table_migrations.py +67 -1
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_meta_tables_sqlalchemy_contracts.py +145 -5
- {mainsequence-4.2.38 → mainsequence-4.2.40}/LICENSE +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/README.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/__main__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/base.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/client.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/metatables/core.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/defaults.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/logconf.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/setup.cfg +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_cli.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_cli_migrations.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_client.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_filter_normalization.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_logconf.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_schema_names.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_workspace_snapshot.py +0 -0
|
@@ -191,6 +191,12 @@ MetaTable migration provider and run `mainsequence migrations upgrade --provider
|
|
|
191
191
|
... head`. Do not call `PricesTable.register()` directly and do not rely on
|
|
192
192
|
DataNode construction to register storage tables.
|
|
193
193
|
|
|
194
|
+
`__index_names__` is the full DataNode storage grain. `PlatformTimeIndexMetaData`
|
|
195
|
+
automatically adds a SQLAlchemy unique index over that tuple before Alembic
|
|
196
|
+
autogenerate runs. Do not manually repeat the full grain unique index in
|
|
197
|
+
`__table_args__`; add ordinary SQLAlchemy `Index(...)` entries only for
|
|
198
|
+
additional lookup/performance paths.
|
|
199
|
+
|
|
194
200
|
`PlatformTimeIndexMetaData.register()` remains SDK plumbing for the migration
|
|
195
201
|
workflow. Do not manually attach an existing UID, reconstruct a generic
|
|
196
202
|
`MetaTable`, or use manual bind helpers as an authoring step.
|
|
@@ -137,7 +137,11 @@ first version, use Alembic. Keep the SDK model as a normal
|
|
|
137
137
|
`PlatformManagedMetaTable` or `PlatformTimeIndexMetaData` catalog contract, and
|
|
138
138
|
apply physical schema changes through the Alembic migration workflow.
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
Default-schema tables must leave SQLAlchemy `Table.schema` unset; do not write
|
|
141
|
+
`__table_args__ = {"schema": "public"}` for the default PostgreSQL schema. Set
|
|
142
|
+
schema metadata only for non-default schemas, using `__table_args__ = {"schema":
|
|
143
|
+
"custom_schema"}` or the tuple form ending in `{"schema": ...}`. Do not add a
|
|
144
|
+
separate MetaTable-specific schema attribute.
|
|
141
145
|
|
|
142
146
|
Always declare `__metatable_description__` on the model. The description must
|
|
143
147
|
explain the table's business intention, row grain, and expected use, not only
|
|
@@ -246,7 +250,7 @@ Use this pattern:
|
|
|
246
250
|
```python
|
|
247
251
|
account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
248
252
|
Uuid,
|
|
249
|
-
ForeignKey("
|
|
253
|
+
ForeignKey("sdk_examples__account.uid", ondelete="RESTRICT"),
|
|
250
254
|
nullable=False,
|
|
251
255
|
)
|
|
252
256
|
```
|
|
@@ -27,9 +27,14 @@ from mainsequence.meta_tables.hashing import build_meta_table_storage_hash
|
|
|
27
27
|
from mainsequence.meta_tables.sqlalchemy_contracts import (
|
|
28
28
|
PlatformManagedMetaTable,
|
|
29
29
|
PlatformTimeIndexMetaData,
|
|
30
|
+
_ensure_time_index_unique_grain_index,
|
|
31
|
+
_normalize_table_default_schema,
|
|
30
32
|
_resolve_model_data_source_uid,
|
|
31
33
|
_resolve_table,
|
|
34
|
+
_resolve_time_index_name,
|
|
35
|
+
_resolve_time_index_names,
|
|
32
36
|
_table_name,
|
|
37
|
+
_validate_time_index_contract,
|
|
33
38
|
platform_managed_migration_registration_context,
|
|
34
39
|
)
|
|
35
40
|
|
|
@@ -268,6 +273,8 @@ class AlembicMetaTableMigration:
|
|
|
268
273
|
self.after_register_metatables
|
|
269
274
|
):
|
|
270
275
|
raise TypeError("after_register_metatables must be callable when provided.")
|
|
276
|
+
_normalize_provider_default_schemas(self.metatable_models)
|
|
277
|
+
_ensure_provider_time_index_grain_indexes(self.metatable_models)
|
|
271
278
|
|
|
272
279
|
@property
|
|
273
280
|
def alembic_version_table(self) -> str:
|
|
@@ -956,6 +963,27 @@ def _metadata_table_names(target_metadata: Any) -> list[str]:
|
|
|
956
963
|
return list(dict.fromkeys(names))
|
|
957
964
|
|
|
958
965
|
|
|
966
|
+
def _normalize_provider_default_schemas(models: Sequence[type[Any]]) -> None:
|
|
967
|
+
for model in models:
|
|
968
|
+
if _is_platform_managed_metatable_model(model):
|
|
969
|
+
_normalize_table_default_schema(_resolve_table(model))
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
def _ensure_provider_time_index_grain_indexes(models: Sequence[type[Any]]) -> None:
|
|
973
|
+
for model in models:
|
|
974
|
+
if not _is_platform_time_index_metatable_model(model):
|
|
975
|
+
continue
|
|
976
|
+
table = _resolve_table(model)
|
|
977
|
+
time_index_name = _resolve_time_index_name(model)
|
|
978
|
+
index_names = _resolve_time_index_names(model, time_index_name=time_index_name)
|
|
979
|
+
_validate_time_index_contract(
|
|
980
|
+
columns=list(table.columns),
|
|
981
|
+
time_index_name=time_index_name,
|
|
982
|
+
index_names=index_names,
|
|
983
|
+
)
|
|
984
|
+
_ensure_time_index_unique_grain_index(table=table, index_names=index_names)
|
|
985
|
+
|
|
986
|
+
|
|
959
987
|
def _normalize_down_revision(value: Any) -> str | None:
|
|
960
988
|
if value in (None, ""):
|
|
961
989
|
return None
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/sqlalchemy_contracts.py
RENAMED
|
@@ -11,6 +11,7 @@ from uuid import UUID
|
|
|
11
11
|
from sqlalchemy import Table as _SQLAlchemyTable
|
|
12
12
|
from sqlalchemy import inspect as _sqlalchemy_inspect
|
|
13
13
|
from sqlalchemy.orm import declared_attr as _sqlalchemy_declared_attr
|
|
14
|
+
from sqlalchemy.schema import BLANK_SCHEMA as _SQLALCHEMY_BLANK_SCHEMA
|
|
14
15
|
|
|
15
16
|
from mainsequence.client.dtype_codec import (
|
|
16
17
|
is_temporal_token,
|
|
@@ -28,12 +29,14 @@ from mainsequence.client.metatables import (
|
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
from .hashing import build_meta_table_configured_storage_hash
|
|
32
|
+
from .schema_names import schema_index_name
|
|
31
33
|
|
|
32
34
|
DEFAULT_PLATFORM_MANAGED_PROVISIONING = {
|
|
33
35
|
"create_table": True,
|
|
34
36
|
"if_not_exists": True,
|
|
35
37
|
}
|
|
36
38
|
SERVER_GENERATED_UUID_DEFAULT = "gen_random_uuid()"
|
|
39
|
+
DEFAULT_POSTGRES_SCHEMA = "public"
|
|
37
40
|
_PLATFORM_MANAGED_MIGRATION_REGISTRATION_CONTEXT: contextvars.ContextVar[bool] = (
|
|
38
41
|
contextvars.ContextVar(
|
|
39
42
|
"mainsequence_platform_managed_migration_registration_context",
|
|
@@ -152,8 +155,14 @@ class PlatformManagedMetaTable:
|
|
|
152
155
|
|
|
153
156
|
name, metadata, *table_items = args
|
|
154
157
|
kwargs = dict(kwargs)
|
|
155
|
-
schema =
|
|
156
|
-
|
|
158
|
+
schema = _normalize_sqlalchemy_schema(
|
|
159
|
+
kwargs.get("schema")
|
|
160
|
+
if "schema" in kwargs
|
|
161
|
+
else _resolve_class_schema(cls, metadata=metadata)
|
|
162
|
+
)
|
|
163
|
+
if schema is None:
|
|
164
|
+
kwargs["schema"] = _SQLALCHEMY_BLANK_SCHEMA
|
|
165
|
+
else:
|
|
157
166
|
kwargs["schema"] = schema
|
|
158
167
|
|
|
159
168
|
from sqlalchemy import Table
|
|
@@ -307,8 +316,14 @@ class PlatformTimeIndexMetaData(PlatformManagedMetaTable):
|
|
|
307
316
|
|
|
308
317
|
name, metadata, *table_items = args
|
|
309
318
|
kwargs = dict(kwargs)
|
|
310
|
-
schema =
|
|
311
|
-
|
|
319
|
+
schema = _normalize_sqlalchemy_schema(
|
|
320
|
+
kwargs.get("schema")
|
|
321
|
+
if "schema" in kwargs
|
|
322
|
+
else _resolve_class_schema(cls, metadata=metadata)
|
|
323
|
+
)
|
|
324
|
+
if schema is None:
|
|
325
|
+
kwargs["schema"] = _SQLALCHEMY_BLANK_SCHEMA
|
|
326
|
+
else:
|
|
312
327
|
kwargs["schema"] = schema
|
|
313
328
|
|
|
314
329
|
columns = [item for item in table_items if _looks_like_column(item)]
|
|
@@ -322,7 +337,9 @@ class PlatformTimeIndexMetaData(PlatformManagedMetaTable):
|
|
|
322
337
|
|
|
323
338
|
from sqlalchemy import Table
|
|
324
339
|
|
|
325
|
-
|
|
340
|
+
table = Table(str(name), metadata, *table_items, **kwargs)
|
|
341
|
+
_ensure_time_index_unique_grain_index(table=table, index_names=index_names)
|
|
342
|
+
return table
|
|
326
343
|
|
|
327
344
|
@classmethod
|
|
328
345
|
def build_registration_request(
|
|
@@ -413,7 +430,7 @@ def table_contract_from_sqlalchemy_model(
|
|
|
413
430
|
include_physical_table_name: bool = True,
|
|
414
431
|
) -> MetaTableContract:
|
|
415
432
|
table = _resolve_table(model_or_table)
|
|
416
|
-
_resolve_schema(table, schema=schema)
|
|
433
|
+
resolved_schema = _resolve_schema(table, schema=schema)
|
|
417
434
|
module, qualname = _resolve_model_path(
|
|
418
435
|
model_or_table,
|
|
419
436
|
table_model_module=table_model_module,
|
|
@@ -430,6 +447,7 @@ def table_contract_from_sqlalchemy_model(
|
|
|
430
447
|
}
|
|
431
448
|
},
|
|
432
449
|
physical=MetaTablePhysicalContract(
|
|
450
|
+
schema_=resolved_schema,
|
|
433
451
|
table_name=_table_name(table) if include_physical_table_name else None,
|
|
434
452
|
),
|
|
435
453
|
columns=[
|
|
@@ -560,7 +578,10 @@ def time_indexed_registration_request_from_sqlalchemy_model(
|
|
|
560
578
|
),
|
|
561
579
|
},
|
|
562
580
|
},
|
|
563
|
-
"physical": {
|
|
581
|
+
"physical": {
|
|
582
|
+
"schema": resolved_schema,
|
|
583
|
+
"table_name": _table_name(table),
|
|
584
|
+
},
|
|
564
585
|
"columns": column_contracts,
|
|
565
586
|
},
|
|
566
587
|
)
|
|
@@ -741,7 +762,7 @@ def _table_name(table: Any) -> str:
|
|
|
741
762
|
def _resolve_schema(table: Any, *, schema: str | None = None) -> str:
|
|
742
763
|
resolved_schema = schema or getattr(table, "schema", None)
|
|
743
764
|
if not resolved_schema:
|
|
744
|
-
|
|
765
|
+
return DEFAULT_POSTGRES_SCHEMA
|
|
745
766
|
return str(resolved_schema)
|
|
746
767
|
|
|
747
768
|
|
|
@@ -752,7 +773,7 @@ def _resolve_class_namespace(cls: type[Any]) -> str:
|
|
|
752
773
|
return str(resolved_namespace)
|
|
753
774
|
|
|
754
775
|
|
|
755
|
-
def _resolve_class_schema(cls: type[Any], *, metadata: Any | None = None) -> str:
|
|
776
|
+
def _resolve_class_schema(cls: type[Any], *, metadata: Any | None = None) -> str | None:
|
|
756
777
|
table_args = getattr(cls, "__table_args__", None)
|
|
757
778
|
if isinstance(table_args, Mapping):
|
|
758
779
|
schema = table_args.get("schema")
|
|
@@ -762,7 +783,29 @@ def _resolve_class_schema(cls: type[Any], *, metadata: Any | None = None) -> str
|
|
|
762
783
|
schema = None
|
|
763
784
|
if schema is None and metadata is not None:
|
|
764
785
|
schema = getattr(metadata, "schema", None)
|
|
765
|
-
return
|
|
786
|
+
return _normalize_sqlalchemy_schema(schema)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def _normalize_sqlalchemy_schema(schema: Any | None) -> str | None:
|
|
790
|
+
if schema is None:
|
|
791
|
+
return None
|
|
792
|
+
schema_name = str(schema).strip()
|
|
793
|
+
if schema_name in ("", DEFAULT_POSTGRES_SCHEMA):
|
|
794
|
+
return None
|
|
795
|
+
return schema_name
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def _normalize_table_default_schema(table: Any) -> None:
|
|
799
|
+
if getattr(table, "schema", None) != DEFAULT_POSTGRES_SCHEMA:
|
|
800
|
+
return
|
|
801
|
+
|
|
802
|
+
metadata = getattr(table, "metadata", None)
|
|
803
|
+
if metadata is not None:
|
|
804
|
+
metadata._remove_table(table.name, table.schema)
|
|
805
|
+
table.schema = None
|
|
806
|
+
table.fullname = _table_name(table)
|
|
807
|
+
if metadata is not None:
|
|
808
|
+
metadata._add_table(table.name, table.schema, table)
|
|
766
809
|
|
|
767
810
|
|
|
768
811
|
def _resolve_data_source_uid(
|
|
@@ -1391,6 +1434,75 @@ def _validate_time_index_contract(
|
|
|
1391
1434
|
)
|
|
1392
1435
|
|
|
1393
1436
|
|
|
1437
|
+
def _ensure_time_index_unique_grain_index(
|
|
1438
|
+
*,
|
|
1439
|
+
table: Any,
|
|
1440
|
+
index_names: Sequence[str],
|
|
1441
|
+
) -> None:
|
|
1442
|
+
grain_names = [str(name) for name in index_names]
|
|
1443
|
+
if _has_unique_grain_enforcement(table=table, index_names=grain_names):
|
|
1444
|
+
return
|
|
1445
|
+
|
|
1446
|
+
from sqlalchemy import Index
|
|
1447
|
+
|
|
1448
|
+
Index(
|
|
1449
|
+
schema_index_name(_table_name(table), grain_names, unique=True),
|
|
1450
|
+
*(table.c[name] for name in grain_names),
|
|
1451
|
+
unique=True,
|
|
1452
|
+
)
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
def _has_unique_grain_enforcement(
|
|
1456
|
+
*,
|
|
1457
|
+
table: Any,
|
|
1458
|
+
index_names: Sequence[str],
|
|
1459
|
+
) -> bool:
|
|
1460
|
+
for index in _iter_indexes(table):
|
|
1461
|
+
if bool(getattr(index, "unique", False)) and _grain_columns_match(
|
|
1462
|
+
_index_column_names(index),
|
|
1463
|
+
index_names,
|
|
1464
|
+
):
|
|
1465
|
+
return True
|
|
1466
|
+
|
|
1467
|
+
for constraint in _iter_unique_constraints(table):
|
|
1468
|
+
if _grain_columns_match(_constraint_column_names(constraint), index_names):
|
|
1469
|
+
return True
|
|
1470
|
+
|
|
1471
|
+
return False
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
def _grain_columns_match(existing_names: Sequence[str], index_names: Sequence[str]) -> bool:
|
|
1475
|
+
existing = [str(name) for name in existing_names]
|
|
1476
|
+
expected = [str(name) for name in index_names]
|
|
1477
|
+
if len(existing) != len(expected):
|
|
1478
|
+
return False
|
|
1479
|
+
return set(existing) == set(expected)
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
def _index_column_names(index: Any) -> list[str]:
|
|
1483
|
+
return [_storage_item_name(item) for item in _iter_index_items(index)]
|
|
1484
|
+
|
|
1485
|
+
|
|
1486
|
+
def _iter_unique_constraints(table: Any) -> list[Any]:
|
|
1487
|
+
from sqlalchemy import UniqueConstraint
|
|
1488
|
+
|
|
1489
|
+
constraints = getattr(table, "constraints", None)
|
|
1490
|
+
if constraints is None:
|
|
1491
|
+
return []
|
|
1492
|
+
return [
|
|
1493
|
+
constraint
|
|
1494
|
+
for constraint in constraints
|
|
1495
|
+
if isinstance(constraint, UniqueConstraint)
|
|
1496
|
+
]
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
def _constraint_column_names(constraint: Any) -> list[str]:
|
|
1500
|
+
columns = getattr(constraint, "columns", None)
|
|
1501
|
+
if columns is None:
|
|
1502
|
+
return []
|
|
1503
|
+
return [_storage_item_name(column) for column in columns]
|
|
1504
|
+
|
|
1505
|
+
|
|
1394
1506
|
def _storage_identity_from_parts(
|
|
1395
1507
|
*,
|
|
1396
1508
|
columns: Sequence[Any],
|
|
@@ -16,7 +16,11 @@ from mainsequence.client.metatables import (
|
|
|
16
16
|
MetaTable,
|
|
17
17
|
TimeIndexMetaData,
|
|
18
18
|
)
|
|
19
|
-
from mainsequence.meta_tables import
|
|
19
|
+
from mainsequence.meta_tables import (
|
|
20
|
+
PlatformManagedMetaTable,
|
|
21
|
+
PlatformTimeIndexMetaData,
|
|
22
|
+
schema_index_name,
|
|
23
|
+
)
|
|
20
24
|
from mainsequence.meta_tables.migrations import (
|
|
21
25
|
DEFAULT_ALEMBIC_VERSION_COLUMN_NAME,
|
|
22
26
|
DEFAULT_ALEMBIC_VERSION_IDENTIFIER,
|
|
@@ -1005,6 +1009,68 @@ def test_prepare_for_alembic_routes_time_indexed_models_to_dynamic_table_bulk_cr
|
|
|
1005
1009
|
assert "schema_management" not in row
|
|
1006
1010
|
|
|
1007
1011
|
|
|
1012
|
+
def test_provider_adds_time_index_grain_index_when_table_cls_is_overridden():
|
|
1013
|
+
class Base(DeclarativeBase):
|
|
1014
|
+
metadata = MetaData(schema="public")
|
|
1015
|
+
|
|
1016
|
+
class CustomTimeIndexMixin(PlatformTimeIndexMetaData):
|
|
1017
|
+
__abstract__ = True
|
|
1018
|
+
|
|
1019
|
+
@classmethod
|
|
1020
|
+
def __table_cls__(cls, *args, **kwargs):
|
|
1021
|
+
name, metadata, *table_items = args
|
|
1022
|
+
return Table(str(name), metadata, *table_items, **kwargs)
|
|
1023
|
+
|
|
1024
|
+
class ProjectAlembicVersion(AlembicVersionMetaTable):
|
|
1025
|
+
__metatable_uid__ = "registry-meta-table-uid"
|
|
1026
|
+
__metatable_data_source_uid__ = "data-source-uid"
|
|
1027
|
+
|
|
1028
|
+
class Prices(CustomTimeIndexMixin, Base):
|
|
1029
|
+
__tablename__ = "example_assets__prices"
|
|
1030
|
+
__metatable_data_source_uid__ = "data-source-uid"
|
|
1031
|
+
__metatable_namespace__ = "example.assets"
|
|
1032
|
+
__metatable_identifier__ = "Prices"
|
|
1033
|
+
__time_index_name__ = "time_index"
|
|
1034
|
+
__index_names__ = ["time_index", "asset_identifier"]
|
|
1035
|
+
|
|
1036
|
+
time_index: Mapped[datetime.datetime] = mapped_column(
|
|
1037
|
+
DateTime(timezone=True),
|
|
1038
|
+
nullable=False,
|
|
1039
|
+
)
|
|
1040
|
+
asset_identifier: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
1041
|
+
close: Mapped[int] = mapped_column(Integer, nullable=True)
|
|
1042
|
+
|
|
1043
|
+
assert not Prices.__table__.indexes
|
|
1044
|
+
assert Prices.__table__.schema == "public"
|
|
1045
|
+
assert "public.example_assets__prices" in Base.metadata.tables
|
|
1046
|
+
|
|
1047
|
+
AlembicMetaTableMigration(
|
|
1048
|
+
package="sample",
|
|
1049
|
+
migration_namespace="markets",
|
|
1050
|
+
script_location="sample:migrations",
|
|
1051
|
+
target_metadata=Base.metadata,
|
|
1052
|
+
alembic_registry=ProjectAlembicVersion,
|
|
1053
|
+
metatable_models=[Prices],
|
|
1054
|
+
)
|
|
1055
|
+
|
|
1056
|
+
assert Prices.__table__.schema is None
|
|
1057
|
+
assert "example_assets__prices" in Base.metadata.tables
|
|
1058
|
+
assert "public.example_assets__prices" not in Base.metadata.tables
|
|
1059
|
+
|
|
1060
|
+
grain_indexes = [
|
|
1061
|
+
index
|
|
1062
|
+
for index in Prices.__table__.indexes
|
|
1063
|
+
if index.unique
|
|
1064
|
+
and [column.name for column in index.columns] == ["time_index", "asset_identifier"]
|
|
1065
|
+
]
|
|
1066
|
+
assert len(grain_indexes) == 1
|
|
1067
|
+
assert grain_indexes[0].name == schema_index_name(
|
|
1068
|
+
"example_assets__prices",
|
|
1069
|
+
["time_index", "asset_identifier"],
|
|
1070
|
+
unique=True,
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
|
|
1008
1074
|
def test_prepare_for_alembic_reserves_existing_table_name_with_provider_identity(monkeypatch):
|
|
1009
1075
|
class Base(DeclarativeBase):
|
|
1010
1076
|
metadata = MetaData()
|
|
@@ -22,6 +22,8 @@ from mainsequence.meta_tables import (
|
|
|
22
22
|
external_registered_registration_request_from_sqlalchemy_model,
|
|
23
23
|
platform_managed_migration_registration_context,
|
|
24
24
|
platform_managed_registration_request_from_sqlalchemy_model,
|
|
25
|
+
schema_index_name,
|
|
26
|
+
sqlalchemy_naming_convention,
|
|
25
27
|
table_contract_from_sqlalchemy_model,
|
|
26
28
|
time_indexed_registration_request_from_sqlalchemy_model,
|
|
27
29
|
)
|
|
@@ -708,6 +710,63 @@ def test_platform_managed_schema_resolves_from_sqlalchemy_table_args_only():
|
|
|
708
710
|
assert sqlalchemy_contracts._resolve_class_schema(Account) == "table_args_schema"
|
|
709
711
|
|
|
710
712
|
|
|
713
|
+
def test_platform_managed_default_public_schema_stays_sqlalchemy_default():
|
|
714
|
+
pytest.importorskip("sqlalchemy")
|
|
715
|
+
|
|
716
|
+
from sqlalchemy import MetaData
|
|
717
|
+
from sqlalchemy import Uuid as SQLAlchemyUuid
|
|
718
|
+
from sqlalchemy.orm import DeclarativeBase, mapped_column
|
|
719
|
+
|
|
720
|
+
class Base(DeclarativeBase):
|
|
721
|
+
metadata = MetaData()
|
|
722
|
+
|
|
723
|
+
class Account(PlatformManagedMetaTable, Base):
|
|
724
|
+
__tablename__ = "example_assets__account"
|
|
725
|
+
__table_args__ = {"schema": "public"}
|
|
726
|
+
__metatable_namespace__ = "example.assets"
|
|
727
|
+
__metatable_identifier__ = "Account"
|
|
728
|
+
|
|
729
|
+
uid: Mapped[uuid.UUID] = mapped_column(SQLAlchemyUuid, primary_key=True)
|
|
730
|
+
|
|
731
|
+
assert Account.__table__.schema is None
|
|
732
|
+
assert sqlalchemy_contracts._resolve_schema(Account.__table__) == "public"
|
|
733
|
+
|
|
734
|
+
request = Account.build_registration_request(
|
|
735
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
assert request.table_contract.physical.schema_ == "public"
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def test_platform_managed_default_public_metadata_schema_stays_sqlalchemy_default():
|
|
742
|
+
pytest.importorskip("sqlalchemy")
|
|
743
|
+
|
|
744
|
+
from sqlalchemy import MetaData
|
|
745
|
+
from sqlalchemy import Uuid as SQLAlchemyUuid
|
|
746
|
+
from sqlalchemy.orm import DeclarativeBase, mapped_column
|
|
747
|
+
|
|
748
|
+
class Base(DeclarativeBase):
|
|
749
|
+
metadata = MetaData(schema="public")
|
|
750
|
+
|
|
751
|
+
class Account(PlatformManagedMetaTable, Base):
|
|
752
|
+
__tablename__ = "example_assets__account"
|
|
753
|
+
__metatable_namespace__ = "example.assets"
|
|
754
|
+
__metatable_identifier__ = "Account"
|
|
755
|
+
|
|
756
|
+
uid: Mapped[uuid.UUID] = mapped_column(SQLAlchemyUuid, primary_key=True)
|
|
757
|
+
|
|
758
|
+
assert Account.__table__.schema is None
|
|
759
|
+
assert Account.__table__.fullname == "example_assets__account"
|
|
760
|
+
assert "example_assets__account" in Base.metadata.tables
|
|
761
|
+
assert "public.example_assets__account" not in Base.metadata.tables
|
|
762
|
+
|
|
763
|
+
request = Account.build_registration_request(
|
|
764
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
assert request.table_contract.physical.schema_ == "public"
|
|
768
|
+
|
|
769
|
+
|
|
711
770
|
def test_time_index_optional_table_info_resolvers_allow_pending_declarative_class():
|
|
712
771
|
class PendingTimeIndexTable:
|
|
713
772
|
__time_index_name__ = "time_index"
|
|
@@ -1535,6 +1594,85 @@ def test_platform_managed_metatable_preserves_authored_tablename_with_sqlalchemy
|
|
|
1535
1594
|
assert not hasattr(request.table_contract, "foreign_keys")
|
|
1536
1595
|
|
|
1537
1596
|
|
|
1597
|
+
def test_time_index_metadata_generates_unique_grain_index_with_schema_name():
|
|
1598
|
+
pytest.importorskip("sqlalchemy")
|
|
1599
|
+
|
|
1600
|
+
from sqlalchemy import DateTime, Float, MetaData, String
|
|
1601
|
+
from sqlalchemy.orm import DeclarativeBase, mapped_column
|
|
1602
|
+
|
|
1603
|
+
class Base(DeclarativeBase):
|
|
1604
|
+
metadata = MetaData(naming_convention=sqlalchemy_naming_convention())
|
|
1605
|
+
|
|
1606
|
+
class Prices(PlatformTimeIndexMetaData, Base):
|
|
1607
|
+
__tablename__ = "ms_markets__prices__mainsequence_examples"
|
|
1608
|
+
__metatable_namespace__ = "mainsequence.examples"
|
|
1609
|
+
__time_index_name__ = "time_index"
|
|
1610
|
+
__index_names__ = ["time_index", "asset_identifier"]
|
|
1611
|
+
|
|
1612
|
+
time_index: Mapped[datetime.datetime] = mapped_column(
|
|
1613
|
+
DateTime(timezone=True),
|
|
1614
|
+
nullable=False,
|
|
1615
|
+
)
|
|
1616
|
+
asset_identifier: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
1617
|
+
close: Mapped[float] = mapped_column(Float, nullable=True)
|
|
1618
|
+
|
|
1619
|
+
grain_indexes = [
|
|
1620
|
+
index
|
|
1621
|
+
for index in Prices.__table__.indexes
|
|
1622
|
+
if index.unique
|
|
1623
|
+
and [column.name for column in index.columns] == ["time_index", "asset_identifier"]
|
|
1624
|
+
]
|
|
1625
|
+
|
|
1626
|
+
assert len(grain_indexes) == 1
|
|
1627
|
+
assert grain_indexes[0].name == schema_index_name(
|
|
1628
|
+
"ms_markets__prices__mainsequence_examples",
|
|
1629
|
+
["time_index", "asset_identifier"],
|
|
1630
|
+
unique=True,
|
|
1631
|
+
)
|
|
1632
|
+
|
|
1633
|
+
|
|
1634
|
+
def test_time_index_metadata_reuses_existing_unique_grain_constraint():
|
|
1635
|
+
pytest.importorskip("sqlalchemy")
|
|
1636
|
+
|
|
1637
|
+
from sqlalchemy import DateTime, Float, MetaData, String, UniqueConstraint
|
|
1638
|
+
from sqlalchemy.orm import DeclarativeBase, mapped_column
|
|
1639
|
+
|
|
1640
|
+
class Base(DeclarativeBase):
|
|
1641
|
+
metadata = MetaData(naming_convention=sqlalchemy_naming_convention())
|
|
1642
|
+
|
|
1643
|
+
class Prices(PlatformTimeIndexMetaData, Base):
|
|
1644
|
+
__tablename__ = "ms_markets__prices__mainsequence_examples"
|
|
1645
|
+
__table_args__ = (
|
|
1646
|
+
UniqueConstraint(
|
|
1647
|
+
"asset_identifier",
|
|
1648
|
+
"time_index",
|
|
1649
|
+
name="uix_custom_asset_time",
|
|
1650
|
+
),
|
|
1651
|
+
)
|
|
1652
|
+
__metatable_namespace__ = "mainsequence.examples"
|
|
1653
|
+
__time_index_name__ = "time_index"
|
|
1654
|
+
__index_names__ = ["time_index", "asset_identifier"]
|
|
1655
|
+
|
|
1656
|
+
time_index: Mapped[datetime.datetime] = mapped_column(
|
|
1657
|
+
DateTime(timezone=True),
|
|
1658
|
+
nullable=False,
|
|
1659
|
+
)
|
|
1660
|
+
asset_identifier: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
1661
|
+
close: Mapped[float] = mapped_column(Float, nullable=True)
|
|
1662
|
+
|
|
1663
|
+
assert not Prices.__table__.indexes
|
|
1664
|
+
unique_constraints = [
|
|
1665
|
+
constraint
|
|
1666
|
+
for constraint in Prices.__table__.constraints
|
|
1667
|
+
if isinstance(constraint, UniqueConstraint)
|
|
1668
|
+
]
|
|
1669
|
+
assert len(unique_constraints) == 1
|
|
1670
|
+
assert [column.name for column in unique_constraints[0].columns] == [
|
|
1671
|
+
"asset_identifier",
|
|
1672
|
+
"time_index",
|
|
1673
|
+
]
|
|
1674
|
+
|
|
1675
|
+
|
|
1538
1676
|
def test_platform_managed_register_preserves_authored_sqlalchemy_table_name(
|
|
1539
1677
|
monkeypatch,
|
|
1540
1678
|
):
|
|
@@ -1582,14 +1720,15 @@ def test_platform_managed_register_preserves_authored_sqlalchemy_table_name(
|
|
|
1582
1720
|
assert Account.get_storage_hash() == storage_hash
|
|
1583
1721
|
assert Account.get_physical_table_name() == "example_assets__account"
|
|
1584
1722
|
assert Account.__table__.name == "example_assets__account"
|
|
1585
|
-
assert Account.__table__.
|
|
1586
|
-
assert
|
|
1723
|
+
assert Account.__table__.schema is None
|
|
1724
|
+
assert Account.__table__.fullname == "example_assets__account"
|
|
1725
|
+
assert Base.metadata.tables["example_assets__account"] is Account.__table__
|
|
1587
1726
|
assert f"public.{physical_table_name}" not in Base.metadata.tables
|
|
1588
1727
|
|
|
1589
1728
|
compiled_sql = str(
|
|
1590
1729
|
select(Account.__table__).compile(dialect=postgresql.dialect(paramstyle="pyformat"))
|
|
1591
1730
|
)
|
|
1592
|
-
assert "FROM
|
|
1731
|
+
assert "FROM example_assets__account" in compiled_sql
|
|
1593
1732
|
assert physical_table_name not in compiled_sql
|
|
1594
1733
|
assert storage_hash not in compiled_sql
|
|
1595
1734
|
|
|
@@ -1621,7 +1760,8 @@ def test_bound_parent_table_foreign_key_stays_sqlalchemy_only():
|
|
|
1621
1760
|
)
|
|
1622
1761
|
)
|
|
1623
1762
|
|
|
1624
|
-
assert Account.__table__.
|
|
1763
|
+
assert Account.__table__.schema is None
|
|
1764
|
+
assert Account.__table__.fullname == "example_assets__account"
|
|
1625
1765
|
|
|
1626
1766
|
class Asset(PlatformManagedMetaTable, Base):
|
|
1627
1767
|
__tablename__ = "example_assets__asset"
|
|
@@ -1631,7 +1771,7 @@ def test_bound_parent_table_foreign_key_stays_sqlalchemy_only():
|
|
|
1631
1771
|
uid: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
|
|
1632
1772
|
account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
1633
1773
|
Uuid,
|
|
1634
|
-
ForeignKey("
|
|
1774
|
+
ForeignKey("example_assets__account.uid", ondelete="RESTRICT"),
|
|
1635
1775
|
nullable=False,
|
|
1636
1776
|
)
|
|
1637
1777
|
symbol: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/a2a_communication/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/dashboards/streamlit/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/app_component.py
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/connections.py
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/data_models.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/command_center/workspace_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/sqlite.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/compiled_sql/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/data_nodes.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/namespacing.py
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/run_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_command_center_app_component_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.38 → mainsequence-4.2.40}/tests/test_data_node_storage_dimension_queries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|