mainsequence 4.3.0__tar.gz → 4.3.5__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.
Files changed (129) hide show
  1. {mainsequence-4.3.0/mainsequence.egg-info → mainsequence-4.3.5}/PKG-INFO +1 -1
  2. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/migrations.py +25 -8
  3. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/metatables/core.py +35 -9
  4. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/compiled_sql/v1.py +9 -2
  5. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -1
  6. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -1
  7. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/migrations.py +69 -0
  8. {mainsequence-4.3.0 → mainsequence-4.3.5/mainsequence.egg-info}/PKG-INFO +1 -1
  9. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/entry_points.txt +1 -0
  10. {mainsequence-4.3.0 → mainsequence-4.3.5}/pyproject.toml +2 -1
  11. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_cli_migrations.py +102 -1
  12. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_data_access_mixin_dimension_audit.py +0 -2
  13. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_filter_normalization.py +16 -0
  14. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_meta_table_migrations.py +52 -0
  15. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_meta_tables_client_models.py +44 -1
  16. {mainsequence-4.3.0 → mainsequence-4.3.5}/LICENSE +0 -0
  17. {mainsequence-4.3.0 → mainsequence-4.3.5}/README.md +0 -0
  18. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/AGENTS.md +0 -0
  19. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  20. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  21. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  22. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  23. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  24. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  25. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  26. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  27. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  28. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  29. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  30. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  31. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  32. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  33. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
  34. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  35. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  36. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  37. {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  38. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/__init__.py +0 -0
  39. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/__main__.py +0 -0
  40. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/bootstrap.py +0 -0
  41. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/__init__.py +0 -0
  42. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/api.py +0 -0
  43. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/browser_auth.py +0 -0
  44. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/cli.py +0 -0
  45. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/config.py +0 -0
  46. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/docker_utils.py +0 -0
  47. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/doctor.py +0 -0
  48. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/local_ops.py +0 -0
  49. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/model_filters.py +0 -0
  50. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/project_status.py +0 -0
  51. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/pydantic_cli.py +0 -0
  52. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/sdk_utils.py +0 -0
  53. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/ssh_utils.py +0 -0
  54. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/ui.py +0 -0
  55. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/__init__.py +0 -0
  56. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/agent_runtime_models.py +0 -0
  57. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/base.py +0 -0
  58. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/client.py +0 -0
  59. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/__init__.py +0 -0
  60. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/app_component.py +0 -0
  61. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/connections.py +0 -0
  62. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/data_models.py +0 -0
  63. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/workspace.py +0 -0
  64. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  65. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/compute_validation.py +0 -0
  66. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  67. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  68. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  69. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  70. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/dtype_codec.py +0 -0
  71. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/exceptions.py +0 -0
  72. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/fastapi/__init__.py +0 -0
  73. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/fastapi/auth.py +0 -0
  74. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/metatables/__init__.py +0 -0
  75. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/models_foundry.py +0 -0
  76. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/models_helpers.py +0 -0
  77. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/models_user.py +0 -0
  78. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/utils.py +0 -0
  79. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/defaults.py +0 -0
  80. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/instrumentation/__init__.py +0 -0
  81. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/instrumentation/utils.py +0 -0
  82. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/logconf.py +0 -0
  83. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/__init__.py +0 -0
  84. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/__main__.py +0 -0
  85. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  86. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  87. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  88. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  89. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  90. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  91. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  92. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/future_registry.py +0 -0
  93. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/hashing.py +0 -0
  94. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  95. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/schema_names.py +0 -0
  96. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  97. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/runtime_flags.py +0 -0
  98. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/SOURCES.txt +0 -0
  99. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/dependency_links.txt +0 -0
  100. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/requires.txt +0 -0
  101. {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/top_level.txt +0 -0
  102. {mainsequence-4.3.0 → mainsequence-4.3.5}/setup.cfg +0 -0
  103. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_auth_precedence.py +0 -0
  104. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_build_operations_hashing.py +0 -0
  105. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_cli.py +0 -0
  106. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_cli_browser_auth.py +0 -0
  107. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_client.py +0 -0
  108. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_command_center_app_component_models.py +0 -0
  109. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_command_center_data_models.py +0 -0
  110. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_command_center_models.py +0 -0
  111. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_data_node_storage_dimension_queries.py +0 -0
  112. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_data_node_update_flow.py +0 -0
  113. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_dependency_extras.py +0 -0
  114. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_duckdb_interface_dimensions.py +0 -0
  115. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_instrumentation.py +0 -0
  116. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_logconf.py +0 -0
  117. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  118. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_models_user_request_bound_auth.py +0 -0
  119. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_pod_project_resolution.py +0 -0
  120. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_project_batch_jobs_from_file.py +0 -0
  121. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_run_configuration.py +0 -0
  122. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_schema_names.py +0 -0
  123. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_secret_client_model.py +0 -0
  124. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_source_table_configuration.py +0 -0
  125. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_sqlite_interface_dimensions.py +0 -0
  126. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_update_runner_uid_runtime.py +0 -0
  127. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_update_statistics.py +0 -0
  128. {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_update_uid_guards.py +0 -0
  129. {mainsequence-4.3.0 → mainsequence-4.3.5}/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.0
3
+ Version: 4.3.5
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
@@ -122,12 +122,21 @@ def _load_alembic_command(command_name: str) -> Any:
122
122
  return command
123
123
 
124
124
 
125
+ def _coerce_alembic_version_path(version_path: str) -> str:
126
+ try:
127
+ from alembic import util
128
+ except ImportError as exc:
129
+ raise typer.BadParameter("Alembic is required for migration commands.") from exc
130
+ return str(util.coerce_resource_to_filename(version_path))
131
+
132
+
125
133
  def _emit_alembic_script_context(
126
134
  config: Any,
127
135
  *,
128
136
  target_revision: str | None = None,
129
137
  ) -> None:
130
138
  script_location = config.get_main_option("script_location")
139
+ version_locations = config.get_main_option("version_locations")
131
140
  version_table = config.get_main_option("version_table")
132
141
  version_table_schema = config.get_main_option("version_table_schema")
133
142
  version_table_label = (
@@ -135,9 +144,14 @@ def _emit_alembic_script_context(
135
144
  if version_table_schema not in (None, "")
136
145
  else version_table
137
146
  )
147
+ version_locations_label = (
148
+ version_locations.replace("\n", ", ") if version_locations else "<default>"
149
+ )
138
150
  _emit_status(
139
151
  "Alembic script context "
140
- f"script_location={script_location} version_table={version_table_label}"
152
+ f"script_location={script_location} "
153
+ f"version_locations={version_locations_label} "
154
+ f"version_table={version_table_label}"
141
155
  )
142
156
  try:
143
157
  from alembic.script import ScriptDirectory
@@ -745,14 +759,17 @@ def revision(
745
759
  )
746
760
  _emit_alembic_script_context(config, target_revision=head)
747
761
  _emit_status(f"Starting Alembic revision now rev_id={resolved_rev_id}...")
762
+ revision_kwargs = {
763
+ "message": resolved_message,
764
+ "autogenerate": autogenerate,
765
+ "rev_id": resolved_rev_id,
766
+ "head": head,
767
+ }
768
+ version_path = migration.resolved_version_path()
769
+ if version_path is not None:
770
+ revision_kwargs["version_path"] = _coerce_alembic_version_path(version_path)
748
771
  with _forward_alembic_logging():
749
- script = command.revision(
750
- config,
751
- message=resolved_message,
752
- autogenerate=autogenerate,
753
- rev_id=resolved_rev_id,
754
- head=head,
755
- )
772
+ script = command.revision(config, **revision_kwargs)
756
773
  _emit_status("Alembic revision finished.")
757
774
  _emit(
758
775
  {
@@ -452,8 +452,43 @@ class MetaTableOperationScopeTable(BasePydanticModel):
452
452
 
453
453
 
454
454
  class MetaTableOperationScope(BasePydanticModel):
455
+ data_source_uid: str | None = Field(
456
+ default=None,
457
+ min_length=1,
458
+ validation_alias=AliasChoices("data_source_uid", "dataSourceUid"),
459
+ description=(
460
+ "Public UID of the DynamicTableDataSource that owns the compiled "
461
+ "SQL execution connection. If omitted, the SDK resolves the "
462
+ "configured project/session default data source. Scoped MetaTables "
463
+ "are the permission contract, not the source of execution routing."
464
+ ),
465
+ )
455
466
  tables: list[MetaTableOperationScopeTable] = Field(..., min_length=1)
456
467
 
468
+ model_config = ConfigDict(populate_by_name=True)
469
+
470
+ @model_validator(mode="after")
471
+ def resolve_default_data_source_uid(self):
472
+ if self.data_source_uid not in (None, ""):
473
+ self.data_source_uid = str(self.data_source_uid)
474
+ return self
475
+
476
+ try:
477
+ data_source = get_session_data_source()
478
+ except Exception as exc:
479
+ raise ValueError(
480
+ "MetaTable compiled SQL scope requires data_source_uid or a "
481
+ "configured project/session default data source."
482
+ ) from exc
483
+
484
+ uid = getattr(data_source, "uid", None) or getattr(data_source, "data_source_uid", None)
485
+ if uid in (None, ""):
486
+ raise ValueError(
487
+ "Configured project/session default data source does not expose a uid."
488
+ )
489
+ self.data_source_uid = str(uid)
490
+ return self
491
+
457
492
 
458
493
  class MetaTableOperationLimits(BasePydanticModel):
459
494
  max_rows: int | None = Field(default=None, ge=1)
@@ -2928,15 +2963,6 @@ class TimeIndexMetaTable(MetaTable):
2928
2963
  "labels__in": "str",
2929
2964
  "labels__contains": "str",
2930
2965
  }
2931
- READ_QUERY_PARAMS: ClassVar[dict[str, str]] = {
2932
- "include_relations_detail": "bool",
2933
- }
2934
- READ_QUERY_PARAM_DESCRIPTIONS: ClassVar[dict[str, str]] = {
2935
- "include_relations_detail": (
2936
- "Expand related objects in the serializer response. "
2937
- "This changes response detail only and does not change which rows are returned."
2938
- ),
2939
- }
2940
2966
  build_configuration_json_schema: dict[str, Any] | None = Field(
2941
2967
  None,
2942
2968
  description="JSON schema describing the DataNode update build configuration.",
@@ -34,8 +34,8 @@ def build_operation(
34
34
  Build and validate the TS Manager compiled-sql.v1 operation contract.
35
35
 
36
36
  This is the client-side protocol object. It is intentionally plain SQL plus
37
- bound parameters and declared MetaTable scope, not a serialized SQLAlchemy
38
- object.
37
+ bound parameters, an explicit execution data source, and declared MetaTable
38
+ scope, not a serialized SQLAlchemy object.
39
39
  """
40
40
 
41
41
  if parameters is None:
@@ -72,6 +72,7 @@ def compile_sqlalchemy_statement(
72
72
  statement: Any,
73
73
  *,
74
74
  operation: MetaTableOperation,
75
+ data_source_uid: str | None = None,
75
76
  scope_tables: Sequence[MetaTableOperationScopeTable | Mapping[str, Any]],
76
77
  limits: MetaTableOperationLimits | Mapping[str, Any] | None = None,
77
78
  dialect: MetaTableCompiledSQLDialect = "postgresql",
@@ -80,6 +81,11 @@ def compile_sqlalchemy_statement(
80
81
  ) -> MetaTableCompiledSQLOperation:
81
82
  """
82
83
  Compile a SQLAlchemy/Core statement into the TS Manager compiled-sql.v1 payload.
84
+
85
+ ``data_source_uid`` selects the DynamicTableDataSource connection. If it is
86
+ omitted, the SDK resolves the configured project/session default data source.
87
+ ``scope_tables`` remains the declared MetaTable permission scope for the
88
+ operation.
83
89
  """
84
90
 
85
91
  if dialect != "postgresql":
@@ -97,6 +103,7 @@ def compile_sqlalchemy_statement(
97
103
  )
98
104
  parameter_types = _compiled_sqlalchemy_parameter_types(compiled)
99
105
  scope = MetaTableOperationScope(
106
+ data_source_uid=data_source_uid,
100
107
  tables=[
101
108
  (
102
109
  table
@@ -211,7 +211,6 @@ class APIDataNode(DataAccessMixin):
211
211
  def build_from_table_name(cls, table_name: str) -> "APIDataNode":
212
212
  storage_table = TimeIndexMetaTable.get_or_none(
213
213
  physical_table_name=table_name,
214
- include_relations_detail=True,
215
214
  )
216
215
  if storage_table is None:
217
216
  raise DoesNotExist(
@@ -567,7 +567,6 @@ class APIPersistManager:
567
567
  result = TimeIndexMetaTable.get_or_none(
568
568
  physical_table_name=self.storage_hash,
569
569
  data_source__uid=self.data_source_uid,
570
- include_relations_detail=True,
571
570
  )
572
571
  self._storage_table_future.set_result(result)
573
572
  except Exception as exc:
@@ -101,6 +101,49 @@ class AlembicProviderPhysicalStateError(RuntimeError):
101
101
  )
102
102
 
103
103
 
104
+ def _normalize_optional_alembic_location(value: Any, field_name: str) -> str | None:
105
+ if value is None:
106
+ return None
107
+ text = str(value).strip()
108
+ if not text:
109
+ raise ValueError(f"AlembicMetaTableMigration {field_name} cannot be empty.")
110
+ return text
111
+
112
+
113
+ def _normalize_alembic_version_locations(value: str | Sequence[str] | None) -> tuple[str, ...]:
114
+ if value is None:
115
+ return ()
116
+ raw_locations: Sequence[str]
117
+ if isinstance(value, str):
118
+ raw_locations = [value]
119
+ else:
120
+ raw_locations = value
121
+
122
+ locations: list[str] = []
123
+ seen: set[str] = set()
124
+ for raw_location in raw_locations:
125
+ location = _normalize_optional_alembic_location(raw_location, "version_locations")
126
+ if location is None or location in seen:
127
+ continue
128
+ seen.add(location)
129
+ locations.append(location)
130
+ if not locations:
131
+ raise ValueError("AlembicMetaTableMigration version_locations cannot be empty.")
132
+ return tuple(locations)
133
+
134
+
135
+ def _configure_alembic_version_locations(
136
+ config: Any,
137
+ version_locations: str | Sequence[str] | None,
138
+ ) -> tuple[str, ...]:
139
+ locations = _normalize_alembic_version_locations(version_locations)
140
+ if not locations:
141
+ return ()
142
+ config.set_main_option("version_locations", "\n".join(locations))
143
+ config.set_main_option("path_separator", "newline")
144
+ return locations
145
+
146
+
104
147
  class AlembicVersionMetaTable:
105
148
  """MetaTable catalog binding for Alembic's version table.
106
149
 
@@ -255,6 +298,8 @@ class AlembicMetaTableMigration:
255
298
  script_location: str
256
299
  target_metadata: Any
257
300
  alembic_registry: type[AlembicVersionMetaTable]
301
+ version_locations: str | Sequence[str] | None = None
302
+ version_path: str | None = None
258
303
  metatable_models: Sequence[type[Any]] = field(default_factory=tuple)
259
304
  after_register_metatables: Callable[[AlembicMetaTableCatalogRefreshContext], Any] | None = None
260
305
  include_name_hook: Any | None = None
@@ -273,6 +318,8 @@ class AlembicMetaTableMigration:
273
318
  self.after_register_metatables
274
319
  ):
275
320
  raise TypeError("after_register_metatables must be callable when provided.")
321
+ self.resolved_version_locations()
322
+ self.resolved_version_path()
276
323
  _normalize_provider_default_schemas(self.metatable_models)
277
324
  _ensure_provider_time_index_grain_indexes(self.metatable_models)
278
325
 
@@ -292,6 +339,23 @@ class AlembicMetaTableMigration:
292
339
  def version_table_schema(self) -> str | None:
293
340
  return self.alembic_registry.__alembic_version_schema__
294
341
 
342
+ def resolved_version_locations(self) -> tuple[str, ...]:
343
+ return _normalize_alembic_version_locations(self.version_locations)
344
+
345
+ def resolved_version_path(self) -> str | None:
346
+ explicit = _normalize_optional_alembic_location(self.version_path, "version_path")
347
+ if explicit is not None:
348
+ return explicit
349
+ locations = self.resolved_version_locations()
350
+ if len(locations) == 1:
351
+ return locations[0]
352
+ if len(locations) > 1:
353
+ raise ValueError(
354
+ "AlembicMetaTableMigration with multiple version_locations requires "
355
+ "an explicit version_path for revision generation."
356
+ )
357
+ return None
358
+
295
359
  def include_name(self, name: str | None, type_: str, parent_names: dict[str, Any]) -> bool:
296
360
  if self.include_name_hook is not None:
297
361
  return bool(self.include_name_hook(name, type_, parent_names))
@@ -750,6 +814,7 @@ def alembic_config_for_provider(
750
814
  if output_buffer is not None:
751
815
  config.output_buffer = output_buffer
752
816
  config.set_main_option("script_location", migration.script_location)
817
+ version_locations = _configure_alembic_version_locations(config, migration.version_locations)
753
818
  config.set_main_option("sqlalchemy.url", sqlalchemy_url.replace("%", "%%"))
754
819
  config.set_main_option("sqlalchemy.echo", "true")
755
820
  config.set_main_option("version_table", migration.version_table)
@@ -764,6 +829,8 @@ def alembic_config_for_provider(
764
829
  config.attributes["alembic_version_table"] = migration.alembic_version_table
765
830
  config.attributes["version_table"] = migration.version_table
766
831
  config.attributes["version_table_schema"] = migration.version_table_schema
832
+ config.attributes["version_locations"] = version_locations
833
+ config.attributes["version_path"] = migration.resolved_version_path()
767
834
  return config
768
835
 
769
836
 
@@ -805,6 +872,7 @@ def resolve_alembic_revision_metadata(
805
872
  *,
806
873
  script_location: str,
807
874
  revision: str,
875
+ version_locations: str | Sequence[str] | None = None,
808
876
  ) -> tuple[str, str | None]:
809
877
  try:
810
878
  from alembic.config import Config
@@ -814,6 +882,7 @@ def resolve_alembic_revision_metadata(
814
882
 
815
883
  config = Config()
816
884
  config.set_main_option("script_location", script_location)
885
+ _configure_alembic_version_locations(config, version_locations)
817
886
  script = ScriptDirectory.from_config(config)
818
887
  resolved = script.get_revision(revision)
819
888
  if resolved is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.3.0
3
+ Version: 4.3.5
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
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  mainsequence = mainsequence.cli.cli:app
3
+ ms = mainsequence.cli.cli:app
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "4.3.0"
7
+ version = "4.3.5"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -60,6 +60,7 @@ Homepage = "https://github.com/mainsequence-sdk/mainsequence-sdk"
60
60
  Issues = "https://github.com/mainsequence-sdk/mainsequence-sdk/issues"
61
61
  [project.scripts]
62
62
  mainsequence = "mainsequence.cli.cli:app"
63
+ ms = "mainsequence.cli.cli:app"
63
64
 
64
65
  # PEP 621 dependencies (published requirements)
65
66
  [dependency-groups]
@@ -5,6 +5,7 @@ import logging
5
5
  import pathlib
6
6
  import sys
7
7
  import types
8
+ from collections.abc import Sequence
8
9
 
9
10
  import pytest
10
11
  from sqlalchemy import MetaData
@@ -37,7 +38,11 @@ def _load_cli_module():
37
38
  return importlib.import_module("mainsequence.cli.cli")
38
39
 
39
40
 
40
- def _migration() -> AlembicMetaTableMigration:
41
+ def _migration(
42
+ *,
43
+ version_locations: str | Sequence[str] | None = None,
44
+ version_path: str | None = None,
45
+ ) -> AlembicMetaTableMigration:
41
46
  class Registry(AlembicVersionMetaTable):
42
47
  __metatable_uid__ = "registry-meta-table-uid"
43
48
  __metatable_data_source_uid__ = "data-source-uid"
@@ -54,6 +59,8 @@ def _migration() -> AlembicMetaTableMigration:
54
59
  script_location="msm:alembic",
55
60
  target_metadata=MetaData(),
56
61
  alembic_registry=Registry,
62
+ version_locations=version_locations,
63
+ version_path=version_path,
57
64
  )
58
65
 
59
66
 
@@ -360,6 +367,100 @@ def test_migrations_revision_forwards_alembic_logs_and_scans_revision_id(monkeyp
360
367
  assert "Preparing platform-managed MetaTable reservations" not in output
361
368
 
362
369
 
370
+ def test_coerce_alembic_version_path_resolves_package_resource(tmp_path, monkeypatch):
371
+ migration_cli = importlib.import_module("mainsequence.cli.migrations")
372
+ package_root = tmp_path / "sample_provider"
373
+ namespace_versions = package_root / "migrations" / "versions" / "mainsequence_examples"
374
+ namespace_versions.mkdir(parents=True)
375
+ (package_root / "__init__.py").write_text("", encoding="utf-8")
376
+ (package_root / "migrations" / "__init__.py").write_text("", encoding="utf-8")
377
+ monkeypatch.syspath_prepend(str(tmp_path))
378
+
379
+ assert migration_cli._coerce_alembic_version_path(
380
+ "sample_provider:migrations/versions/mainsequence_examples"
381
+ ) == str(namespace_versions)
382
+
383
+
384
+ def test_migrations_revision_passes_provider_version_path(monkeypatch):
385
+ cli_mod = _load_cli_module()
386
+ runner = CliRunner()
387
+ migration_cli = importlib.import_module("mainsequence.cli.migrations")
388
+ version_location = "msm:alembic/versions/mainsequence_examples"
389
+ resolved_version_path = "/tmp/msm/alembic/versions/mainsequence_examples"
390
+ migration = _migration(version_locations=[version_location], version_path=version_location)
391
+ captured = {}
392
+ monkeypatch.setattr(migration_cli, "_load_migration", lambda provider: migration)
393
+
394
+ def fake_coerce_version_path(path):
395
+ captured["unresolved_version_path"] = path
396
+ return resolved_version_path
397
+
398
+ monkeypatch.setattr(migration_cli, "_coerce_alembic_version_path", fake_coerce_version_path)
399
+
400
+ def fail_backend_call(*args, **kwargs):
401
+ raise AssertionError("revision must not provision MetaTables")
402
+
403
+ monkeypatch.setattr(
404
+ AlembicMetaTableMigration,
405
+ "ensure_alembic_registry",
406
+ fail_backend_call,
407
+ )
408
+ monkeypatch.setattr(
409
+ AlembicMetaTableMigration,
410
+ "prepare_for_alembic",
411
+ fail_backend_call,
412
+ )
413
+
414
+ from alembic import command
415
+ from alembic.script import ScriptDirectory
416
+
417
+ class FakeScriptDirectory:
418
+ def get_heads(self):
419
+ return ["0004"]
420
+
421
+ def fake_script_directory_from_config(config):
422
+ captured["script_context_version_locations"] = config.get_main_option(
423
+ "version_locations"
424
+ )
425
+ captured["script_context_path_separator"] = config.get_main_option("path_separator")
426
+ return FakeScriptDirectory()
427
+
428
+ monkeypatch.setattr(
429
+ ScriptDirectory,
430
+ "from_config",
431
+ staticmethod(fake_script_directory_from_config),
432
+ )
433
+
434
+ def fake_revision(config, **kwargs):
435
+ captured["revision_version_locations"] = config.get_main_option("version_locations")
436
+ captured["revision_path_separator"] = config.get_main_option("path_separator")
437
+ captured["version_path"] = kwargs["version_path"]
438
+ return types.SimpleNamespace(revision=kwargs["rev_id"], path="/tmp/0005_migration.py")
439
+
440
+ monkeypatch.setattr(command, "revision", fake_revision)
441
+
442
+ result = runner.invoke(
443
+ cli_mod.app,
444
+ [
445
+ "migrations",
446
+ "revision",
447
+ "--provider",
448
+ "ignored:migration",
449
+ "--rev-id",
450
+ "0005",
451
+ "--no-autogenerate",
452
+ ],
453
+ )
454
+
455
+ assert result.exit_code == 0
456
+ assert captured["script_context_version_locations"] == version_location
457
+ assert captured["script_context_path_separator"] == "newline"
458
+ assert captured["revision_version_locations"] == version_location
459
+ assert captured["revision_path_separator"] == "newline"
460
+ assert captured["unresolved_version_path"] == version_location
461
+ assert captured["version_path"] == resolved_version_path
462
+
463
+
363
464
  def test_migrations_revision_default_autogenerates_without_metatable_provisioning(monkeypatch):
364
465
  cli_mod = _load_cli_module()
365
466
  runner = CliRunner()
@@ -142,7 +142,6 @@ def test_api_data_node_build_from_table_name_uses_physical_table_name(monkeypatc
142
142
 
143
143
  assert captured == {
144
144
  "physical_table_name": "prices_table",
145
- "include_relations_detail": True,
146
145
  }
147
146
  assert node.storage_hash == "prices_table"
148
147
  assert node.data_source_uid == "data-source-uid"
@@ -182,7 +181,6 @@ def test_api_persist_manager_resolves_storage_by_table_name_and_data_source(monk
182
181
  assert captured == {
183
182
  "physical_table_name": "prices_table",
184
183
  "data_source__uid": "data-source-uid",
185
- "include_relations_detail": True,
186
184
  }
187
185
 
188
186
 
@@ -287,6 +287,22 @@ def test_data_node_storage_rejects_data_source_id_filter():
287
287
  TimeIndexMetaTable._normalize_filter_kwargs({"data_source__id": {"id": 7}})
288
288
 
289
289
 
290
+ def test_include_relations_detail_is_only_data_node_update_read_param():
291
+ from mainsequence.client.metatables import DataNodeUpdate, TimeIndexMetaTable
292
+
293
+ assert "include_relations_detail" in DataNodeUpdate.READ_QUERY_PARAMS
294
+ assert "include_relations_detail" not in (TimeIndexMetaTable.READ_QUERY_PARAMS or {})
295
+
296
+ filter_kwargs, read_query_kwargs = TimeIndexMetaTable._split_filter_and_read_query_kwargs(
297
+ {"include_relations_detail": True}
298
+ )
299
+
300
+ assert read_query_kwargs == {}
301
+ assert filter_kwargs == {"include_relations_detail": True}
302
+ with pytest.raises(ValueError, match="Unsupported TimeIndexMetaTable filter"):
303
+ TimeIndexMetaTable._normalize_filter_kwargs(filter_kwargs)
304
+
305
+
290
306
  def test_data_node_storage_delete_after_date_posts_tail_delete(monkeypatch):
291
307
  from mainsequence.client import metatables as models_metatables
292
308
 
@@ -34,6 +34,7 @@ from mainsequence.meta_tables.migrations import (
34
34
  alembic_config_for_provider,
35
35
  apply_mainsequence_migration_role,
36
36
  load_alembic_metatable_migration_provider,
37
+ resolve_alembic_revision_metadata,
37
38
  )
38
39
 
39
40
 
@@ -659,6 +660,57 @@ def test_alembic_config_for_provider_uses_scoped_url_and_owner_role():
659
660
  )
660
661
 
661
662
 
663
+ def test_alembic_config_for_provider_supports_namespace_version_locations(tmp_path):
664
+ script_root = tmp_path / "migrations"
665
+ namespace_versions = script_root / "versions" / "mainsequence_examples"
666
+ namespace_versions.mkdir(parents=True)
667
+ (script_root / "env.py").write_text("", encoding="utf-8")
668
+ (namespace_versions / "0003_migration.py").write_text(
669
+ "revision = '0003'\n"
670
+ "down_revision = None\n"
671
+ "branch_labels = None\n"
672
+ "depends_on = None\n",
673
+ encoding="utf-8",
674
+ )
675
+ (namespace_versions / "0004_migration.py").write_text(
676
+ "revision = '0004'\n"
677
+ "down_revision = '0003'\n"
678
+ "branch_labels = None\n"
679
+ "depends_on = None\n",
680
+ encoding="utf-8",
681
+ )
682
+
683
+ class ProjectAlembicVersion(AlembicVersionMetaTable):
684
+ __metatable_data_source_uid__ = "data-source-uid"
685
+
686
+ migration = AlembicMetaTableMigration(
687
+ package="sample",
688
+ migration_namespace="markets",
689
+ script_location=str(script_root),
690
+ version_locations=[str(namespace_versions)],
691
+ version_path=str(namespace_versions),
692
+ target_metadata=MetaData(),
693
+ alembic_registry=ProjectAlembicVersion,
694
+ )
695
+
696
+ config = alembic_config_for_provider(migration, sqlalchemy_url="postgresql://example")
697
+
698
+ assert config.get_main_option("version_locations") == str(namespace_versions)
699
+ assert config.get_main_option("path_separator") == "newline"
700
+ assert config.attributes["version_locations"] == (str(namespace_versions),)
701
+ assert config.attributes["version_path"] == str(namespace_versions)
702
+
703
+ from alembic.script import ScriptDirectory
704
+
705
+ script = ScriptDirectory.from_config(config)
706
+ assert script.get_revision("0004").revision == "0004"
707
+ assert resolve_alembic_revision_metadata(
708
+ script_location=str(script_root),
709
+ version_locations=[str(namespace_versions)],
710
+ revision="0004",
711
+ ) == ("0004", "0003")
712
+
713
+
662
714
  def test_apply_mainsequence_migration_role_executes_quoted_set_role():
663
715
  class FakeConnection:
664
716
  def __init__(self):
@@ -350,6 +350,7 @@ def test_meta_table_execute_operation_serializes_scope_uid(monkeypatch):
350
350
  parameters={"symbol_1": "%BTC%"},
351
351
  ),
352
352
  scope=meta_table_models.MetaTableOperationScope(
353
+ data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
353
354
  tables=[
354
355
  meta_table_models.MetaTableOperationScopeTable(
355
356
  metaTableUid="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
@@ -367,6 +368,10 @@ def test_meta_table_execute_operation_serializes_scope_uid(monkeypatch):
367
368
  assert result["ok"] is True
368
369
  assert captured["r_type"] == "POST"
369
370
  assert captured["url"].endswith("/ts_manager/meta_table/execute-operation/")
371
+ assert (
372
+ captured["payload"]["json"]["scope"]["data_source_uid"]
373
+ == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
374
+ )
370
375
  assert captured["payload"]["json"]["scope"]["tables"][0] == {
371
376
  "meta_table_uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
372
377
  "alias": "asset",
@@ -697,6 +702,7 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
697
702
  sql="SELECT asset.symbol FROM public.asset AS asset",
698
703
  parameters={"symbol_1": "%BTC%"},
699
704
  scope={
705
+ "dataSourceUid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
700
706
  "tables": [
701
707
  {
702
708
  "metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
@@ -712,6 +718,7 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
712
718
  assert operation.version == "compiled-sql.v1"
713
719
  assert operation.dialect == "postgresql"
714
720
  assert operation.statement.paramstyle == "pyformat"
721
+ assert operation.scope.data_source_uid == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
715
722
  assert operation.scope.tables[0].meta_table_uid == "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
716
723
  assert operation.scope.tables[0].reserved_policy == "reconcile"
717
724
  assert (
@@ -725,6 +732,7 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
725
732
  version="compiled-sql.v2",
726
733
  statement=meta_table_models.MetaTableStatementPayload(sql="SELECT 1"),
727
734
  scope=meta_table_models.MetaTableOperationScope(
735
+ data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
728
736
  tables=[
729
737
  meta_table_models.MetaTableOperationScopeTable(
730
738
  metaTableUid="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
@@ -737,10 +745,40 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
737
745
  meta_table_models.MetaTableCompiledSQLOperation(
738
746
  operation="select",
739
747
  statement=meta_table_models.MetaTableStatementPayload(sql="SELECT 1"),
740
- scope=meta_table_models.MetaTableOperationScope(tables=[]),
748
+ scope=meta_table_models.MetaTableOperationScope(
749
+ data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
750
+ tables=[],
751
+ ),
741
752
  )
742
753
 
743
754
 
755
+ def test_compiled_sql_v1_scope_resolves_session_data_source(monkeypatch):
756
+ monkeypatch.setattr(
757
+ meta_table_models,
758
+ "get_session_data_source",
759
+ lambda: SimpleNamespace(uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd"),
760
+ )
761
+
762
+ operation = build_operation(
763
+ operation="select",
764
+ sql="SELECT asset.symbol FROM public.asset AS asset",
765
+ scope={
766
+ "tables": [
767
+ {
768
+ "metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
769
+ "alias": "asset",
770
+ }
771
+ ]
772
+ },
773
+ )
774
+
775
+ assert operation.scope.data_source_uid == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
776
+ assert (
777
+ operation.model_dump(mode="json", by_alias=True)["scope"]["data_source_uid"]
778
+ == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
779
+ )
780
+
781
+
744
782
  def test_compiled_sql_v1_serializes_typed_temporal_parameters():
745
783
  operation = build_operation(
746
784
  operation="select",
@@ -754,6 +792,7 @@ def test_compiled_sql_v1_serializes_typed_temporal_parameters():
754
792
  "seen_at": "datetime64[ns, UTC]",
755
793
  },
756
794
  scope={
795
+ "data_source_uid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
757
796
  "tables": [
758
797
  {
759
798
  "metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
@@ -780,6 +819,7 @@ def test_compiled_sql_v1_rejects_untyped_temporal_parameters():
780
819
  sql="SELECT * FROM asset WHERE as_of = %(as_of)s",
781
820
  parameters={"as_of": datetime.date(2026, 5, 28)},
782
821
  scope={
822
+ "data_source_uid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
783
823
  "tables": [
784
824
  {
785
825
  "metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
@@ -804,6 +844,7 @@ def test_compile_sqlalchemy_statement_emits_temporal_parameter_types():
804
844
  operation = compile_sqlalchemy_statement(
805
845
  statement,
806
846
  operation="select",
847
+ data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
807
848
  scope_tables=[
808
849
  {
809
850
  "metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
@@ -815,6 +856,7 @@ def test_compile_sqlalchemy_statement_emits_temporal_parameter_types():
815
856
  assert operation.statement.parameter_types == {
816
857
  "seen_at": "timestamp with time zone",
817
858
  }
859
+ assert operation.scope.data_source_uid == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
818
860
  assert operation.statement.parameters["seen_at"] == "2026-05-28T12:30:00Z"
819
861
 
820
862
 
@@ -833,6 +875,7 @@ def test_compile_sqlalchemy_statement_rejects_naive_datetime_bind_types():
833
875
  compile_sqlalchemy_statement(
834
876
  statement,
835
877
  operation="select",
878
+ data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
836
879
  scope_tables=[
837
880
  {
838
881
  "metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
File without changes
File without changes
File without changes