relationalai 0.13.5__py3-none-any.whl → 1.0.0a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- relationalai/__init__.py +1 -256
- relationalai/config/__init__.py +56 -0
- relationalai/config/config.py +289 -0
- relationalai/config/config_fields.py +86 -0
- relationalai/config/connections/__init__.py +46 -0
- relationalai/config/connections/base.py +23 -0
- relationalai/config/connections/duckdb.py +29 -0
- relationalai/config/connections/snowflake.py +243 -0
- relationalai/config/external/__init__.py +17 -0
- relationalai/config/external/dbt_converter.py +101 -0
- relationalai/config/external/dbt_models.py +93 -0
- relationalai/config/external/snowflake_converter.py +41 -0
- relationalai/config/external/snowflake_models.py +85 -0
- relationalai/config/external/utils.py +19 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +146 -22
- relationalai/semantics/backends/lqp/annotations.py +11 -0
- relationalai/semantics/backends/sql/sql_compiler.py +327 -0
- relationalai/semantics/frontend/base.py +1719 -0
- relationalai/semantics/frontend/core.py +179 -0
- relationalai/semantics/frontend/front_compiler.py +1316 -0
- relationalai/semantics/frontend/pprint.py +408 -0
- relationalai/semantics/metamodel/__init__.py +6 -40
- relationalai/semantics/metamodel/builtins.py +206 -772
- relationalai/semantics/metamodel/metamodel.py +465 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
- relationalai/semantics/metamodel/pprint.py +414 -0
- relationalai/semantics/metamodel/rewriter.py +266 -0
- relationalai/semantics/metamodel/typer.py +1213 -0
- relationalai/semantics/std/__init__.py +60 -40
- relationalai/semantics/std/aggregates.py +148 -0
- relationalai/semantics/std/common.py +44 -0
- relationalai/semantics/std/constraints.py +37 -43
- relationalai/semantics/std/datetime.py +249 -135
- relationalai/semantics/std/decimals.py +45 -52
- relationalai/semantics/std/floats.py +13 -5
- relationalai/semantics/std/integers.py +26 -11
- relationalai/semantics/std/math.py +183 -112
- relationalai/semantics/std/numbers.py +86 -0
- relationalai/semantics/std/re.py +80 -62
- relationalai/semantics/std/strings.py +101 -46
- relationalai/shims/executor.py +179 -0
- relationalai/shims/helpers.py +126 -0
- relationalai/shims/hoister.py +221 -0
- relationalai/shims/mm2v0.py +1394 -0
- relationalai/tools/cli/__init__.py +6 -0
- relationalai/tools/cli/cli.py +90 -0
- relationalai/tools/cli/components/__init__.py +5 -0
- relationalai/tools/cli/components/progress_reader.py +1524 -0
- relationalai/tools/cli/components/utils.py +58 -0
- relationalai/tools/cli/config_template.py +45 -0
- relationalai/tools/cli/dev.py +19 -0
- relationalai/tools/debugger.py +289 -183
- relationalai/tools/typer_debugger.py +93 -0
- relationalai/util/dataclasses.py +43 -0
- relationalai/util/docutils.py +40 -0
- relationalai/util/error.py +199 -0
- relationalai/util/format.py +48 -109
- relationalai/util/naming.py +145 -0
- relationalai/util/python.py +35 -0
- relationalai/util/runtime.py +156 -0
- relationalai/util/schema.py +197 -0
- relationalai/util/source.py +185 -0
- relationalai/util/structures.py +163 -0
- relationalai/util/tracing.py +261 -0
- relationalai-1.0.0a2.dist-info/METADATA +44 -0
- relationalai-1.0.0a2.dist-info/RECORD +489 -0
- relationalai-1.0.0a2.dist-info/WHEEL +5 -0
- relationalai-1.0.0a2.dist-info/entry_points.txt +3 -0
- relationalai-1.0.0a2.dist-info/top_level.txt +2 -0
- v0/relationalai/__init__.py +216 -0
- v0/relationalai/clients/__init__.py +5 -0
- v0/relationalai/clients/azure.py +477 -0
- v0/relationalai/clients/client.py +912 -0
- v0/relationalai/clients/config.py +673 -0
- v0/relationalai/clients/direct_access_client.py +118 -0
- v0/relationalai/clients/hash_util.py +31 -0
- v0/relationalai/clients/local.py +571 -0
- v0/relationalai/clients/profile_polling.py +73 -0
- v0/relationalai/clients/result_helpers.py +420 -0
- v0/relationalai/clients/snowflake.py +3869 -0
- v0/relationalai/clients/types.py +113 -0
- v0/relationalai/clients/use_index_poller.py +980 -0
- v0/relationalai/clients/util.py +356 -0
- v0/relationalai/debugging.py +389 -0
- v0/relationalai/dsl.py +1749 -0
- v0/relationalai/early_access/builder/__init__.py +30 -0
- v0/relationalai/early_access/builder/builder/__init__.py +35 -0
- v0/relationalai/early_access/builder/snowflake/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/__init__.py +25 -0
- v0/relationalai/early_access/builder/std/decimals/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/integers/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/math/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/strings/__init__.py +14 -0
- v0/relationalai/early_access/devtools/__init__.py +12 -0
- v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
- v0/relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
- v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
- v0/relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
- v0/relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
- v0/relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
- v0/relationalai/early_access/dsl/bindings/common.py +402 -0
- v0/relationalai/early_access/dsl/bindings/csv.py +170 -0
- v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
- v0/relationalai/early_access/dsl/bindings/snowflake.py +64 -0
- v0/relationalai/early_access/dsl/codegen/binder.py +411 -0
- v0/relationalai/early_access/dsl/codegen/common.py +79 -0
- v0/relationalai/early_access/dsl/codegen/helpers.py +23 -0
- v0/relationalai/early_access/dsl/codegen/relations.py +700 -0
- v0/relationalai/early_access/dsl/codegen/weaver.py +417 -0
- v0/relationalai/early_access/dsl/core/builders/__init__.py +47 -0
- v0/relationalai/early_access/dsl/core/builders/logic.py +19 -0
- v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
- v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
- v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
- v0/relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
- v0/relationalai/early_access/dsl/core/context.py +13 -0
- v0/relationalai/early_access/dsl/core/cset.py +132 -0
- v0/relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
- v0/relationalai/early_access/dsl/core/exprs/relational.py +18 -0
- v0/relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
- v0/relationalai/early_access/dsl/core/instances.py +44 -0
- v0/relationalai/early_access/dsl/core/logic/__init__.py +193 -0
- v0/relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
- v0/relationalai/early_access/dsl/core/logic/exists.py +223 -0
- v0/relationalai/early_access/dsl/core/logic/helper.py +163 -0
- v0/relationalai/early_access/dsl/core/namespaces.py +32 -0
- v0/relationalai/early_access/dsl/core/relations.py +276 -0
- v0/relationalai/early_access/dsl/core/rules.py +112 -0
- v0/relationalai/early_access/dsl/core/std/__init__.py +45 -0
- v0/relationalai/early_access/dsl/core/temporal/recall.py +6 -0
- v0/relationalai/early_access/dsl/core/types/__init__.py +270 -0
- v0/relationalai/early_access/dsl/core/types/concepts.py +128 -0
- v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
- v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
- v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
- v0/relationalai/early_access/dsl/core/types/standard.py +92 -0
- v0/relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
- v0/relationalai/early_access/dsl/core/types/variables.py +203 -0
- v0/relationalai/early_access/dsl/ir/compiler.py +318 -0
- v0/relationalai/early_access/dsl/ir/executor.py +260 -0
- v0/relationalai/early_access/dsl/ontologies/constraints.py +88 -0
- v0/relationalai/early_access/dsl/ontologies/export.py +30 -0
- v0/relationalai/early_access/dsl/ontologies/models.py +453 -0
- v0/relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
- v0/relationalai/early_access/dsl/ontologies/readings.py +60 -0
- v0/relationalai/early_access/dsl/ontologies/relationships.py +322 -0
- v0/relationalai/early_access/dsl/ontologies/roles.py +87 -0
- v0/relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
- v0/relationalai/early_access/dsl/orm/constraints.py +438 -0
- v0/relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
- v0/relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
- v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
- v0/relationalai/early_access/dsl/orm/measures/measures.py +299 -0
- v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
- v0/relationalai/early_access/dsl/orm/models.py +256 -0
- v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
- v0/relationalai/early_access/dsl/orm/printer.py +469 -0
- v0/relationalai/early_access/dsl/orm/reasoners.py +480 -0
- v0/relationalai/early_access/dsl/orm/relations.py +19 -0
- v0/relationalai/early_access/dsl/orm/relationships.py +251 -0
- v0/relationalai/early_access/dsl/orm/types.py +42 -0
- v0/relationalai/early_access/dsl/orm/utils.py +79 -0
- v0/relationalai/early_access/dsl/orm/verb.py +204 -0
- v0/relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
- v0/relationalai/early_access/dsl/relations.py +170 -0
- v0/relationalai/early_access/dsl/rulesets.py +69 -0
- v0/relationalai/early_access/dsl/schemas/__init__.py +450 -0
- v0/relationalai/early_access/dsl/schemas/builder.py +48 -0
- v0/relationalai/early_access/dsl/schemas/comp_names.py +51 -0
- v0/relationalai/early_access/dsl/schemas/components.py +203 -0
- v0/relationalai/early_access/dsl/schemas/contexts.py +156 -0
- v0/relationalai/early_access/dsl/schemas/exprs.py +89 -0
- v0/relationalai/early_access/dsl/schemas/fragments.py +464 -0
- v0/relationalai/early_access/dsl/serialization.py +79 -0
- v0/relationalai/early_access/dsl/serialize/exporter.py +163 -0
- v0/relationalai/early_access/dsl/snow/api.py +104 -0
- v0/relationalai/early_access/dsl/snow/common.py +76 -0
- v0/relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
- v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
- v0/relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
- v0/relationalai/early_access/dsl/types/__init__.py +40 -0
- v0/relationalai/early_access/dsl/types/concepts.py +12 -0
- v0/relationalai/early_access/dsl/types/entities.py +135 -0
- v0/relationalai/early_access/dsl/types/values.py +17 -0
- v0/relationalai/early_access/dsl/utils.py +102 -0
- v0/relationalai/early_access/graphs/__init__.py +13 -0
- v0/relationalai/early_access/lqp/__init__.py +12 -0
- v0/relationalai/early_access/lqp/compiler/__init__.py +12 -0
- v0/relationalai/early_access/lqp/constructors/__init__.py +18 -0
- v0/relationalai/early_access/lqp/executor/__init__.py +12 -0
- v0/relationalai/early_access/lqp/ir/__init__.py +12 -0
- v0/relationalai/early_access/lqp/passes/__init__.py +12 -0
- v0/relationalai/early_access/lqp/pragmas/__init__.py +12 -0
- v0/relationalai/early_access/lqp/primitives/__init__.py +12 -0
- v0/relationalai/early_access/lqp/types/__init__.py +12 -0
- v0/relationalai/early_access/lqp/utils/__init__.py +12 -0
- v0/relationalai/early_access/lqp/validators/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/__init__.py +58 -0
- v0/relationalai/early_access/metamodel/builtins/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/compiler/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/dependency/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/factory/__init__.py +17 -0
- v0/relationalai/early_access/metamodel/helpers/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/ir/__init__.py +14 -0
- v0/relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
- v0/relationalai/early_access/metamodel/typer/__init__.py +3 -0
- v0/relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/types/__init__.py +15 -0
- v0/relationalai/early_access/metamodel/util/__init__.py +15 -0
- v0/relationalai/early_access/metamodel/visitor/__init__.py +12 -0
- v0/relationalai/early_access/rel/__init__.py +12 -0
- v0/relationalai/early_access/rel/executor/__init__.py +12 -0
- v0/relationalai/early_access/rel/rel_utils/__init__.py +12 -0
- v0/relationalai/early_access/rel/rewrite/__init__.py +7 -0
- v0/relationalai/early_access/solvers/__init__.py +19 -0
- v0/relationalai/early_access/sql/__init__.py +11 -0
- v0/relationalai/early_access/sql/executor/__init__.py +3 -0
- v0/relationalai/early_access/sql/rewrite/__init__.py +3 -0
- v0/relationalai/early_access/tests/logging/__init__.py +12 -0
- v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
- v0/relationalai/early_access/tests/utils/__init__.py +12 -0
- v0/relationalai/environments/__init__.py +35 -0
- v0/relationalai/environments/base.py +381 -0
- v0/relationalai/environments/colab.py +14 -0
- v0/relationalai/environments/generic.py +71 -0
- v0/relationalai/environments/ipython.py +68 -0
- v0/relationalai/environments/jupyter.py +9 -0
- v0/relationalai/environments/snowbook.py +169 -0
- v0/relationalai/errors.py +2478 -0
- v0/relationalai/experimental/SF.py +38 -0
- v0/relationalai/experimental/inspect.py +47 -0
- v0/relationalai/experimental/pathfinder/__init__.py +158 -0
- v0/relationalai/experimental/pathfinder/api.py +160 -0
- v0/relationalai/experimental/pathfinder/automaton.py +584 -0
- v0/relationalai/experimental/pathfinder/bridge.py +226 -0
- v0/relationalai/experimental/pathfinder/compiler.py +416 -0
- v0/relationalai/experimental/pathfinder/datalog.py +214 -0
- v0/relationalai/experimental/pathfinder/diagnostics.py +56 -0
- v0/relationalai/experimental/pathfinder/filter.py +236 -0
- v0/relationalai/experimental/pathfinder/glushkov.py +439 -0
- v0/relationalai/experimental/pathfinder/options.py +265 -0
- v0/relationalai/experimental/pathfinder/rpq.py +344 -0
- v0/relationalai/experimental/pathfinder/transition.py +200 -0
- v0/relationalai/experimental/pathfinder/utils.py +26 -0
- v0/relationalai/experimental/paths/api.py +143 -0
- v0/relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
- v0/relationalai/experimental/paths/examples/basic_example.py +40 -0
- v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
- v0/relationalai/experimental/paths/examples/movie_example.py +77 -0
- v0/relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
- v0/relationalai/experimental/paths/examples/paths_example.py +116 -0
- v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
- v0/relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
- v0/relationalai/experimental/paths/graph.py +185 -0
- v0/relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
- v0/relationalai/experimental/paths/path_algorithms/single.py +59 -0
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
- v0/relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
- v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
- v0/relationalai/experimental/paths/path_algorithms/usp.py +150 -0
- v0/relationalai/experimental/paths/product_graph.py +93 -0
- v0/relationalai/experimental/paths/rpq/automaton.py +584 -0
- v0/relationalai/experimental/paths/rpq/diagnostics.py +56 -0
- v0/relationalai/experimental/paths/rpq/rpq.py +378 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
- v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
- v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
- v0/relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
- v0/relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
- v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
- v0/relationalai/experimental/paths/tree_agg.py +168 -0
- v0/relationalai/experimental/paths/utilities/iterators.py +27 -0
- v0/relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
- v0/relationalai/experimental/solvers.py +1087 -0
- v0/relationalai/loaders/csv.py +195 -0
- v0/relationalai/loaders/loader.py +177 -0
- v0/relationalai/loaders/types.py +23 -0
- v0/relationalai/rel_emitter.py +373 -0
- v0/relationalai/rel_utils.py +185 -0
- v0/relationalai/semantics/__init__.py +29 -0
- v0/relationalai/semantics/devtools/benchmark_lqp.py +536 -0
- v0/relationalai/semantics/devtools/compilation_manager.py +294 -0
- v0/relationalai/semantics/devtools/extract_lqp.py +110 -0
- v0/relationalai/semantics/internal/internal.py +3785 -0
- v0/relationalai/semantics/internal/snowflake.py +325 -0
- v0/relationalai/semantics/lqp/builtins.py +16 -0
- v0/relationalai/semantics/lqp/compiler.py +22 -0
- v0/relationalai/semantics/lqp/constructors.py +68 -0
- v0/relationalai/semantics/lqp/executor.py +474 -0
- v0/relationalai/semantics/lqp/intrinsics.py +24 -0
- v0/relationalai/semantics/lqp/ir.py +124 -0
- v0/relationalai/semantics/lqp/model2lqp.py +877 -0
- v0/relationalai/semantics/lqp/passes.py +680 -0
- v0/relationalai/semantics/lqp/primitives.py +252 -0
- v0/relationalai/semantics/lqp/result_helpers.py +202 -0
- v0/relationalai/semantics/lqp/rewrite/__init__.py +18 -0
- v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
- v0/relationalai/semantics/lqp/rewrite/cdc.py +216 -0
- v0/relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +490 -0
- v0/relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
- v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
- v0/relationalai/semantics/lqp/rewrite/splinter.py +76 -0
- v0/relationalai/semantics/lqp/types.py +101 -0
- v0/relationalai/semantics/lqp/utils.py +160 -0
- v0/relationalai/semantics/lqp/validators.py +57 -0
- v0/relationalai/semantics/metamodel/__init__.py +40 -0
- v0/relationalai/semantics/metamodel/builtins.py +776 -0
- v0/relationalai/semantics/metamodel/compiler.py +133 -0
- v0/relationalai/semantics/metamodel/dependency.py +862 -0
- v0/relationalai/semantics/metamodel/executor.py +61 -0
- v0/relationalai/semantics/metamodel/factory.py +287 -0
- v0/relationalai/semantics/metamodel/helpers.py +361 -0
- v0/relationalai/semantics/metamodel/ir.py +923 -0
- v0/relationalai/semantics/metamodel/rewrite/__init__.py +7 -0
- v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
- v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +554 -0
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
- v0/relationalai/semantics/metamodel/typer/checker.py +353 -0
- v0/relationalai/semantics/metamodel/typer/typer.py +1395 -0
- v0/relationalai/semantics/metamodel/util.py +505 -0
- v0/relationalai/semantics/metamodel/visitor.py +944 -0
- v0/relationalai/semantics/reasoners/__init__.py +10 -0
- v0/relationalai/semantics/reasoners/graph/__init__.py +37 -0
- v0/relationalai/semantics/reasoners/graph/core.py +9019 -0
- v0/relationalai/semantics/reasoners/optimization/__init__.py +68 -0
- v0/relationalai/semantics/reasoners/optimization/common.py +88 -0
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +1163 -0
- v0/relationalai/semantics/rel/builtins.py +40 -0
- v0/relationalai/semantics/rel/compiler.py +989 -0
- v0/relationalai/semantics/rel/executor.py +359 -0
- v0/relationalai/semantics/rel/rel.py +482 -0
- v0/relationalai/semantics/rel/rel_utils.py +276 -0
- v0/relationalai/semantics/snowflake/__init__.py +3 -0
- v0/relationalai/semantics/sql/compiler.py +2503 -0
- v0/relationalai/semantics/sql/executor/duck_db.py +52 -0
- v0/relationalai/semantics/sql/executor/result_helpers.py +64 -0
- v0/relationalai/semantics/sql/executor/snowflake.py +145 -0
- v0/relationalai/semantics/sql/rewrite/denormalize.py +222 -0
- v0/relationalai/semantics/sql/rewrite/double_negation.py +49 -0
- v0/relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
- v0/relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
- v0/relationalai/semantics/sql/sql.py +504 -0
- v0/relationalai/semantics/std/__init__.py +54 -0
- v0/relationalai/semantics/std/constraints.py +43 -0
- v0/relationalai/semantics/std/datetime.py +363 -0
- v0/relationalai/semantics/std/decimals.py +62 -0
- v0/relationalai/semantics/std/floats.py +7 -0
- v0/relationalai/semantics/std/integers.py +22 -0
- v0/relationalai/semantics/std/math.py +141 -0
- v0/relationalai/semantics/std/pragmas.py +11 -0
- v0/relationalai/semantics/std/re.py +83 -0
- v0/relationalai/semantics/std/std.py +14 -0
- v0/relationalai/semantics/std/strings.py +63 -0
- v0/relationalai/semantics/tests/__init__.py +0 -0
- v0/relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
- v0/relationalai/semantics/tests/test_snapshot_base.py +9 -0
- v0/relationalai/semantics/tests/utils.py +46 -0
- v0/relationalai/std/__init__.py +70 -0
- v0/relationalai/tools/__init__.py +0 -0
- v0/relationalai/tools/cli.py +1940 -0
- v0/relationalai/tools/cli_controls.py +1826 -0
- v0/relationalai/tools/cli_helpers.py +390 -0
- v0/relationalai/tools/debugger.py +183 -0
- v0/relationalai/tools/debugger_client.py +109 -0
- v0/relationalai/tools/debugger_server.py +302 -0
- v0/relationalai/tools/dev.py +685 -0
- v0/relationalai/tools/qb_debugger.py +425 -0
- v0/relationalai/util/clean_up_databases.py +95 -0
- v0/relationalai/util/format.py +123 -0
- v0/relationalai/util/list_databases.py +9 -0
- v0/relationalai/util/otel_configuration.py +25 -0
- v0/relationalai/util/otel_handler.py +484 -0
- v0/relationalai/util/snowflake_handler.py +88 -0
- v0/relationalai/util/span_format_test.py +43 -0
- v0/relationalai/util/span_tracker.py +207 -0
- v0/relationalai/util/spans_file_handler.py +72 -0
- v0/relationalai/util/tracing_handler.py +34 -0
- frontend/debugger/dist/.gitignore +0 -2
- frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
- frontend/debugger/dist/assets/index-Cssla-O7.js +0 -208
- frontend/debugger/dist/assets/index-DlHsYx1V.css +0 -9
- frontend/debugger/dist/index.html +0 -17
- relationalai/clients/__init__.py +0 -18
- relationalai/clients/client.py +0 -946
- relationalai/clients/config.py +0 -673
- relationalai/clients/direct_access_client.py +0 -118
- relationalai/clients/exec_txn_poller.py +0 -153
- relationalai/clients/hash_util.py +0 -31
- relationalai/clients/local.py +0 -594
- relationalai/clients/profile_polling.py +0 -73
- relationalai/clients/resources/__init__.py +0 -8
- relationalai/clients/resources/azure/azure.py +0 -502
- relationalai/clients/resources/snowflake/__init__.py +0 -20
- relationalai/clients/resources/snowflake/cli_resources.py +0 -98
- relationalai/clients/resources/snowflake/direct_access_resources.py +0 -739
- relationalai/clients/resources/snowflake/engine_service.py +0 -381
- relationalai/clients/resources/snowflake/engine_state_handlers.py +0 -315
- relationalai/clients/resources/snowflake/error_handlers.py +0 -240
- relationalai/clients/resources/snowflake/export_procedure.py.jinja +0 -249
- relationalai/clients/resources/snowflake/resources_factory.py +0 -99
- relationalai/clients/resources/snowflake/snowflake.py +0 -3193
- relationalai/clients/resources/snowflake/use_index_poller.py +0 -1019
- relationalai/clients/resources/snowflake/use_index_resources.py +0 -188
- relationalai/clients/resources/snowflake/util.py +0 -387
- relationalai/clients/result_helpers.py +0 -420
- relationalai/clients/types.py +0 -118
- relationalai/clients/util.py +0 -356
- relationalai/debugging.py +0 -389
- relationalai/dsl.py +0 -1749
- relationalai/early_access/builder/__init__.py +0 -30
- relationalai/early_access/builder/builder/__init__.py +0 -35
- relationalai/early_access/builder/snowflake/__init__.py +0 -12
- relationalai/early_access/builder/std/__init__.py +0 -25
- relationalai/early_access/builder/std/decimals/__init__.py +0 -12
- relationalai/early_access/builder/std/integers/__init__.py +0 -12
- relationalai/early_access/builder/std/math/__init__.py +0 -12
- relationalai/early_access/builder/std/strings/__init__.py +0 -14
- relationalai/early_access/devtools/__init__.py +0 -12
- relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
- relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
- relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
- relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
- relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
- relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
- relationalai/early_access/dsl/bindings/common.py +0 -402
- relationalai/early_access/dsl/bindings/csv.py +0 -170
- relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
- relationalai/early_access/dsl/bindings/snowflake.py +0 -64
- relationalai/early_access/dsl/codegen/binder.py +0 -411
- relationalai/early_access/dsl/codegen/common.py +0 -79
- relationalai/early_access/dsl/codegen/helpers.py +0 -23
- relationalai/early_access/dsl/codegen/relations.py +0 -700
- relationalai/early_access/dsl/codegen/weaver.py +0 -417
- relationalai/early_access/dsl/core/builders/__init__.py +0 -47
- relationalai/early_access/dsl/core/builders/logic.py +0 -19
- relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
- relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
- relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
- relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
- relationalai/early_access/dsl/core/context.py +0 -13
- relationalai/early_access/dsl/core/cset.py +0 -132
- relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
- relationalai/early_access/dsl/core/exprs/relational.py +0 -18
- relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
- relationalai/early_access/dsl/core/instances.py +0 -44
- relationalai/early_access/dsl/core/logic/__init__.py +0 -193
- relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
- relationalai/early_access/dsl/core/logic/exists.py +0 -223
- relationalai/early_access/dsl/core/logic/helper.py +0 -163
- relationalai/early_access/dsl/core/namespaces.py +0 -32
- relationalai/early_access/dsl/core/relations.py +0 -276
- relationalai/early_access/dsl/core/rules.py +0 -112
- relationalai/early_access/dsl/core/std/__init__.py +0 -45
- relationalai/early_access/dsl/core/temporal/recall.py +0 -6
- relationalai/early_access/dsl/core/types/__init__.py +0 -270
- relationalai/early_access/dsl/core/types/concepts.py +0 -128
- relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
- relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
- relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
- relationalai/early_access/dsl/core/types/standard.py +0 -92
- relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
- relationalai/early_access/dsl/core/types/variables.py +0 -203
- relationalai/early_access/dsl/ir/compiler.py +0 -318
- relationalai/early_access/dsl/ir/executor.py +0 -260
- relationalai/early_access/dsl/ontologies/constraints.py +0 -88
- relationalai/early_access/dsl/ontologies/export.py +0 -30
- relationalai/early_access/dsl/ontologies/models.py +0 -453
- relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
- relationalai/early_access/dsl/ontologies/readings.py +0 -60
- relationalai/early_access/dsl/ontologies/relationships.py +0 -322
- relationalai/early_access/dsl/ontologies/roles.py +0 -87
- relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
- relationalai/early_access/dsl/orm/constraints.py +0 -438
- relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
- relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
- relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
- relationalai/early_access/dsl/orm/measures/measures.py +0 -299
- relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
- relationalai/early_access/dsl/orm/models.py +0 -256
- relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
- relationalai/early_access/dsl/orm/printer.py +0 -469
- relationalai/early_access/dsl/orm/reasoners.py +0 -480
- relationalai/early_access/dsl/orm/relations.py +0 -19
- relationalai/early_access/dsl/orm/relationships.py +0 -251
- relationalai/early_access/dsl/orm/types.py +0 -42
- relationalai/early_access/dsl/orm/utils.py +0 -79
- relationalai/early_access/dsl/orm/verb.py +0 -204
- relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
- relationalai/early_access/dsl/relations.py +0 -170
- relationalai/early_access/dsl/rulesets.py +0 -69
- relationalai/early_access/dsl/schemas/__init__.py +0 -450
- relationalai/early_access/dsl/schemas/builder.py +0 -48
- relationalai/early_access/dsl/schemas/comp_names.py +0 -51
- relationalai/early_access/dsl/schemas/components.py +0 -203
- relationalai/early_access/dsl/schemas/contexts.py +0 -156
- relationalai/early_access/dsl/schemas/exprs.py +0 -89
- relationalai/early_access/dsl/schemas/fragments.py +0 -464
- relationalai/early_access/dsl/serialization.py +0 -79
- relationalai/early_access/dsl/serialize/exporter.py +0 -163
- relationalai/early_access/dsl/snow/api.py +0 -105
- relationalai/early_access/dsl/snow/common.py +0 -76
- relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
- relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
- relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
- relationalai/early_access/dsl/types/__init__.py +0 -40
- relationalai/early_access/dsl/types/concepts.py +0 -12
- relationalai/early_access/dsl/types/entities.py +0 -135
- relationalai/early_access/dsl/types/values.py +0 -17
- relationalai/early_access/dsl/utils.py +0 -102
- relationalai/early_access/graphs/__init__.py +0 -13
- relationalai/early_access/lqp/__init__.py +0 -12
- relationalai/early_access/lqp/compiler/__init__.py +0 -12
- relationalai/early_access/lqp/constructors/__init__.py +0 -18
- relationalai/early_access/lqp/executor/__init__.py +0 -12
- relationalai/early_access/lqp/ir/__init__.py +0 -12
- relationalai/early_access/lqp/passes/__init__.py +0 -12
- relationalai/early_access/lqp/pragmas/__init__.py +0 -12
- relationalai/early_access/lqp/primitives/__init__.py +0 -12
- relationalai/early_access/lqp/types/__init__.py +0 -12
- relationalai/early_access/lqp/utils/__init__.py +0 -12
- relationalai/early_access/lqp/validators/__init__.py +0 -12
- relationalai/early_access/metamodel/__init__.py +0 -58
- relationalai/early_access/metamodel/builtins/__init__.py +0 -12
- relationalai/early_access/metamodel/compiler/__init__.py +0 -12
- relationalai/early_access/metamodel/dependency/__init__.py +0 -12
- relationalai/early_access/metamodel/factory/__init__.py +0 -17
- relationalai/early_access/metamodel/helpers/__init__.py +0 -12
- relationalai/early_access/metamodel/ir/__init__.py +0 -14
- relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
- relationalai/early_access/metamodel/typer/__init__.py +0 -3
- relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
- relationalai/early_access/metamodel/types/__init__.py +0 -15
- relationalai/early_access/metamodel/util/__init__.py +0 -15
- relationalai/early_access/metamodel/visitor/__init__.py +0 -12
- relationalai/early_access/rel/__init__.py +0 -12
- relationalai/early_access/rel/executor/__init__.py +0 -12
- relationalai/early_access/rel/rel_utils/__init__.py +0 -12
- relationalai/early_access/rel/rewrite/__init__.py +0 -7
- relationalai/early_access/solvers/__init__.py +0 -19
- relationalai/early_access/sql/__init__.py +0 -11
- relationalai/early_access/sql/executor/__init__.py +0 -3
- relationalai/early_access/sql/rewrite/__init__.py +0 -3
- relationalai/early_access/tests/logging/__init__.py +0 -12
- relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
- relationalai/early_access/tests/utils/__init__.py +0 -12
- relationalai/environments/__init__.py +0 -35
- relationalai/environments/base.py +0 -381
- relationalai/environments/colab.py +0 -14
- relationalai/environments/generic.py +0 -71
- relationalai/environments/ipython.py +0 -68
- relationalai/environments/jupyter.py +0 -9
- relationalai/environments/snowbook.py +0 -169
- relationalai/errors.py +0 -2496
- relationalai/experimental/SF.py +0 -38
- relationalai/experimental/inspect.py +0 -47
- relationalai/experimental/pathfinder/__init__.py +0 -158
- relationalai/experimental/pathfinder/api.py +0 -160
- relationalai/experimental/pathfinder/automaton.py +0 -584
- relationalai/experimental/pathfinder/bridge.py +0 -226
- relationalai/experimental/pathfinder/compiler.py +0 -416
- relationalai/experimental/pathfinder/datalog.py +0 -214
- relationalai/experimental/pathfinder/diagnostics.py +0 -56
- relationalai/experimental/pathfinder/filter.py +0 -236
- relationalai/experimental/pathfinder/glushkov.py +0 -439
- relationalai/experimental/pathfinder/options.py +0 -265
- relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +0 -1951
- relationalai/experimental/pathfinder/rpq.py +0 -344
- relationalai/experimental/pathfinder/transition.py +0 -200
- relationalai/experimental/pathfinder/utils.py +0 -26
- relationalai/experimental/paths/README.md +0 -107
- relationalai/experimental/paths/api.py +0 -143
- relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
- relationalai/experimental/paths/code_organization.md +0 -2
- relationalai/experimental/paths/examples/Movies.ipynb +0 -16328
- relationalai/experimental/paths/examples/basic_example.py +0 -40
- relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
- relationalai/experimental/paths/examples/movie_example.py +0 -77
- relationalai/experimental/paths/examples/movies_data/actedin.csv +0 -193
- relationalai/experimental/paths/examples/movies_data/directed.csv +0 -45
- relationalai/experimental/paths/examples/movies_data/follows.csv +0 -7
- relationalai/experimental/paths/examples/movies_data/movies.csv +0 -39
- relationalai/experimental/paths/examples/movies_data/person.csv +0 -134
- relationalai/experimental/paths/examples/movies_data/produced.csv +0 -16
- relationalai/experimental/paths/examples/movies_data/ratings.csv +0 -10
- relationalai/experimental/paths/examples/movies_data/wrote.csv +0 -11
- relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
- relationalai/experimental/paths/examples/paths_example.py +0 -116
- relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
- relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
- relationalai/experimental/paths/graph.py +0 -185
- relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
- relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
- relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
- relationalai/experimental/paths/path_algorithms/single.py +0 -59
- relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
- relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
- relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
- relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
- relationalai/experimental/paths/path_algorithms/usp.py +0 -150
- relationalai/experimental/paths/product_graph.py +0 -93
- relationalai/experimental/paths/rpq/automaton.py +0 -584
- relationalai/experimental/paths/rpq/diagnostics.py +0 -56
- relationalai/experimental/paths/rpq/rpq.py +0 -378
- relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
- relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
- relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
- relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
- relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
- relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
- relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
- relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
- relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
- relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
- relationalai/experimental/paths/tree_agg.py +0 -168
- relationalai/experimental/paths/utilities/iterators.py +0 -27
- relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
- relationalai/experimental/solvers.py +0 -1095
- relationalai/loaders/csv.py +0 -195
- relationalai/loaders/loader.py +0 -177
- relationalai/loaders/types.py +0 -23
- relationalai/rel_emitter.py +0 -373
- relationalai/rel_utils.py +0 -185
- relationalai/semantics/designs/query_builder/identify_by.md +0 -106
- relationalai/semantics/devtools/benchmark_lqp.py +0 -535
- relationalai/semantics/devtools/compilation_manager.py +0 -294
- relationalai/semantics/devtools/extract_lqp.py +0 -110
- relationalai/semantics/internal/internal.py +0 -3785
- relationalai/semantics/internal/snowflake.py +0 -329
- relationalai/semantics/lqp/README.md +0 -34
- relationalai/semantics/lqp/algorithms.py +0 -173
- relationalai/semantics/lqp/builtins.py +0 -213
- relationalai/semantics/lqp/compiler.py +0 -22
- relationalai/semantics/lqp/constructors.py +0 -68
- relationalai/semantics/lqp/executor.py +0 -518
- relationalai/semantics/lqp/export_rewriter.py +0 -40
- relationalai/semantics/lqp/intrinsics.py +0 -24
- relationalai/semantics/lqp/ir.py +0 -150
- relationalai/semantics/lqp/model2lqp.py +0 -1056
- relationalai/semantics/lqp/passes.py +0 -38
- relationalai/semantics/lqp/primitives.py +0 -252
- relationalai/semantics/lqp/result_helpers.py +0 -266
- relationalai/semantics/lqp/rewrite/__init__.py +0 -32
- relationalai/semantics/lqp/rewrite/algorithm.py +0 -385
- relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -69
- relationalai/semantics/lqp/rewrite/cdc.py +0 -216
- relationalai/semantics/lqp/rewrite/constants_to_vars.py +0 -70
- relationalai/semantics/lqp/rewrite/deduplicate_vars.py +0 -104
- relationalai/semantics/lqp/rewrite/eliminate_data.py +0 -108
- relationalai/semantics/lqp/rewrite/extract_common.py +0 -340
- relationalai/semantics/lqp/rewrite/extract_keys.py +0 -577
- relationalai/semantics/lqp/rewrite/flatten_script.py +0 -301
- relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
- relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -348
- relationalai/semantics/lqp/rewrite/period_math.py +0 -77
- relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -339
- relationalai/semantics/lqp/rewrite/splinter.py +0 -76
- relationalai/semantics/lqp/rewrite/unify_definitions.py +0 -323
- relationalai/semantics/lqp/types.py +0 -101
- relationalai/semantics/lqp/utils.py +0 -170
- relationalai/semantics/lqp/validators.py +0 -70
- relationalai/semantics/metamodel/compiler.py +0 -134
- relationalai/semantics/metamodel/dependency.py +0 -880
- relationalai/semantics/metamodel/executor.py +0 -78
- relationalai/semantics/metamodel/factory.py +0 -287
- relationalai/semantics/metamodel/helpers.py +0 -368
- relationalai/semantics/metamodel/ir.py +0 -924
- relationalai/semantics/metamodel/rewrite/__init__.py +0 -8
- relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
- relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -220
- relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
- relationalai/semantics/metamodel/rewrite/flatten.py +0 -590
- relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -256
- relationalai/semantics/metamodel/rewrite/handle_aggregations_and_ranks.py +0 -237
- relationalai/semantics/metamodel/typer/checker.py +0 -355
- relationalai/semantics/metamodel/typer/typer.py +0 -1396
- relationalai/semantics/metamodel/util.py +0 -506
- relationalai/semantics/metamodel/visitor.py +0 -945
- relationalai/semantics/reasoners/__init__.py +0 -10
- relationalai/semantics/reasoners/graph/README.md +0 -620
- relationalai/semantics/reasoners/graph/__init__.py +0 -37
- relationalai/semantics/reasoners/graph/core.py +0 -9019
- relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +0 -797
- relationalai/semantics/reasoners/graph/tests/README.md +0 -21
- relationalai/semantics/reasoners/optimization/__init__.py +0 -68
- relationalai/semantics/reasoners/optimization/common.py +0 -88
- relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
- relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1407
- relationalai/semantics/rel/builtins.py +0 -40
- relationalai/semantics/rel/compiler.py +0 -994
- relationalai/semantics/rel/executor.py +0 -363
- relationalai/semantics/rel/rel.py +0 -482
- relationalai/semantics/rel/rel_utils.py +0 -276
- relationalai/semantics/snowflake/__init__.py +0 -3
- relationalai/semantics/sql/compiler.py +0 -2503
- relationalai/semantics/sql/executor/duck_db.py +0 -52
- relationalai/semantics/sql/executor/result_helpers.py +0 -64
- relationalai/semantics/sql/executor/snowflake.py +0 -149
- relationalai/semantics/sql/rewrite/denormalize.py +0 -222
- relationalai/semantics/sql/rewrite/double_negation.py +0 -49
- relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
- relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
- relationalai/semantics/sql/sql.py +0 -504
- relationalai/semantics/std/pragmas.py +0 -11
- relationalai/semantics/std/std.py +0 -14
- relationalai/semantics/tests/lqp/algorithms.py +0 -345
- relationalai/semantics/tests/test_snapshot_abstract.py +0 -144
- relationalai/semantics/tests/test_snapshot_base.py +0 -9
- relationalai/semantics/tests/utils.py +0 -46
- relationalai/std/__init__.py +0 -70
- relationalai/tools/cli.py +0 -2089
- relationalai/tools/cli_controls.py +0 -1975
- relationalai/tools/cli_helpers.py +0 -802
- relationalai/tools/debugger_client.py +0 -109
- relationalai/tools/debugger_server.py +0 -302
- relationalai/tools/dev.py +0 -685
- relationalai/tools/notes +0 -7
- relationalai/tools/qb_debugger.py +0 -425
- relationalai/tools/txn_progress.py +0 -188
- relationalai/util/clean_up_databases.py +0 -95
- relationalai/util/list_databases.py +0 -9
- relationalai/util/otel_configuration.py +0 -26
- relationalai/util/otel_handler.py +0 -484
- relationalai/util/snowflake_handler.py +0 -88
- relationalai/util/span_format_test.py +0 -43
- relationalai/util/span_tracker.py +0 -207
- relationalai/util/spans_file_handler.py +0 -72
- relationalai/util/tracing_handler.py +0 -34
- relationalai-0.13.5.dist-info/METADATA +0 -74
- relationalai-0.13.5.dist-info/RECORD +0 -473
- relationalai-0.13.5.dist-info/WHEEL +0 -4
- relationalai-0.13.5.dist-info/entry_points.txt +0 -3
- relationalai-0.13.5.dist-info/licenses/LICENSE +0 -202
- relationalai_test_util/__init__.py +0 -4
- relationalai_test_util/fixtures.py +0 -233
- relationalai_test_util/snapshot.py +0 -252
- relationalai_test_util/traceback.py +0 -118
- /relationalai/{analysis → semantics/frontend}/__init__.py +0 -0
- /relationalai/{auth/__init__.py → semantics/metamodel/metamodel_compiler.py} +0 -0
- /relationalai/{early_access → shims}/__init__.py +0 -0
- {relationalai/early_access/dsl/adapters → v0/relationalai/analysis}/__init__.py +0 -0
- {relationalai → v0/relationalai}/analysis/mechanistic.py +0 -0
- {relationalai → v0/relationalai}/analysis/whynot.py +0 -0
- {relationalai/early_access/dsl/adapters/orm → v0/relationalai/auth}/__init__.py +0 -0
- {relationalai → v0/relationalai}/auth/jwt_generator.py +0 -0
- {relationalai → v0/relationalai}/auth/oauth_callback_server.py +0 -0
- {relationalai → v0/relationalai}/auth/token_handler.py +0 -0
- {relationalai → v0/relationalai}/auth/util.py +0 -0
- {relationalai/clients/resources/snowflake → v0/relationalai/clients}/cache_store.py +0 -0
- {relationalai → v0/relationalai}/compiler.py +0 -0
- {relationalai → v0/relationalai}/dependencies.py +0 -0
- {relationalai → v0/relationalai}/docutils.py +0 -0
- {relationalai/early_access/dsl/adapters/owl → v0/relationalai/early_access}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/__init__.py +0 -0
- {relationalai/early_access/dsl/bindings → v0/relationalai/early_access/dsl/adapters}/__init__.py +0 -0
- {relationalai/early_access/dsl/bindings/legacy → v0/relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
- {relationalai/early_access/dsl/codegen → v0/relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
- {relationalai/early_access/dsl/core/temporal → v0/relationalai/early_access/dsl/bindings}/__init__.py +0 -0
- {relationalai/early_access/dsl/ir → v0/relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
- {relationalai/early_access/dsl/ontologies → v0/relationalai/early_access/dsl/codegen}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/constants.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/stack.py +0 -0
- {relationalai/early_access/dsl/orm → v0/relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/utils.py +0 -0
- {relationalai/early_access/dsl/orm/measures → v0/relationalai/early_access/dsl/ir}/__init__.py +0 -0
- {relationalai/early_access/dsl/physical_metadata → v0/relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
- {relationalai/early_access/dsl/serialize → v0/relationalai/early_access/dsl/orm}/__init__.py +0 -0
- {relationalai/early_access/dsl/snow → v0/relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
- {relationalai/loaders → v0/relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
- {relationalai/semantics/tests → v0/relationalai/early_access/dsl/serialize}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/serialize/model.py +0 -0
- {relationalai/semantics/tests/lqp → v0/relationalai/early_access/dsl/snow}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/tests/__init__.py +0 -0
- {relationalai → v0/relationalai}/environments/ci.py +0 -0
- {relationalai → v0/relationalai}/environments/hex.py +0 -0
- {relationalai → v0/relationalai}/environments/terminal.py +0 -0
- {relationalai → v0/relationalai}/experimental/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/graphs.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/filter.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/glushkov.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/transition.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/utilities/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/utilities/utilities.py +0 -0
- {relationalai/tools → v0/relationalai/loaders}/__init__.py +0 -0
- {relationalai → v0/relationalai}/metagen.py +0 -0
- {relationalai → v0/relationalai}/metamodel.py +0 -0
- {relationalai → v0/relationalai}/rel.py +0 -0
- {relationalai → v0/relationalai}/semantics/devtools/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/internal/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/internal/annotations.py +0 -0
- {relationalai → v0/relationalai}/semantics/lqp/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/lqp/pragmas.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/dataflow.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/typer/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/types.py +0 -0
- {relationalai → v0/relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/rel/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/executor/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/rewrite/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/tests/logging.py +0 -0
- {relationalai → v0/relationalai}/std/aggregates.py +0 -0
- {relationalai → v0/relationalai}/std/dates.py +0 -0
- {relationalai → v0/relationalai}/std/graphs.py +0 -0
- {relationalai → v0/relationalai}/std/inspect.py +0 -0
- {relationalai → v0/relationalai}/std/math.py +0 -0
- {relationalai → v0/relationalai}/std/re.py +0 -0
- {relationalai → v0/relationalai}/std/strings.py +0 -0
- {relationalai → v0/relationalai}/tools/cleanup_snapshots.py +0 -0
- {relationalai → v0/relationalai}/tools/constants.py +0 -0
- {relationalai → v0/relationalai}/tools/query_utils.py +0 -0
- {relationalai → v0/relationalai}/tools/snapshot_viewer.py +0 -0
- {relationalai → v0/relationalai}/util/__init__.py +0 -0
- {relationalai → v0/relationalai}/util/constants.py +0 -0
- {relationalai → v0/relationalai}/util/graph.py +0 -0
- {relationalai → v0/relationalai}/util/timeout.py +0 -0
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Iterable, Dict, Optional, List, cast, TYPE_CHECKING
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
8
|
+
from v0.relationalai import debugging
|
|
9
|
+
from v0.relationalai.clients.cache_store import GraphIndexCache
|
|
10
|
+
from v0.relationalai.clients.util import (
|
|
11
|
+
get_pyrel_version,
|
|
12
|
+
normalize_datetime,
|
|
13
|
+
poll_with_specified_overhead,
|
|
14
|
+
)
|
|
15
|
+
from v0.relationalai.errors import (
|
|
16
|
+
ERPNotRunningError,
|
|
17
|
+
EngineProvisioningFailed,
|
|
18
|
+
SnowflakeChangeTrackingNotEnabledException,
|
|
19
|
+
SnowflakeTableObjectsException,
|
|
20
|
+
SnowflakeTableObject,
|
|
21
|
+
)
|
|
22
|
+
from v0.relationalai.tools.cli_controls import (
|
|
23
|
+
DebuggingSpan,
|
|
24
|
+
create_progress,
|
|
25
|
+
TASK_CATEGORY_INDEXING,
|
|
26
|
+
TASK_CATEGORY_PROVISIONING,
|
|
27
|
+
TASK_CATEGORY_CHANGE_TRACKING,
|
|
28
|
+
TASK_CATEGORY_CACHE,
|
|
29
|
+
TASK_CATEGORY_RELATIONS,
|
|
30
|
+
TASK_CATEGORY_STATUS,
|
|
31
|
+
TASK_CATEGORY_VALIDATION,
|
|
32
|
+
)
|
|
33
|
+
from v0.relationalai.tools.constants import WAIT_FOR_STREAM_SYNC, Generation
|
|
34
|
+
|
|
35
|
+
# Set up logger for this module
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from rich.console import Console
|
|
41
|
+
from rich.table import Table
|
|
42
|
+
except ImportError:
|
|
43
|
+
Console = None
|
|
44
|
+
Table = None
|
|
45
|
+
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from v0.relationalai.clients.snowflake import Resources
|
|
48
|
+
from v0.relationalai.clients.snowflake import DirectAccessResources
|
|
49
|
+
|
|
50
|
+
# Maximum number of items to show individual subtasks for
|
|
51
|
+
# If more items than this, show a single summary subtask instead
|
|
52
|
+
MAX_INDIVIDUAL_SUBTASKS = 5
|
|
53
|
+
|
|
54
|
+
# Special engine name for CDC managed engine
|
|
55
|
+
CDC_MANAGED_ENGINE = "CDC_MANAGED_ENGINE"
|
|
56
|
+
|
|
57
|
+
# Maximum number of data source subtasks to show simultaneously
|
|
58
|
+
# When one completes, the next one from the queue will be added
|
|
59
|
+
MAX_DATA_SOURCE_SUBTASKS = 10
|
|
60
|
+
|
|
61
|
+
# How often to check ERP status (every N iterations)
|
|
62
|
+
# To limit performance overhead, we only check ERP status periodically
|
|
63
|
+
ERP_CHECK_FREQUENCY = 15
|
|
64
|
+
|
|
65
|
+
# Polling behavior constants
|
|
66
|
+
POLL_OVERHEAD_RATE = 0.1 # Overhead rate for exponential backoff
|
|
67
|
+
POLL_MAX_DELAY = 2.5 # Maximum delay between polls in seconds
|
|
68
|
+
|
|
69
|
+
# SQL query template for getting stream column hashes
|
|
70
|
+
# This query calculates a hash of column metadata (name, type, precision, scale, nullable)
|
|
71
|
+
# to detect if source table schema has changed since stream was created
|
|
72
|
+
STREAM_COLUMN_HASH_QUERY = """
|
|
73
|
+
WITH stream_columns AS (
|
|
74
|
+
SELECT
|
|
75
|
+
fq_object_name,
|
|
76
|
+
HASH(
|
|
77
|
+
value:name::VARCHAR,
|
|
78
|
+
CASE
|
|
79
|
+
WHEN value:precision IS NOT NULL AND value:scale IS NOT NULL THEN CASE value:type::VARCHAR
|
|
80
|
+
WHEN 'FIXED' THEN 'NUMBER'
|
|
81
|
+
WHEN 'REAL' THEN 'FLOAT'
|
|
82
|
+
WHEN 'TEXT' THEN 'TEXT'
|
|
83
|
+
ELSE value:type::VARCHAR
|
|
84
|
+
END || '(' || value:precision || ',' || value:scale || ')'
|
|
85
|
+
WHEN value:precision IS NOT NULL AND value:scale IS NULL THEN CASE value:type::VARCHAR
|
|
86
|
+
WHEN 'FIXED' THEN 'NUMBER'
|
|
87
|
+
WHEN 'REAL' THEN 'FLOAT'
|
|
88
|
+
WHEN 'TEXT' THEN 'TEXT'
|
|
89
|
+
ELSE value:type::VARCHAR
|
|
90
|
+
END || '(0,' || value:precision || ')'
|
|
91
|
+
WHEN value:length IS NOT NULL THEN CASE value:type::VARCHAR
|
|
92
|
+
WHEN 'FIXED' THEN 'NUMBER'
|
|
93
|
+
WHEN 'REAL' THEN 'FLOAT'
|
|
94
|
+
WHEN 'TEXT' THEN 'TEXT'
|
|
95
|
+
ELSE value:type::VARCHAR
|
|
96
|
+
END || '(' || value:length || ')'
|
|
97
|
+
ELSE CASE value:type::VARCHAR
|
|
98
|
+
WHEN 'FIXED' THEN 'NUMBER'
|
|
99
|
+
WHEN 'REAL' THEN 'FLOAT'
|
|
100
|
+
WHEN 'TEXT' THEN 'TEXT'
|
|
101
|
+
ELSE value:type::VARCHAR
|
|
102
|
+
END
|
|
103
|
+
END,
|
|
104
|
+
IFF(value:nullable::BOOLEAN, 'YES', 'NO')
|
|
105
|
+
) AS column_signature
|
|
106
|
+
FROM {app_name}.api.data_streams,
|
|
107
|
+
LATERAL FLATTEN(input => columns)
|
|
108
|
+
WHERE rai_database = '{rai_database}'
|
|
109
|
+
AND fq_object_name IN ({fqn_list})
|
|
110
|
+
)
|
|
111
|
+
SELECT
|
|
112
|
+
fq_object_name AS FQ_OBJECT_NAME,
|
|
113
|
+
HEX_ENCODE(HASH_AGG(column_signature)) AS STREAM_HASH
|
|
114
|
+
FROM stream_columns
|
|
115
|
+
GROUP BY fq_object_name;
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class UseIndexPoller:
|
|
120
|
+
"""
|
|
121
|
+
Encapsulates the polling logic for `use_index` streams.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def _add_stream_subtask(self, progress, fq_name: str, status: str, batches_count: int) -> bool:
|
|
125
|
+
"""Add a stream subtask if we haven't reached the limit.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if subtask was added, False if limit reached
|
|
129
|
+
"""
|
|
130
|
+
if fq_name not in self.stream_task_ids and len(self.stream_task_ids) < MAX_DATA_SOURCE_SUBTASKS:
|
|
131
|
+
# Get the position in the stream order (should already be there)
|
|
132
|
+
if fq_name in self.stream_order:
|
|
133
|
+
stream_position = self.stream_order.index(fq_name) + 1
|
|
134
|
+
else:
|
|
135
|
+
# Fallback if not in order (shouldn't happen)
|
|
136
|
+
stream_position = 1
|
|
137
|
+
|
|
138
|
+
# Build initial message based on status and batch count
|
|
139
|
+
if status == "synced":
|
|
140
|
+
initial_message = f"{fq_name} already synced"
|
|
141
|
+
elif batches_count > 0:
|
|
142
|
+
# Show stream position (x/y) before batch count
|
|
143
|
+
initial_message = f"Syncing {fq_name} ({stream_position}/{self.total_streams}), batches: {batches_count}"
|
|
144
|
+
else:
|
|
145
|
+
initial_message = f"Syncing {fq_name} ({stream_position}/{self.total_streams})"
|
|
146
|
+
|
|
147
|
+
self.stream_task_ids[fq_name] = progress.add_sub_task(initial_message, task_id=fq_name, category=TASK_CATEGORY_INDEXING)
|
|
148
|
+
|
|
149
|
+
# Complete immediately if already synced (without recording completion time)
|
|
150
|
+
if status == "synced":
|
|
151
|
+
progress.complete_sub_task(fq_name, record_time=False)
|
|
152
|
+
|
|
153
|
+
return True
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def __init__(
|
|
157
|
+
self,
|
|
158
|
+
resource: "Resources",
|
|
159
|
+
app_name: str,
|
|
160
|
+
sources: Iterable[str],
|
|
161
|
+
model: str,
|
|
162
|
+
engine_name: str,
|
|
163
|
+
engine_size: Optional[str],
|
|
164
|
+
language: str = "rel",
|
|
165
|
+
program_span_id: Optional[str] = None,
|
|
166
|
+
headers: Optional[Dict] = None,
|
|
167
|
+
generation: Optional[Generation] = None,
|
|
168
|
+
):
|
|
169
|
+
self.res = resource
|
|
170
|
+
self.app_name = app_name
|
|
171
|
+
self.sources = list(sources)
|
|
172
|
+
self.model = model
|
|
173
|
+
self.engine_name = engine_name
|
|
174
|
+
self.engine_size = engine_size or self.res.config.get_default_engine_size()
|
|
175
|
+
self.language = language
|
|
176
|
+
self.program_span_id = program_span_id
|
|
177
|
+
self.headers = headers or {}
|
|
178
|
+
self.counter = 1
|
|
179
|
+
self.check_ready_count = 0
|
|
180
|
+
self.tables_with_not_enabled_change_tracking: List = []
|
|
181
|
+
self.table_objects_with_other_errors: List = []
|
|
182
|
+
self.engine_errors: List = []
|
|
183
|
+
# Flag to only ensure the engine is created asynchronously the initial call
|
|
184
|
+
self.init_engine_async = True
|
|
185
|
+
# Initially, we assume that cdc is not checked,
|
|
186
|
+
# then on subsequent calls, if we get if cdc is enabled, if it is not, we will check it
|
|
187
|
+
# on every 5th iteration we reset the cdc status, so it will be checked again
|
|
188
|
+
self.should_check_cdc = True
|
|
189
|
+
|
|
190
|
+
self.wait_for_stream_sync = self.res.config.get(
|
|
191
|
+
"wait_for_stream_sync", WAIT_FOR_STREAM_SYNC
|
|
192
|
+
)
|
|
193
|
+
current_user = self.res.get_sf_session().get_current_user()
|
|
194
|
+
assert current_user is not None, "current_user must be set"
|
|
195
|
+
self.data_freshness = self.res.config.get_data_freshness_mins()
|
|
196
|
+
self.cache = GraphIndexCache(current_user, model, self.data_freshness, self.sources)
|
|
197
|
+
self.sources = self.cache.choose_sources()
|
|
198
|
+
# execution_id is allowed to group use_index call, which belongs to the same loop iteration
|
|
199
|
+
self.execution_id = str(uuid.uuid4())
|
|
200
|
+
|
|
201
|
+
self.pyrel_version = get_pyrel_version(generation)
|
|
202
|
+
|
|
203
|
+
self.source_info = self.res._check_source_updates(self.sources)
|
|
204
|
+
|
|
205
|
+
# Track subtask IDs for streams, engines, and relations across multiple poll iterations
|
|
206
|
+
self.stream_task_ids = {}
|
|
207
|
+
self.engine_task_ids = {}
|
|
208
|
+
self.relations_task_id = None
|
|
209
|
+
self._erp_check_task_id = None
|
|
210
|
+
|
|
211
|
+
# Track total number of streams and current stream position for (x/y) progress display
|
|
212
|
+
self.total_streams = 0
|
|
213
|
+
self.stream_position = 0
|
|
214
|
+
self.stream_order = [] # Track the order of streams as they appear in data
|
|
215
|
+
|
|
216
|
+
# Timing will be tracked by TaskProgress
|
|
217
|
+
|
|
218
|
+
def poll(self) -> None:
|
|
219
|
+
"""
|
|
220
|
+
Standard stream-based polling for use_index.
|
|
221
|
+
"""
|
|
222
|
+
# Read show_duration_summary config flag (defaults to True for backward compatibility)
|
|
223
|
+
show_duration_summary = bool(self.res.config.get("show_duration_summary", True))
|
|
224
|
+
|
|
225
|
+
with create_progress(
|
|
226
|
+
description="Initializing data index",
|
|
227
|
+
success_message="", # We'll handle this in the context manager
|
|
228
|
+
leading_newline=True,
|
|
229
|
+
trailing_newline=True,
|
|
230
|
+
show_duration_summary=show_duration_summary,
|
|
231
|
+
) as progress:
|
|
232
|
+
# Set process start time
|
|
233
|
+
progress.set_process_start_time()
|
|
234
|
+
progress.update_main_status("Validating data sources")
|
|
235
|
+
self._maybe_delete_stale(progress)
|
|
236
|
+
|
|
237
|
+
# Add cache usage subtask
|
|
238
|
+
self._add_cache_subtask(progress)
|
|
239
|
+
|
|
240
|
+
progress.update_main_status("Initializing data index")
|
|
241
|
+
self._poll_loop(progress)
|
|
242
|
+
self._post_check(progress)
|
|
243
|
+
|
|
244
|
+
# Set process end time (summary will be automatically printed by __exit__)
|
|
245
|
+
progress.set_process_end_time()
|
|
246
|
+
|
|
247
|
+
def _add_cache_subtask(self, progress) -> None:
|
|
248
|
+
"""Add a subtask showing cache usage information only when cache is used."""
|
|
249
|
+
if self.cache.using_cache:
|
|
250
|
+
# Cache was used - show how many sources were cached
|
|
251
|
+
total_sources = len(self.cache.sources)
|
|
252
|
+
cached_sources = total_sources - len(self.sources)
|
|
253
|
+
|
|
254
|
+
# Get the timestamp when sources were cached
|
|
255
|
+
entry = self.cache._metadata.get("cachedIndices", {}).get(self.cache.key, {})
|
|
256
|
+
cached_timestamp = entry.get("last_use_index_update_on", "")
|
|
257
|
+
|
|
258
|
+
message = f"Using cached data for {cached_sources}/{total_sources} data streams"
|
|
259
|
+
# Format the message with timestamp
|
|
260
|
+
if cached_timestamp:
|
|
261
|
+
message += f" (cached at {cached_timestamp})"
|
|
262
|
+
|
|
263
|
+
progress.add_sub_task(message, task_id="cache_usage", category=TASK_CATEGORY_CACHE)
|
|
264
|
+
# Complete the subtask immediately since it's just informational
|
|
265
|
+
progress.complete_sub_task("cache_usage")
|
|
266
|
+
|
|
267
|
+
def _get_stream_column_hashes(self, sources: List[str], progress) -> Dict[str, str]:
|
|
268
|
+
"""
|
|
269
|
+
Query data_streams to get current column hashes for the given sources.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
sources: List of source FQNs to query
|
|
273
|
+
progress: TaskProgress instance for updating status on error
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Dict mapping FQN -> column hash
|
|
277
|
+
|
|
278
|
+
Raises:
|
|
279
|
+
ValueError: If the query fails (permissions, table doesn't exist, etc.)
|
|
280
|
+
"""
|
|
281
|
+
from v0.relationalai.clients.snowflake import PYREL_ROOT_DB
|
|
282
|
+
|
|
283
|
+
# Build FQN list for SQL IN clause
|
|
284
|
+
fqn_list = ", ".join([f"'{source}'" for source in sources])
|
|
285
|
+
|
|
286
|
+
# Format query template with actual values
|
|
287
|
+
hash_query = STREAM_COLUMN_HASH_QUERY.format(
|
|
288
|
+
app_name=self.app_name,
|
|
289
|
+
rai_database=PYREL_ROOT_DB,
|
|
290
|
+
fqn_list=fqn_list
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
hash_results = self.res._exec(hash_query)
|
|
295
|
+
return {row["FQ_OBJECT_NAME"]: row["STREAM_HASH"] for row in hash_results}
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
logger.error(f"Failed to query stream column hashes: {e}")
|
|
299
|
+
logger.error(f" Query: {hash_query[:200]}...")
|
|
300
|
+
logger.error(f" Sources: {sources}")
|
|
301
|
+
progress.update_main_status("❌ Failed to validate data stream metadata")
|
|
302
|
+
raise ValueError(
|
|
303
|
+
f"Failed to validate stream column hashes. This may indicate a permissions "
|
|
304
|
+
f"issue or missing data_streams table. Error: {e}"
|
|
305
|
+
) from e
|
|
306
|
+
|
|
307
|
+
def _filter_truly_stale_sources(self, stale_sources: List[str], progress) -> List[str]:
|
|
308
|
+
"""
|
|
309
|
+
Filter stale sources to only include those with mismatched column hashes.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
stale_sources: List of source FQNs marked as stale
|
|
313
|
+
progress: TaskProgress instance for updating status on error
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
List of truly stale sources that need to be deleted/recreated
|
|
317
|
+
|
|
318
|
+
A source is truly stale if any of the following apply:
|
|
319
|
+
- The stream doesn't exist (needs to be created)
|
|
320
|
+
- The source table was recreated after the stream (table creation timestamp is newer)
|
|
321
|
+
- The column hashes don't match (schema drift needs cleanup)
|
|
322
|
+
"""
|
|
323
|
+
stream_hashes = self._get_stream_column_hashes(stale_sources, progress)
|
|
324
|
+
|
|
325
|
+
truly_stale = []
|
|
326
|
+
for source in stale_sources:
|
|
327
|
+
source_hash = self.source_info[source].get("columns_hash")
|
|
328
|
+
stream_hash = stream_hashes.get(source)
|
|
329
|
+
table_created_at_raw = self.source_info[source].get("table_created_at")
|
|
330
|
+
stream_created_at_raw = self.source_info[source].get("stream_created_at")
|
|
331
|
+
|
|
332
|
+
table_created_at = normalize_datetime(table_created_at_raw)
|
|
333
|
+
stream_created_at = normalize_datetime(stream_created_at_raw)
|
|
334
|
+
|
|
335
|
+
recreated_table = False
|
|
336
|
+
if table_created_at is not None and stream_created_at is not None:
|
|
337
|
+
# If the source table was recreated (new creation timestamp) but kept
|
|
338
|
+
# the same column definitions, we still need to recycle the stream so
|
|
339
|
+
# that Snowflake picks up the new table instance.
|
|
340
|
+
recreated_table = table_created_at > stream_created_at
|
|
341
|
+
|
|
342
|
+
# Log hash comparison for debugging
|
|
343
|
+
logger.debug(f"Source: {source}")
|
|
344
|
+
logger.debug(f" Source table hash: {source_hash}")
|
|
345
|
+
logger.debug(f" Stream hash: {stream_hash}")
|
|
346
|
+
logger.debug(f" Match: {source_hash == stream_hash}")
|
|
347
|
+
if recreated_table:
|
|
348
|
+
logger.debug(" Table appears to have been recreated (table_created_at > stream_created_at)")
|
|
349
|
+
logger.debug(f" table_created_at: {table_created_at}")
|
|
350
|
+
logger.debug(f" stream_created_at: {stream_created_at}")
|
|
351
|
+
|
|
352
|
+
if stream_hash is None or source_hash != stream_hash or recreated_table:
|
|
353
|
+
logger.debug(" Action: DELETE (stale)")
|
|
354
|
+
truly_stale.append(source)
|
|
355
|
+
else:
|
|
356
|
+
logger.debug(" Action: KEEP (valid)")
|
|
357
|
+
|
|
358
|
+
logger.debug(f"Stale sources summary: {len(truly_stale)}/{len(stale_sources)} truly stale")
|
|
359
|
+
|
|
360
|
+
return truly_stale
|
|
361
|
+
|
|
362
|
+
def _add_deletion_subtasks(self, progress, sources: List[str]) -> None:
|
|
363
|
+
"""Add progress subtasks for source deletion.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
progress: TaskProgress instance
|
|
367
|
+
sources: List of source FQNs to be deleted
|
|
368
|
+
"""
|
|
369
|
+
if len(sources) <= MAX_INDIVIDUAL_SUBTASKS:
|
|
370
|
+
for i, source in enumerate(sources):
|
|
371
|
+
progress.add_sub_task(
|
|
372
|
+
f"Removing stale stream {source} ({i+1}/{len(sources)})",
|
|
373
|
+
task_id=f"stale_source_{i}",
|
|
374
|
+
category=TASK_CATEGORY_VALIDATION
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
progress.add_sub_task(
|
|
378
|
+
f"Removing {len(sources)} stale data sources",
|
|
379
|
+
task_id="stale_sources_summary",
|
|
380
|
+
category=TASK_CATEGORY_VALIDATION
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def _complete_deletion_subtasks(self, progress, sources: List[str], deleted_count: int) -> None:
|
|
384
|
+
"""Complete progress subtasks for source deletion.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
progress: TaskProgress instance
|
|
388
|
+
sources: List of source FQNs that were processed
|
|
389
|
+
deleted_count: Number of sources successfully deleted
|
|
390
|
+
"""
|
|
391
|
+
if len(sources) <= MAX_INDIVIDUAL_SUBTASKS:
|
|
392
|
+
for i in range(len(sources)):
|
|
393
|
+
if f"stale_source_{i}" in progress._tasks:
|
|
394
|
+
progress.complete_sub_task(f"stale_source_{i}")
|
|
395
|
+
else:
|
|
396
|
+
if "stale_sources_summary" in progress._tasks:
|
|
397
|
+
if deleted_count > 0:
|
|
398
|
+
s = "s" if deleted_count > 1 else ""
|
|
399
|
+
progress.update_sub_task(
|
|
400
|
+
"stale_sources_summary",
|
|
401
|
+
f"Removed {deleted_count} stale data source{s}"
|
|
402
|
+
)
|
|
403
|
+
progress.complete_sub_task("stale_sources_summary")
|
|
404
|
+
|
|
405
|
+
def _maybe_delete_stale(self, progress) -> None:
|
|
406
|
+
"""Check for and delete stale data streams that need recreation.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
progress: TaskProgress instance for tracking deletion progress
|
|
410
|
+
"""
|
|
411
|
+
with debugging.span("check_sources"):
|
|
412
|
+
stale_sources = [
|
|
413
|
+
source
|
|
414
|
+
for source, info in self.source_info.items()
|
|
415
|
+
if info.get("state") == "STALE"
|
|
416
|
+
]
|
|
417
|
+
|
|
418
|
+
if not stale_sources:
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
with DebuggingSpan("validate_sources"):
|
|
422
|
+
try:
|
|
423
|
+
# Validate which sources truly need deletion by comparing column hashes
|
|
424
|
+
truly_stale = self._filter_truly_stale_sources(stale_sources, progress)
|
|
425
|
+
|
|
426
|
+
if not truly_stale:
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
# Delete truly stale streams
|
|
430
|
+
from v0.relationalai.clients.snowflake import PYREL_ROOT_DB
|
|
431
|
+
query = f"CALL {self.app_name}.api.delete_data_streams({truly_stale}, '{PYREL_ROOT_DB}');"
|
|
432
|
+
|
|
433
|
+
self._add_deletion_subtasks(progress, truly_stale)
|
|
434
|
+
|
|
435
|
+
delete_response = self.res._exec(query)
|
|
436
|
+
delete_json_str = delete_response[0]["DELETE_DATA_STREAMS"].lower()
|
|
437
|
+
delete_data = json.loads(delete_json_str)
|
|
438
|
+
deleted_count = delete_data.get("deleted", 0)
|
|
439
|
+
|
|
440
|
+
self._complete_deletion_subtasks(progress, truly_stale, deleted_count)
|
|
441
|
+
|
|
442
|
+
# Check for errors
|
|
443
|
+
diff = len(truly_stale) - deleted_count
|
|
444
|
+
if diff > 0:
|
|
445
|
+
errors = delete_data.get("errors", None)
|
|
446
|
+
if errors:
|
|
447
|
+
raise Exception(f"Error(s) deleting streams with modified sources: {errors}")
|
|
448
|
+
|
|
449
|
+
except Exception as e:
|
|
450
|
+
# Complete any remaining subtasks
|
|
451
|
+
self._complete_deletion_subtasks(progress, stale_sources, 0)
|
|
452
|
+
if "stale_sources_summary" in progress._tasks:
|
|
453
|
+
progress.update_sub_task(
|
|
454
|
+
"stale_sources_summary",
|
|
455
|
+
f"❌ Failed to remove stale sources: {str(e)}"
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
# Don't raise if streams don't exist - this is expected
|
|
459
|
+
if "data streams do not exist" not in str(e).lower():
|
|
460
|
+
raise e from None
|
|
461
|
+
|
|
462
|
+
def _poll_loop(self, progress) -> None:
|
|
463
|
+
"""Main polling loop for use_index streams.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
progress: TaskProgress instance for tracking polling progress
|
|
467
|
+
"""
|
|
468
|
+
source_references = self.res._get_source_references(self.source_info)
|
|
469
|
+
sources_object_references_str = ", ".join(source_references)
|
|
470
|
+
|
|
471
|
+
def check_ready(progress) -> bool:
|
|
472
|
+
self.check_ready_count += 1
|
|
473
|
+
|
|
474
|
+
# To limit the performance overhead, we only check if ERP is running every N iterations
|
|
475
|
+
if self.check_ready_count % ERP_CHECK_FREQUENCY == 0:
|
|
476
|
+
with debugging.span("check_erp_status"):
|
|
477
|
+
# Add subtask for ERP status check
|
|
478
|
+
if self._erp_check_task_id is None:
|
|
479
|
+
self._erp_check_task_id = progress.add_sub_task("Checking system status", task_id="erp_check", category=TASK_CATEGORY_STATUS)
|
|
480
|
+
|
|
481
|
+
if not self.res.is_erp_running(self.app_name):
|
|
482
|
+
progress.update_sub_task("erp_check", "❌ System status check failed")
|
|
483
|
+
progress.complete_sub_task("erp_check")
|
|
484
|
+
raise ERPNotRunningError
|
|
485
|
+
else:
|
|
486
|
+
progress.update_sub_task("erp_check", "System status check complete")
|
|
487
|
+
progress.complete_sub_task("erp_check")
|
|
488
|
+
|
|
489
|
+
use_index_id = f"{self.model}_{self.execution_id}"
|
|
490
|
+
|
|
491
|
+
params = json.dumps({
|
|
492
|
+
"model": self.model,
|
|
493
|
+
"engine": self.engine_name,
|
|
494
|
+
"default_engine_size": self.engine_size, # engine_size
|
|
495
|
+
"user_agent": self.pyrel_version,
|
|
496
|
+
"use_index_id": use_index_id,
|
|
497
|
+
"pyrel_program_id": self.program_span_id,
|
|
498
|
+
"wait_for_stream_sync": self.wait_for_stream_sync,
|
|
499
|
+
"should_check_cdc": self.should_check_cdc,
|
|
500
|
+
"init_engine_async": self.init_engine_async,
|
|
501
|
+
"language": self.language,
|
|
502
|
+
"data_freshness_mins": self.data_freshness,
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
request_headers = debugging.add_current_propagation_headers(self.headers)
|
|
506
|
+
|
|
507
|
+
sql_string = f"CALL {self.app_name}.api.use_index([{sources_object_references_str}], PARSE_JSON(?), {request_headers});"
|
|
508
|
+
|
|
509
|
+
with debugging.span("wait", counter=self.counter, use_index_id=use_index_id) as span:
|
|
510
|
+
results = self.res._exec(sql_string, [params])
|
|
511
|
+
|
|
512
|
+
# Extract the JSON string from the `USE_INDEX` field
|
|
513
|
+
use_index_json_str = results[0]["USE_INDEX"]
|
|
514
|
+
|
|
515
|
+
# Parse the JSON string into a Python dictionary
|
|
516
|
+
try:
|
|
517
|
+
use_index_data = json.loads(use_index_json_str)
|
|
518
|
+
except json.JSONDecodeError as e:
|
|
519
|
+
logger.error(f"Invalid JSON from use_index API: {e}")
|
|
520
|
+
logger.error(f"Raw response (first 500 chars): {use_index_json_str[:500]}")
|
|
521
|
+
progress.update_main_status("❌ Received invalid response from server")
|
|
522
|
+
raise ValueError(f"Invalid JSON response from use_index: {e}") from e
|
|
523
|
+
|
|
524
|
+
span.update(use_index_data)
|
|
525
|
+
|
|
526
|
+
# Log the full use_index_data for debugging if needed
|
|
527
|
+
logger.debug(f"use_index_data: {json.dumps(use_index_data, indent=4)}")
|
|
528
|
+
|
|
529
|
+
all_data = use_index_data.get("data", [])
|
|
530
|
+
ready = use_index_data.get("ready", False)
|
|
531
|
+
engines = use_index_data.get("engines", [])
|
|
532
|
+
errors = use_index_data.get("errors", [])
|
|
533
|
+
relations = use_index_data.get("relations", {})
|
|
534
|
+
cdc_enabled = use_index_data.get("cdcEnabled", False)
|
|
535
|
+
if self.check_ready_count % ERP_CHECK_FREQUENCY == 0 or not cdc_enabled:
|
|
536
|
+
self.should_check_cdc = True
|
|
537
|
+
else:
|
|
538
|
+
self.should_check_cdc = False
|
|
539
|
+
|
|
540
|
+
if engines and self.init_engine_async:
|
|
541
|
+
self.init_engine_async = False
|
|
542
|
+
|
|
543
|
+
break_loop = False
|
|
544
|
+
has_stream_errors = False
|
|
545
|
+
has_general_errors = False
|
|
546
|
+
|
|
547
|
+
# Update main progress message
|
|
548
|
+
if ready:
|
|
549
|
+
progress.update_main_status("Done")
|
|
550
|
+
|
|
551
|
+
# Handle streams data
|
|
552
|
+
if not ready and all_data:
|
|
553
|
+
progress.update_main_status("Processing background tasks. This may take a while...")
|
|
554
|
+
|
|
555
|
+
# Build complete stream order first (only on first iteration with data)
|
|
556
|
+
if self.total_streams == 0:
|
|
557
|
+
for data in all_data:
|
|
558
|
+
if data is None:
|
|
559
|
+
continue
|
|
560
|
+
fq_name = data.get("fq_object_name", "Unknown")
|
|
561
|
+
if fq_name not in self.stream_order:
|
|
562
|
+
self.stream_order.append(fq_name)
|
|
563
|
+
|
|
564
|
+
# Set total streams count based on complete order (only once)
|
|
565
|
+
self.total_streams = len(self.stream_order)
|
|
566
|
+
|
|
567
|
+
# Add new streams as subtasks if we haven't reached the limit
|
|
568
|
+
for data in all_data:
|
|
569
|
+
fq_name = data.get("fq_object_name", "Unknown")
|
|
570
|
+
status = data.get("data_sync_status", "").lower() if data else ""
|
|
571
|
+
batches_count = data.get("pending_batches_count", 0)
|
|
572
|
+
|
|
573
|
+
# Only add if we haven't seen this stream and we're under the limit
|
|
574
|
+
self._add_stream_subtask(progress, fq_name, status, batches_count)
|
|
575
|
+
|
|
576
|
+
# Handle errors for existing streams
|
|
577
|
+
if fq_name in self.stream_task_ids and data.get("errors", []):
|
|
578
|
+
for error in data.get("errors", []):
|
|
579
|
+
error_msg = f"{error.get('error')}, source: {error.get('source')}"
|
|
580
|
+
self.table_objects_with_other_errors.append(
|
|
581
|
+
SnowflakeTableObject(error_msg, fq_name)
|
|
582
|
+
)
|
|
583
|
+
# Mark stream as failed
|
|
584
|
+
progress.update_sub_task(fq_name, f"❌ Failed: {fq_name}")
|
|
585
|
+
has_stream_errors = True
|
|
586
|
+
|
|
587
|
+
# Update stream status (only for streams that aren't already completed)
|
|
588
|
+
if fq_name in self.stream_task_ids and fq_name in progress._tasks and not progress._tasks[fq_name].completed:
|
|
589
|
+
# Get the stream position from the stream order
|
|
590
|
+
if fq_name in self.stream_order:
|
|
591
|
+
stream_position = self.stream_order.index(fq_name) + 1
|
|
592
|
+
else:
|
|
593
|
+
# Fallback to 1 if not in order (shouldn't happen)
|
|
594
|
+
stream_position = 1
|
|
595
|
+
|
|
596
|
+
# Build status message
|
|
597
|
+
if batches_count > 0 and status == 'syncing':
|
|
598
|
+
status_message = f"Syncing {fq_name} ({stream_position}/{self.total_streams}), batches: {batches_count}"
|
|
599
|
+
else:
|
|
600
|
+
status_message = f"Pending {fq_name} ({stream_position}/{self.total_streams})..."
|
|
601
|
+
|
|
602
|
+
progress.update_sub_task(fq_name, status_message)
|
|
603
|
+
|
|
604
|
+
# Complete the stream if it's synced
|
|
605
|
+
if status == "synced":
|
|
606
|
+
progress.complete_sub_task(fq_name)
|
|
607
|
+
|
|
608
|
+
# Add more streams from the queue if we have space and more streams exist
|
|
609
|
+
if len(self.stream_task_ids) < MAX_DATA_SOURCE_SUBTASKS:
|
|
610
|
+
for data in all_data:
|
|
611
|
+
fq_name = data.get("fq_object_name", "Unknown")
|
|
612
|
+
status = data.get("data_sync_status", "").lower()
|
|
613
|
+
batches_count = data.get("pending_batches_count", 0)
|
|
614
|
+
|
|
615
|
+
self._add_stream_subtask(progress, fq_name, status, batches_count)
|
|
616
|
+
|
|
617
|
+
self.counter += 1
|
|
618
|
+
|
|
619
|
+
# Handle engines data
|
|
620
|
+
if not ready and engines:
|
|
621
|
+
# Add new engines as subtasks if they don't exist
|
|
622
|
+
for engine in engines:
|
|
623
|
+
if not engine or not isinstance(engine, dict):
|
|
624
|
+
continue
|
|
625
|
+
size = self.engine_size
|
|
626
|
+
name = engine.get("name", "Unknown")
|
|
627
|
+
status = (engine.get("status") or "").lower()
|
|
628
|
+
sub_task_id = self.engine_task_ids.get(name, None)
|
|
629
|
+
sub_task_status_message = ""
|
|
630
|
+
|
|
631
|
+
# Complete the sub task if it exists and the engine status is ready
|
|
632
|
+
if sub_task_id and name in progress._tasks and not progress._tasks[name].completed and (status == "ready"):
|
|
633
|
+
sub_task_status_message = f"Engine {name} ({size}) ready"
|
|
634
|
+
progress.update_sub_task(name, sub_task_status_message)
|
|
635
|
+
progress.complete_sub_task(name)
|
|
636
|
+
|
|
637
|
+
# Add the sub task if it doesn't exist and the engine status is pending
|
|
638
|
+
if not sub_task_id and status == "pending":
|
|
639
|
+
writer = engine.get("writer", False)
|
|
640
|
+
engine_type = "writer engine" if writer else "engine"
|
|
641
|
+
sub_task_status_message = f"Provisioning {engine_type} {name} ({size})"
|
|
642
|
+
self.engine_task_ids[name] = progress.add_sub_task(sub_task_status_message, task_id=name, category=TASK_CATEGORY_PROVISIONING)
|
|
643
|
+
|
|
644
|
+
# Special handling for CDC_MANAGED_ENGINE - mark ready when any stream starts processing
|
|
645
|
+
cdc_task = progress._tasks.get(CDC_MANAGED_ENGINE) if CDC_MANAGED_ENGINE in progress._tasks else None
|
|
646
|
+
if CDC_MANAGED_ENGINE in self.engine_task_ids and cdc_task and not cdc_task.completed:
|
|
647
|
+
|
|
648
|
+
has_processing_streams = any(
|
|
649
|
+
stream.get("next_batch_status", "") == "processing"
|
|
650
|
+
for stream in all_data
|
|
651
|
+
)
|
|
652
|
+
if has_processing_streams and cdc_task and not cdc_task.completed:
|
|
653
|
+
progress.update_sub_task(CDC_MANAGED_ENGINE, f"Engine {CDC_MANAGED_ENGINE} ({self.engine_size}) ready")
|
|
654
|
+
progress.complete_sub_task(CDC_MANAGED_ENGINE)
|
|
655
|
+
|
|
656
|
+
self.counter += 1
|
|
657
|
+
|
|
658
|
+
# Handle relations data
|
|
659
|
+
if relations and isinstance(relations, dict):
|
|
660
|
+
txn = relations.get("txn", {}) or {}
|
|
661
|
+
txn_id = txn.get("id", None)
|
|
662
|
+
|
|
663
|
+
# Only show relations subtask if there is a valid txn object
|
|
664
|
+
if txn_id:
|
|
665
|
+
status = relations.get("status", "").upper()
|
|
666
|
+
|
|
667
|
+
# Create relations subtask if it doesn't exist
|
|
668
|
+
if self.relations_task_id is None:
|
|
669
|
+
self.relations_task_id = progress.add_sub_task("Populating relations", task_id="relations", category=TASK_CATEGORY_RELATIONS)
|
|
670
|
+
|
|
671
|
+
# Set the start time from the JSON if available (always update)
|
|
672
|
+
start_time_ms = relations.get("start_time")
|
|
673
|
+
if start_time_ms:
|
|
674
|
+
start_time_seconds = start_time_ms / 1000.0
|
|
675
|
+
progress._tasks["relations"].added_time = start_time_seconds
|
|
676
|
+
|
|
677
|
+
# Update relations status
|
|
678
|
+
if status == "COMPLETED":
|
|
679
|
+
progress.update_sub_task("relations", f"Relations populated (txn: {txn_id})")
|
|
680
|
+
|
|
681
|
+
# Set the completion time from the JSON if available
|
|
682
|
+
end_time_ms = relations.get("end_time")
|
|
683
|
+
if end_time_ms:
|
|
684
|
+
end_time_seconds = end_time_ms / 1000.0
|
|
685
|
+
progress._tasks["relations"].completed_time = end_time_seconds
|
|
686
|
+
|
|
687
|
+
progress.complete_sub_task("relations", record_time=False) # Don't record local time
|
|
688
|
+
else:
|
|
689
|
+
progress.update_sub_task("relations", f"Relations populating (txn: {txn_id})")
|
|
690
|
+
|
|
691
|
+
self.counter += 1
|
|
692
|
+
|
|
693
|
+
# Handle errors
|
|
694
|
+
if not ready and errors:
|
|
695
|
+
for error in errors:
|
|
696
|
+
if error is None:
|
|
697
|
+
continue
|
|
698
|
+
if error.get("type") == "data":
|
|
699
|
+
message = error.get("message", "").lower()
|
|
700
|
+
if ("change_tracking" in message or "change tracking" in message):
|
|
701
|
+
err_source = error.get("source")
|
|
702
|
+
err_source_type = self.source_info.get(err_source, {}).get("type")
|
|
703
|
+
self.tables_with_not_enabled_change_tracking.append((err_source, err_source_type))
|
|
704
|
+
else:
|
|
705
|
+
self.table_objects_with_other_errors.append(
|
|
706
|
+
SnowflakeTableObject(error.get("message"), error.get("source"))
|
|
707
|
+
)
|
|
708
|
+
elif error.get("type") == "engine":
|
|
709
|
+
self.engine_errors.append(error)
|
|
710
|
+
else:
|
|
711
|
+
# Other types of errors, e.g. "validation"
|
|
712
|
+
self.table_objects_with_other_errors.append(
|
|
713
|
+
SnowflakeTableObject(error.get("message"), error.get("source"))
|
|
714
|
+
)
|
|
715
|
+
has_general_errors = True
|
|
716
|
+
|
|
717
|
+
# If ready, complete all remaining subtasks
|
|
718
|
+
if ready:
|
|
719
|
+
self.cache.record_update(self.source_info)
|
|
720
|
+
# Complete any remaining stream subtasks
|
|
721
|
+
for fq_name in self.stream_task_ids:
|
|
722
|
+
if fq_name in progress._tasks and not progress._tasks[fq_name].completed:
|
|
723
|
+
progress.complete_sub_task(fq_name)
|
|
724
|
+
# Complete any remaining engine subtasks
|
|
725
|
+
for name in self.engine_task_ids:
|
|
726
|
+
if name in progress._tasks and not progress._tasks[name].completed:
|
|
727
|
+
progress.complete_sub_task(name)
|
|
728
|
+
# Complete relations subtask if it exists and isn't completed
|
|
729
|
+
if self.relations_task_id and "relations" in progress._tasks and not progress._tasks["relations"].completed:
|
|
730
|
+
progress.complete_sub_task("relations")
|
|
731
|
+
break_loop = True
|
|
732
|
+
elif has_stream_errors or has_general_errors:
|
|
733
|
+
# Break the loop if there are errors, but only after reporting all progress
|
|
734
|
+
break_loop = True
|
|
735
|
+
|
|
736
|
+
return break_loop
|
|
737
|
+
|
|
738
|
+
poll_with_specified_overhead(lambda: check_ready(progress), overhead_rate=POLL_OVERHEAD_RATE, max_delay=POLL_MAX_DELAY)
|
|
739
|
+
|
|
740
|
+
def _post_check(self, progress) -> None:
|
|
741
|
+
"""Run post-processing checks including change tracking enablement.
|
|
742
|
+
|
|
743
|
+
Args:
|
|
744
|
+
progress: TaskProgress instance for tracking progress
|
|
745
|
+
|
|
746
|
+
Raises:
|
|
747
|
+
SnowflakeChangeTrackingNotEnabledException: If change tracking cannot be enabled
|
|
748
|
+
SnowflakeTableObjectsException: If there are table-related errors
|
|
749
|
+
EngineProvisioningFailed: If engine provisioning fails
|
|
750
|
+
"""
|
|
751
|
+
num_tables_altered = 0
|
|
752
|
+
failed_tables = [] # Track tables that failed to enable change tracking
|
|
753
|
+
|
|
754
|
+
enabled_tables = []
|
|
755
|
+
if (
|
|
756
|
+
self.tables_with_not_enabled_change_tracking
|
|
757
|
+
and self.res.config.get("ensure_change_tracking", False)
|
|
758
|
+
):
|
|
759
|
+
tables_to_process = self.tables_with_not_enabled_change_tracking
|
|
760
|
+
# Track timing for change tracking
|
|
761
|
+
|
|
762
|
+
# Add subtasks based on count
|
|
763
|
+
if len(tables_to_process) <= MAX_INDIVIDUAL_SUBTASKS:
|
|
764
|
+
# Add individual subtasks for each table
|
|
765
|
+
for i, table in enumerate(tables_to_process):
|
|
766
|
+
fqn, kind = table
|
|
767
|
+
progress.add_sub_task(f"Enabling change tracking on {fqn} ({i+1}/{len(tables_to_process)})", task_id=f"change_tracking_{i}", category=TASK_CATEGORY_CHANGE_TRACKING)
|
|
768
|
+
else:
|
|
769
|
+
# Add single summary subtask for many tables
|
|
770
|
+
progress.add_sub_task(f"Enabling change tracking on {len(tables_to_process)} tables", task_id="change_tracking_summary", category=TASK_CATEGORY_CHANGE_TRACKING)
|
|
771
|
+
|
|
772
|
+
# Process tables
|
|
773
|
+
for i, table in enumerate(tables_to_process):
|
|
774
|
+
fqn, kind = table # Unpack outside try block to ensure fqn is defined
|
|
775
|
+
|
|
776
|
+
try:
|
|
777
|
+
# Validate table_type to prevent SQL injection
|
|
778
|
+
# Should only be TABLE or VIEW
|
|
779
|
+
if kind not in ("TABLE", "VIEW"):
|
|
780
|
+
logger.error(f"Invalid table kind '{kind}' for {fqn}, skipping")
|
|
781
|
+
failed_tables.append((fqn, f"Invalid table kind: {kind}"))
|
|
782
|
+
# Mark as failed in progress
|
|
783
|
+
if len(tables_to_process) <= MAX_INDIVIDUAL_SUBTASKS:
|
|
784
|
+
if f"change_tracking_{i}" in progress._tasks:
|
|
785
|
+
progress.update_sub_task(f"change_tracking_{i}", f"❌ Invalid type: {fqn}")
|
|
786
|
+
progress.complete_sub_task(f"change_tracking_{i}")
|
|
787
|
+
continue
|
|
788
|
+
|
|
789
|
+
# Execute ALTER statement
|
|
790
|
+
# Note: fqn should already be properly quoted from source_info
|
|
791
|
+
self.res._exec(f"ALTER {kind} {fqn} SET CHANGE_TRACKING = TRUE;")
|
|
792
|
+
enabled_tables.append(table)
|
|
793
|
+
num_tables_altered += 1
|
|
794
|
+
|
|
795
|
+
# Update progress based on subtask type
|
|
796
|
+
if len(tables_to_process) <= MAX_INDIVIDUAL_SUBTASKS:
|
|
797
|
+
# Complete individual table subtask
|
|
798
|
+
progress.complete_sub_task(f"change_tracking_{i}")
|
|
799
|
+
else:
|
|
800
|
+
# Update summary subtask with progress
|
|
801
|
+
progress.update_sub_task("change_tracking_summary",
|
|
802
|
+
f"Enabling change tracking on {len(tables_to_process)} tables... ({i+1}/{len(tables_to_process)})")
|
|
803
|
+
except Exception as e:
|
|
804
|
+
# Log the error for debugging
|
|
805
|
+
logger.warning(f"Failed to enable change tracking on {fqn}: {e}")
|
|
806
|
+
failed_tables.append((fqn, str(e)))
|
|
807
|
+
|
|
808
|
+
# Handle errors based on subtask type
|
|
809
|
+
if len(tables_to_process) <= MAX_INDIVIDUAL_SUBTASKS:
|
|
810
|
+
# Mark the individual subtask as failed and complete it
|
|
811
|
+
if f"change_tracking_{i}" in progress._tasks:
|
|
812
|
+
progress.update_sub_task(f"change_tracking_{i}", f"❌ Failed: {fqn}")
|
|
813
|
+
progress.complete_sub_task(f"change_tracking_{i}")
|
|
814
|
+
# Continue processing other tables despite this failure
|
|
815
|
+
|
|
816
|
+
# Complete summary subtask if used
|
|
817
|
+
if len(tables_to_process) > MAX_INDIVIDUAL_SUBTASKS and "change_tracking_summary" in progress._tasks:
|
|
818
|
+
if num_tables_altered > 0:
|
|
819
|
+
s = "s" if num_tables_altered > 1 else ""
|
|
820
|
+
success_msg = f"Enabled change tracking on {num_tables_altered} table{s}"
|
|
821
|
+
if failed_tables:
|
|
822
|
+
success_msg += f" ({len(failed_tables)} failed)"
|
|
823
|
+
progress.update_sub_task("change_tracking_summary", success_msg)
|
|
824
|
+
elif failed_tables:
|
|
825
|
+
progress.update_sub_task("change_tracking_summary", f"❌ Failed on {len(failed_tables)} table(s)")
|
|
826
|
+
progress.complete_sub_task("change_tracking_summary")
|
|
827
|
+
|
|
828
|
+
# Log summary of failed tables
|
|
829
|
+
if failed_tables:
|
|
830
|
+
logger.warning(f"Failed to enable change tracking on {len(failed_tables)} table(s)")
|
|
831
|
+
for fqn, error in failed_tables:
|
|
832
|
+
logger.warning(f" {fqn}: {error}")
|
|
833
|
+
|
|
834
|
+
# Remove the tables that were successfully enabled from the list of not enabled tables
|
|
835
|
+
# so that we don't raise an exception for them later
|
|
836
|
+
self.tables_with_not_enabled_change_tracking = [
|
|
837
|
+
t for t in self.tables_with_not_enabled_change_tracking if t not in enabled_tables
|
|
838
|
+
]
|
|
839
|
+
|
|
840
|
+
if self.tables_with_not_enabled_change_tracking:
|
|
841
|
+
progress.update_main_status("Errors found. See below for details.")
|
|
842
|
+
raise SnowflakeChangeTrackingNotEnabledException(
|
|
843
|
+
self.tables_with_not_enabled_change_tracking
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
if self.table_objects_with_other_errors:
|
|
847
|
+
progress.update_main_status("Errors found. See below for details.")
|
|
848
|
+
raise SnowflakeTableObjectsException(self.table_objects_with_other_errors)
|
|
849
|
+
if self.engine_errors:
|
|
850
|
+
progress.update_main_status("Errors found. See below for details.")
|
|
851
|
+
# if there is an engine error, probably auto create engine failed
|
|
852
|
+
# Create a synthetic exception from the first engine error
|
|
853
|
+
first_error = self.engine_errors[0]
|
|
854
|
+
error_message = first_error.get("message", "Unknown engine error")
|
|
855
|
+
synthetic_exception = Exception(f"Engine error: {error_message}")
|
|
856
|
+
raise EngineProvisioningFailed(self.engine_name, synthetic_exception)
|
|
857
|
+
|
|
858
|
+
if num_tables_altered > 0:
|
|
859
|
+
self._poll_loop(progress)
|
|
860
|
+
|
|
861
|
+
class DirectUseIndexPoller(UseIndexPoller):
|
|
862
|
+
"""
|
|
863
|
+
Extends UseIndexPoller to handle direct-access prepare_index when no sources.
|
|
864
|
+
"""
|
|
865
|
+
def __init__(
|
|
866
|
+
self,
|
|
867
|
+
resource: "DirectAccessResources",
|
|
868
|
+
app_name: str,
|
|
869
|
+
sources: Iterable[str],
|
|
870
|
+
model: str,
|
|
871
|
+
engine_name: str,
|
|
872
|
+
engine_size: Optional[str],
|
|
873
|
+
language: str = "rel",
|
|
874
|
+
program_span_id: Optional[str] = None,
|
|
875
|
+
headers: Optional[Dict] = None,
|
|
876
|
+
generation: Optional[Generation] = None,
|
|
877
|
+
):
|
|
878
|
+
super().__init__(
|
|
879
|
+
resource=resource,
|
|
880
|
+
app_name=app_name,
|
|
881
|
+
sources=sources,
|
|
882
|
+
model=model,
|
|
883
|
+
engine_name=engine_name,
|
|
884
|
+
engine_size=engine_size,
|
|
885
|
+
language=language,
|
|
886
|
+
program_span_id=program_span_id,
|
|
887
|
+
headers=headers,
|
|
888
|
+
generation=generation,
|
|
889
|
+
)
|
|
890
|
+
from v0.relationalai.clients.snowflake import DirectAccessResources
|
|
891
|
+
self.res: DirectAccessResources = cast(DirectAccessResources, self.res)
|
|
892
|
+
|
|
893
|
+
def poll(self) -> None:
|
|
894
|
+
if not self.sources:
|
|
895
|
+
from v0.relationalai.errors import RAIException
|
|
896
|
+
collected_errors: List[Dict] = []
|
|
897
|
+
attempt = 1
|
|
898
|
+
|
|
899
|
+
def check_direct(progress) -> bool:
|
|
900
|
+
nonlocal attempt
|
|
901
|
+
with debugging.span("wait", counter=self.counter) as span:
|
|
902
|
+
span.update({"attempt": attempt, "engine_name": self.engine_name, "model": self.model})
|
|
903
|
+
# we are skipping pulling relations here, as direct access only handle non-sources cases
|
|
904
|
+
# and we don't need to pull relations for that, therefore, we pass empty list for rai_relations
|
|
905
|
+
# and set skip_pull_relations to True
|
|
906
|
+
resp = self.res._prepare_index(
|
|
907
|
+
model=self.model,
|
|
908
|
+
engine_name=self.engine_name,
|
|
909
|
+
engine_size=self.engine_size,
|
|
910
|
+
language=self.language,
|
|
911
|
+
rai_relations=[],
|
|
912
|
+
pyrel_program_id=self.program_span_id,
|
|
913
|
+
skip_pull_relations=True,
|
|
914
|
+
headers=self.headers,
|
|
915
|
+
)
|
|
916
|
+
span.update(resp)
|
|
917
|
+
caller_engine = resp.get("caller_engine", {})
|
|
918
|
+
# Handle case where caller_engine might be None
|
|
919
|
+
ce_status = caller_engine.get("status", "").lower() if caller_engine else ""
|
|
920
|
+
errors = resp.get("errors", [])
|
|
921
|
+
|
|
922
|
+
ready = resp.get("ready", False)
|
|
923
|
+
|
|
924
|
+
# Update main progress message
|
|
925
|
+
if ready:
|
|
926
|
+
progress.update_main_status("Done")
|
|
927
|
+
else:
|
|
928
|
+
progress.update_main_status("Preparing your data...")
|
|
929
|
+
|
|
930
|
+
if ready:
|
|
931
|
+
return True
|
|
932
|
+
else:
|
|
933
|
+
if ce_status == "pending":
|
|
934
|
+
# Add or update engine subtask
|
|
935
|
+
engine_name = caller_engine.get('name', self.engine_name)
|
|
936
|
+
if not hasattr(progress, '_engine_task_id'):
|
|
937
|
+
progress._engine_task_id = progress.add_sub_task(f"Waiting for engine '{engine_name}' to be ready...", task_id=engine_name)
|
|
938
|
+
else:
|
|
939
|
+
progress.update_sub_task(engine_name, f"Waiting for engine '{engine_name}' to be ready...")
|
|
940
|
+
else:
|
|
941
|
+
# Handle errors as subtasks
|
|
942
|
+
if errors:
|
|
943
|
+
progress.update_main_status("Encountered errors during preparation...")
|
|
944
|
+
for i, err in enumerate(errors):
|
|
945
|
+
error_id = f"error_{i}"
|
|
946
|
+
if not hasattr(progress, f'_error_task_id_{i}'):
|
|
947
|
+
error_msg = err.get('message', 'Unknown error')
|
|
948
|
+
setattr(progress, f'_error_task_id_{i}', progress.add_sub_task(f"❌ {error_msg}", task_id=error_id))
|
|
949
|
+
else:
|
|
950
|
+
error_msg = err.get('message', 'Unknown error')
|
|
951
|
+
progress.update_sub_task(error_id, f"❌ {error_msg}")
|
|
952
|
+
collected_errors.append(err)
|
|
953
|
+
|
|
954
|
+
attempt += 1
|
|
955
|
+
return False
|
|
956
|
+
|
|
957
|
+
# Read show_duration_summary config flag (defaults to True for backward compatibility)
|
|
958
|
+
show_duration_summary = bool(self.res.config.get("show_duration_summary", True))
|
|
959
|
+
|
|
960
|
+
with create_progress(
|
|
961
|
+
description="Preparing your data...",
|
|
962
|
+
success_message="Done",
|
|
963
|
+
leading_newline=True,
|
|
964
|
+
trailing_newline=True,
|
|
965
|
+
show_duration_summary=show_duration_summary,
|
|
966
|
+
) as progress:
|
|
967
|
+
# Add cache usage subtask
|
|
968
|
+
self._add_cache_subtask(progress)
|
|
969
|
+
|
|
970
|
+
with debugging.span("poll_direct"):
|
|
971
|
+
poll_with_specified_overhead(lambda: check_direct(progress), overhead_rate=POLL_OVERHEAD_RATE, max_delay=POLL_MAX_DELAY)
|
|
972
|
+
|
|
973
|
+
# Run the same post-check logic as UseIndexPoller
|
|
974
|
+
self._post_check(progress)
|
|
975
|
+
|
|
976
|
+
if collected_errors:
|
|
977
|
+
msg = "; ".join(e.get("message", "") for e in collected_errors)
|
|
978
|
+
raise RAIException(msg)
|
|
979
|
+
else:
|
|
980
|
+
super().poll()
|