relationalai 0.13.5__py3-none-any.whl → 1.0.0a1__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.
Files changed (856) hide show
  1. relationalai/__init__.py +1 -256
  2. relationalai/config/__init__.py +56 -0
  3. relationalai/config/config.py +289 -0
  4. relationalai/config/config_fields.py +86 -0
  5. relationalai/config/connections/__init__.py +46 -0
  6. relationalai/config/connections/base.py +23 -0
  7. relationalai/config/connections/duckdb.py +29 -0
  8. relationalai/config/connections/snowflake.py +243 -0
  9. relationalai/config/external/__init__.py +17 -0
  10. relationalai/config/external/dbt_converter.py +101 -0
  11. relationalai/config/external/dbt_models.py +93 -0
  12. relationalai/config/external/snowflake_converter.py +41 -0
  13. relationalai/config/external/snowflake_models.py +85 -0
  14. relationalai/config/external/utils.py +19 -0
  15. relationalai/config/shims.py +1 -0
  16. relationalai/semantics/__init__.py +146 -22
  17. relationalai/semantics/backends/lqp/annotations.py +11 -0
  18. relationalai/semantics/backends/sql/sql_compiler.py +327 -0
  19. relationalai/semantics/frontend/base.py +1716 -0
  20. relationalai/semantics/frontend/core.py +179 -0
  21. relationalai/semantics/frontend/front_compiler.py +1313 -0
  22. relationalai/semantics/frontend/pprint.py +408 -0
  23. relationalai/semantics/metamodel/__init__.py +6 -40
  24. relationalai/semantics/metamodel/builtins.py +205 -772
  25. relationalai/semantics/metamodel/metamodel.py +437 -0
  26. relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
  27. relationalai/semantics/metamodel/pprint.py +412 -0
  28. relationalai/semantics/metamodel/rewriter.py +266 -0
  29. relationalai/semantics/metamodel/typer.py +1186 -0
  30. relationalai/semantics/std/__init__.py +60 -40
  31. relationalai/semantics/std/aggregates.py +149 -0
  32. relationalai/semantics/std/common.py +44 -0
  33. relationalai/semantics/std/constraints.py +37 -43
  34. relationalai/semantics/std/datetime.py +246 -135
  35. relationalai/semantics/std/decimals.py +45 -52
  36. relationalai/semantics/std/floats.py +13 -5
  37. relationalai/semantics/std/integers.py +26 -11
  38. relationalai/semantics/std/math.py +183 -112
  39. relationalai/semantics/std/numbers.py +86 -0
  40. relationalai/semantics/std/re.py +80 -62
  41. relationalai/semantics/std/strings.py +101 -46
  42. relationalai/shims/executor.py +161 -0
  43. relationalai/shims/helpers.py +126 -0
  44. relationalai/shims/hoister.py +221 -0
  45. relationalai/shims/mm2v0.py +1324 -0
  46. relationalai/tools/cli/__init__.py +6 -0
  47. relationalai/tools/cli/cli.py +90 -0
  48. relationalai/tools/cli/components/__init__.py +5 -0
  49. relationalai/tools/cli/components/progress_reader.py +1524 -0
  50. relationalai/tools/cli/components/utils.py +58 -0
  51. relationalai/tools/cli/config_template.py +45 -0
  52. relationalai/tools/cli/dev.py +19 -0
  53. relationalai/tools/debugger.py +289 -183
  54. relationalai/tools/typer_debugger.py +93 -0
  55. relationalai/util/dataclasses.py +43 -0
  56. relationalai/util/docutils.py +40 -0
  57. relationalai/util/error.py +199 -0
  58. relationalai/util/format.py +48 -109
  59. relationalai/util/naming.py +145 -0
  60. relationalai/util/python.py +35 -0
  61. relationalai/util/runtime.py +156 -0
  62. relationalai/util/schema.py +197 -0
  63. relationalai/util/source.py +185 -0
  64. relationalai/util/structures.py +163 -0
  65. relationalai/util/tracing.py +261 -0
  66. relationalai-1.0.0a1.dist-info/METADATA +44 -0
  67. relationalai-1.0.0a1.dist-info/RECORD +489 -0
  68. relationalai-1.0.0a1.dist-info/WHEEL +5 -0
  69. relationalai-1.0.0a1.dist-info/entry_points.txt +3 -0
  70. relationalai-1.0.0a1.dist-info/top_level.txt +2 -0
  71. v0/relationalai/__init__.py +216 -0
  72. v0/relationalai/clients/__init__.py +5 -0
  73. v0/relationalai/clients/azure.py +477 -0
  74. v0/relationalai/clients/client.py +912 -0
  75. v0/relationalai/clients/config.py +673 -0
  76. v0/relationalai/clients/direct_access_client.py +118 -0
  77. v0/relationalai/clients/hash_util.py +31 -0
  78. v0/relationalai/clients/local.py +571 -0
  79. v0/relationalai/clients/profile_polling.py +73 -0
  80. v0/relationalai/clients/result_helpers.py +420 -0
  81. v0/relationalai/clients/snowflake.py +3869 -0
  82. v0/relationalai/clients/types.py +113 -0
  83. v0/relationalai/clients/use_index_poller.py +980 -0
  84. v0/relationalai/clients/util.py +356 -0
  85. v0/relationalai/debugging.py +389 -0
  86. v0/relationalai/dsl.py +1749 -0
  87. v0/relationalai/early_access/builder/__init__.py +30 -0
  88. v0/relationalai/early_access/builder/builder/__init__.py +35 -0
  89. v0/relationalai/early_access/builder/snowflake/__init__.py +12 -0
  90. v0/relationalai/early_access/builder/std/__init__.py +25 -0
  91. v0/relationalai/early_access/builder/std/decimals/__init__.py +12 -0
  92. v0/relationalai/early_access/builder/std/integers/__init__.py +12 -0
  93. v0/relationalai/early_access/builder/std/math/__init__.py +12 -0
  94. v0/relationalai/early_access/builder/std/strings/__init__.py +14 -0
  95. v0/relationalai/early_access/devtools/__init__.py +12 -0
  96. v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
  97. v0/relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
  98. v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
  99. v0/relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
  100. v0/relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
  101. v0/relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
  102. v0/relationalai/early_access/dsl/bindings/common.py +402 -0
  103. v0/relationalai/early_access/dsl/bindings/csv.py +170 -0
  104. v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
  105. v0/relationalai/early_access/dsl/bindings/snowflake.py +64 -0
  106. v0/relationalai/early_access/dsl/codegen/binder.py +411 -0
  107. v0/relationalai/early_access/dsl/codegen/common.py +79 -0
  108. v0/relationalai/early_access/dsl/codegen/helpers.py +23 -0
  109. v0/relationalai/early_access/dsl/codegen/relations.py +700 -0
  110. v0/relationalai/early_access/dsl/codegen/weaver.py +417 -0
  111. v0/relationalai/early_access/dsl/core/builders/__init__.py +47 -0
  112. v0/relationalai/early_access/dsl/core/builders/logic.py +19 -0
  113. v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
  114. v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
  115. v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
  116. v0/relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
  117. v0/relationalai/early_access/dsl/core/context.py +13 -0
  118. v0/relationalai/early_access/dsl/core/cset.py +132 -0
  119. v0/relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
  120. v0/relationalai/early_access/dsl/core/exprs/relational.py +18 -0
  121. v0/relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
  122. v0/relationalai/early_access/dsl/core/instances.py +44 -0
  123. v0/relationalai/early_access/dsl/core/logic/__init__.py +193 -0
  124. v0/relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
  125. v0/relationalai/early_access/dsl/core/logic/exists.py +223 -0
  126. v0/relationalai/early_access/dsl/core/logic/helper.py +163 -0
  127. v0/relationalai/early_access/dsl/core/namespaces.py +32 -0
  128. v0/relationalai/early_access/dsl/core/relations.py +276 -0
  129. v0/relationalai/early_access/dsl/core/rules.py +112 -0
  130. v0/relationalai/early_access/dsl/core/std/__init__.py +45 -0
  131. v0/relationalai/early_access/dsl/core/temporal/recall.py +6 -0
  132. v0/relationalai/early_access/dsl/core/types/__init__.py +270 -0
  133. v0/relationalai/early_access/dsl/core/types/concepts.py +128 -0
  134. v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
  135. v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
  136. v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
  137. v0/relationalai/early_access/dsl/core/types/standard.py +92 -0
  138. v0/relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
  139. v0/relationalai/early_access/dsl/core/types/variables.py +203 -0
  140. v0/relationalai/early_access/dsl/ir/compiler.py +318 -0
  141. v0/relationalai/early_access/dsl/ir/executor.py +260 -0
  142. v0/relationalai/early_access/dsl/ontologies/constraints.py +88 -0
  143. v0/relationalai/early_access/dsl/ontologies/export.py +30 -0
  144. v0/relationalai/early_access/dsl/ontologies/models.py +453 -0
  145. v0/relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
  146. v0/relationalai/early_access/dsl/ontologies/readings.py +60 -0
  147. v0/relationalai/early_access/dsl/ontologies/relationships.py +322 -0
  148. v0/relationalai/early_access/dsl/ontologies/roles.py +87 -0
  149. v0/relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
  150. v0/relationalai/early_access/dsl/orm/constraints.py +438 -0
  151. v0/relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
  152. v0/relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
  153. v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
  154. v0/relationalai/early_access/dsl/orm/measures/measures.py +299 -0
  155. v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
  156. v0/relationalai/early_access/dsl/orm/models.py +256 -0
  157. v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
  158. v0/relationalai/early_access/dsl/orm/printer.py +469 -0
  159. v0/relationalai/early_access/dsl/orm/reasoners.py +480 -0
  160. v0/relationalai/early_access/dsl/orm/relations.py +19 -0
  161. v0/relationalai/early_access/dsl/orm/relationships.py +251 -0
  162. v0/relationalai/early_access/dsl/orm/types.py +42 -0
  163. v0/relationalai/early_access/dsl/orm/utils.py +79 -0
  164. v0/relationalai/early_access/dsl/orm/verb.py +204 -0
  165. v0/relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
  166. v0/relationalai/early_access/dsl/relations.py +170 -0
  167. v0/relationalai/early_access/dsl/rulesets.py +69 -0
  168. v0/relationalai/early_access/dsl/schemas/__init__.py +450 -0
  169. v0/relationalai/early_access/dsl/schemas/builder.py +48 -0
  170. v0/relationalai/early_access/dsl/schemas/comp_names.py +51 -0
  171. v0/relationalai/early_access/dsl/schemas/components.py +203 -0
  172. v0/relationalai/early_access/dsl/schemas/contexts.py +156 -0
  173. v0/relationalai/early_access/dsl/schemas/exprs.py +89 -0
  174. v0/relationalai/early_access/dsl/schemas/fragments.py +464 -0
  175. v0/relationalai/early_access/dsl/serialization.py +79 -0
  176. v0/relationalai/early_access/dsl/serialize/exporter.py +163 -0
  177. v0/relationalai/early_access/dsl/snow/api.py +104 -0
  178. v0/relationalai/early_access/dsl/snow/common.py +76 -0
  179. v0/relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
  180. v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
  181. v0/relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
  182. v0/relationalai/early_access/dsl/types/__init__.py +40 -0
  183. v0/relationalai/early_access/dsl/types/concepts.py +12 -0
  184. v0/relationalai/early_access/dsl/types/entities.py +135 -0
  185. v0/relationalai/early_access/dsl/types/values.py +17 -0
  186. v0/relationalai/early_access/dsl/utils.py +102 -0
  187. v0/relationalai/early_access/graphs/__init__.py +13 -0
  188. v0/relationalai/early_access/lqp/__init__.py +12 -0
  189. v0/relationalai/early_access/lqp/compiler/__init__.py +12 -0
  190. v0/relationalai/early_access/lqp/constructors/__init__.py +18 -0
  191. v0/relationalai/early_access/lqp/executor/__init__.py +12 -0
  192. v0/relationalai/early_access/lqp/ir/__init__.py +12 -0
  193. v0/relationalai/early_access/lqp/passes/__init__.py +12 -0
  194. v0/relationalai/early_access/lqp/pragmas/__init__.py +12 -0
  195. v0/relationalai/early_access/lqp/primitives/__init__.py +12 -0
  196. v0/relationalai/early_access/lqp/types/__init__.py +12 -0
  197. v0/relationalai/early_access/lqp/utils/__init__.py +12 -0
  198. v0/relationalai/early_access/lqp/validators/__init__.py +12 -0
  199. v0/relationalai/early_access/metamodel/__init__.py +58 -0
  200. v0/relationalai/early_access/metamodel/builtins/__init__.py +12 -0
  201. v0/relationalai/early_access/metamodel/compiler/__init__.py +12 -0
  202. v0/relationalai/early_access/metamodel/dependency/__init__.py +12 -0
  203. v0/relationalai/early_access/metamodel/factory/__init__.py +17 -0
  204. v0/relationalai/early_access/metamodel/helpers/__init__.py +12 -0
  205. v0/relationalai/early_access/metamodel/ir/__init__.py +14 -0
  206. v0/relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
  207. v0/relationalai/early_access/metamodel/typer/__init__.py +3 -0
  208. v0/relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
  209. v0/relationalai/early_access/metamodel/types/__init__.py +15 -0
  210. v0/relationalai/early_access/metamodel/util/__init__.py +15 -0
  211. v0/relationalai/early_access/metamodel/visitor/__init__.py +12 -0
  212. v0/relationalai/early_access/rel/__init__.py +12 -0
  213. v0/relationalai/early_access/rel/executor/__init__.py +12 -0
  214. v0/relationalai/early_access/rel/rel_utils/__init__.py +12 -0
  215. v0/relationalai/early_access/rel/rewrite/__init__.py +7 -0
  216. v0/relationalai/early_access/solvers/__init__.py +19 -0
  217. v0/relationalai/early_access/sql/__init__.py +11 -0
  218. v0/relationalai/early_access/sql/executor/__init__.py +3 -0
  219. v0/relationalai/early_access/sql/rewrite/__init__.py +3 -0
  220. v0/relationalai/early_access/tests/logging/__init__.py +12 -0
  221. v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
  222. v0/relationalai/early_access/tests/utils/__init__.py +12 -0
  223. v0/relationalai/environments/__init__.py +35 -0
  224. v0/relationalai/environments/base.py +381 -0
  225. v0/relationalai/environments/colab.py +14 -0
  226. v0/relationalai/environments/generic.py +71 -0
  227. v0/relationalai/environments/ipython.py +68 -0
  228. v0/relationalai/environments/jupyter.py +9 -0
  229. v0/relationalai/environments/snowbook.py +169 -0
  230. v0/relationalai/errors.py +2455 -0
  231. v0/relationalai/experimental/SF.py +38 -0
  232. v0/relationalai/experimental/inspect.py +47 -0
  233. v0/relationalai/experimental/pathfinder/__init__.py +158 -0
  234. v0/relationalai/experimental/pathfinder/api.py +160 -0
  235. v0/relationalai/experimental/pathfinder/automaton.py +584 -0
  236. v0/relationalai/experimental/pathfinder/bridge.py +226 -0
  237. v0/relationalai/experimental/pathfinder/compiler.py +416 -0
  238. v0/relationalai/experimental/pathfinder/datalog.py +214 -0
  239. v0/relationalai/experimental/pathfinder/diagnostics.py +56 -0
  240. v0/relationalai/experimental/pathfinder/filter.py +236 -0
  241. v0/relationalai/experimental/pathfinder/glushkov.py +439 -0
  242. v0/relationalai/experimental/pathfinder/options.py +265 -0
  243. v0/relationalai/experimental/pathfinder/rpq.py +344 -0
  244. v0/relationalai/experimental/pathfinder/transition.py +200 -0
  245. v0/relationalai/experimental/pathfinder/utils.py +26 -0
  246. v0/relationalai/experimental/paths/api.py +143 -0
  247. v0/relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
  248. v0/relationalai/experimental/paths/examples/basic_example.py +40 -0
  249. v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
  250. v0/relationalai/experimental/paths/examples/movie_example.py +77 -0
  251. v0/relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
  252. v0/relationalai/experimental/paths/examples/paths_example.py +116 -0
  253. v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
  254. v0/relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
  255. v0/relationalai/experimental/paths/graph.py +185 -0
  256. v0/relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
  257. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
  258. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
  259. v0/relationalai/experimental/paths/path_algorithms/single.py +59 -0
  260. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
  261. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
  262. v0/relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
  263. v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
  264. v0/relationalai/experimental/paths/path_algorithms/usp.py +150 -0
  265. v0/relationalai/experimental/paths/product_graph.py +93 -0
  266. v0/relationalai/experimental/paths/rpq/automaton.py +584 -0
  267. v0/relationalai/experimental/paths/rpq/diagnostics.py +56 -0
  268. v0/relationalai/experimental/paths/rpq/rpq.py +378 -0
  269. v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
  270. v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
  271. v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
  272. v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
  273. v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
  274. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
  275. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
  276. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
  277. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
  278. v0/relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
  279. v0/relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
  280. v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
  281. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
  282. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
  283. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
  284. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
  285. v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
  286. v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
  287. v0/relationalai/experimental/paths/tree_agg.py +168 -0
  288. v0/relationalai/experimental/paths/utilities/iterators.py +27 -0
  289. v0/relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
  290. v0/relationalai/experimental/solvers.py +1087 -0
  291. v0/relationalai/loaders/csv.py +195 -0
  292. v0/relationalai/loaders/loader.py +177 -0
  293. v0/relationalai/loaders/types.py +23 -0
  294. v0/relationalai/rel_emitter.py +373 -0
  295. v0/relationalai/rel_utils.py +185 -0
  296. v0/relationalai/semantics/__init__.py +29 -0
  297. v0/relationalai/semantics/devtools/benchmark_lqp.py +536 -0
  298. v0/relationalai/semantics/devtools/compilation_manager.py +294 -0
  299. v0/relationalai/semantics/devtools/extract_lqp.py +110 -0
  300. v0/relationalai/semantics/internal/internal.py +3785 -0
  301. v0/relationalai/semantics/internal/snowflake.py +324 -0
  302. v0/relationalai/semantics/lqp/builtins.py +16 -0
  303. v0/relationalai/semantics/lqp/compiler.py +22 -0
  304. v0/relationalai/semantics/lqp/constructors.py +68 -0
  305. v0/relationalai/semantics/lqp/executor.py +469 -0
  306. v0/relationalai/semantics/lqp/intrinsics.py +24 -0
  307. v0/relationalai/semantics/lqp/ir.py +124 -0
  308. v0/relationalai/semantics/lqp/model2lqp.py +839 -0
  309. v0/relationalai/semantics/lqp/passes.py +680 -0
  310. v0/relationalai/semantics/lqp/primitives.py +252 -0
  311. v0/relationalai/semantics/lqp/result_helpers.py +202 -0
  312. v0/relationalai/semantics/lqp/rewrite/__init__.py +18 -0
  313. v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
  314. v0/relationalai/semantics/lqp/rewrite/cdc.py +216 -0
  315. v0/relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
  316. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +449 -0
  317. v0/relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
  318. v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
  319. v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
  320. v0/relationalai/semantics/lqp/rewrite/splinter.py +76 -0
  321. v0/relationalai/semantics/lqp/types.py +101 -0
  322. v0/relationalai/semantics/lqp/utils.py +160 -0
  323. v0/relationalai/semantics/lqp/validators.py +57 -0
  324. v0/relationalai/semantics/metamodel/__init__.py +40 -0
  325. v0/relationalai/semantics/metamodel/builtins.py +774 -0
  326. v0/relationalai/semantics/metamodel/compiler.py +133 -0
  327. v0/relationalai/semantics/metamodel/dependency.py +862 -0
  328. v0/relationalai/semantics/metamodel/executor.py +61 -0
  329. v0/relationalai/semantics/metamodel/factory.py +287 -0
  330. v0/relationalai/semantics/metamodel/helpers.py +361 -0
  331. v0/relationalai/semantics/metamodel/ir.py +923 -0
  332. v0/relationalai/semantics/metamodel/rewrite/__init__.py +7 -0
  333. v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
  334. v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
  335. v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
  336. v0/relationalai/semantics/metamodel/rewrite/flatten.py +549 -0
  337. v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
  338. v0/relationalai/semantics/metamodel/typer/checker.py +353 -0
  339. v0/relationalai/semantics/metamodel/typer/typer.py +1395 -0
  340. v0/relationalai/semantics/metamodel/util.py +505 -0
  341. v0/relationalai/semantics/metamodel/visitor.py +944 -0
  342. v0/relationalai/semantics/reasoners/__init__.py +10 -0
  343. v0/relationalai/semantics/reasoners/graph/__init__.py +37 -0
  344. v0/relationalai/semantics/reasoners/graph/core.py +9020 -0
  345. v0/relationalai/semantics/reasoners/optimization/__init__.py +68 -0
  346. v0/relationalai/semantics/reasoners/optimization/common.py +88 -0
  347. v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
  348. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +1163 -0
  349. v0/relationalai/semantics/rel/builtins.py +40 -0
  350. v0/relationalai/semantics/rel/compiler.py +989 -0
  351. v0/relationalai/semantics/rel/executor.py +359 -0
  352. v0/relationalai/semantics/rel/rel.py +482 -0
  353. v0/relationalai/semantics/rel/rel_utils.py +276 -0
  354. v0/relationalai/semantics/snowflake/__init__.py +3 -0
  355. v0/relationalai/semantics/sql/compiler.py +2503 -0
  356. v0/relationalai/semantics/sql/executor/duck_db.py +52 -0
  357. v0/relationalai/semantics/sql/executor/result_helpers.py +64 -0
  358. v0/relationalai/semantics/sql/executor/snowflake.py +145 -0
  359. v0/relationalai/semantics/sql/rewrite/denormalize.py +222 -0
  360. v0/relationalai/semantics/sql/rewrite/double_negation.py +49 -0
  361. v0/relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
  362. v0/relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
  363. v0/relationalai/semantics/sql/sql.py +504 -0
  364. v0/relationalai/semantics/std/__init__.py +54 -0
  365. v0/relationalai/semantics/std/constraints.py +43 -0
  366. v0/relationalai/semantics/std/datetime.py +363 -0
  367. v0/relationalai/semantics/std/decimals.py +62 -0
  368. v0/relationalai/semantics/std/floats.py +7 -0
  369. v0/relationalai/semantics/std/integers.py +22 -0
  370. v0/relationalai/semantics/std/math.py +141 -0
  371. v0/relationalai/semantics/std/pragmas.py +11 -0
  372. v0/relationalai/semantics/std/re.py +83 -0
  373. v0/relationalai/semantics/std/std.py +14 -0
  374. v0/relationalai/semantics/std/strings.py +63 -0
  375. v0/relationalai/semantics/tests/__init__.py +0 -0
  376. v0/relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
  377. v0/relationalai/semantics/tests/test_snapshot_base.py +9 -0
  378. v0/relationalai/semantics/tests/utils.py +46 -0
  379. v0/relationalai/std/__init__.py +70 -0
  380. v0/relationalai/tools/__init__.py +0 -0
  381. v0/relationalai/tools/cli.py +1940 -0
  382. v0/relationalai/tools/cli_controls.py +1826 -0
  383. v0/relationalai/tools/cli_helpers.py +390 -0
  384. v0/relationalai/tools/debugger.py +183 -0
  385. v0/relationalai/tools/debugger_client.py +109 -0
  386. v0/relationalai/tools/debugger_server.py +302 -0
  387. v0/relationalai/tools/dev.py +685 -0
  388. v0/relationalai/tools/qb_debugger.py +425 -0
  389. v0/relationalai/util/clean_up_databases.py +95 -0
  390. v0/relationalai/util/format.py +123 -0
  391. v0/relationalai/util/list_databases.py +9 -0
  392. v0/relationalai/util/otel_configuration.py +25 -0
  393. v0/relationalai/util/otel_handler.py +484 -0
  394. v0/relationalai/util/snowflake_handler.py +88 -0
  395. v0/relationalai/util/span_format_test.py +43 -0
  396. v0/relationalai/util/span_tracker.py +207 -0
  397. v0/relationalai/util/spans_file_handler.py +72 -0
  398. v0/relationalai/util/tracing_handler.py +34 -0
  399. frontend/debugger/dist/.gitignore +0 -2
  400. frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
  401. frontend/debugger/dist/assets/index-Cssla-O7.js +0 -208
  402. frontend/debugger/dist/assets/index-DlHsYx1V.css +0 -9
  403. frontend/debugger/dist/index.html +0 -17
  404. relationalai/clients/__init__.py +0 -18
  405. relationalai/clients/client.py +0 -946
  406. relationalai/clients/config.py +0 -673
  407. relationalai/clients/direct_access_client.py +0 -118
  408. relationalai/clients/exec_txn_poller.py +0 -153
  409. relationalai/clients/hash_util.py +0 -31
  410. relationalai/clients/local.py +0 -594
  411. relationalai/clients/profile_polling.py +0 -73
  412. relationalai/clients/resources/__init__.py +0 -8
  413. relationalai/clients/resources/azure/azure.py +0 -502
  414. relationalai/clients/resources/snowflake/__init__.py +0 -20
  415. relationalai/clients/resources/snowflake/cli_resources.py +0 -98
  416. relationalai/clients/resources/snowflake/direct_access_resources.py +0 -739
  417. relationalai/clients/resources/snowflake/engine_service.py +0 -381
  418. relationalai/clients/resources/snowflake/engine_state_handlers.py +0 -315
  419. relationalai/clients/resources/snowflake/error_handlers.py +0 -240
  420. relationalai/clients/resources/snowflake/export_procedure.py.jinja +0 -249
  421. relationalai/clients/resources/snowflake/resources_factory.py +0 -99
  422. relationalai/clients/resources/snowflake/snowflake.py +0 -3193
  423. relationalai/clients/resources/snowflake/use_index_poller.py +0 -1019
  424. relationalai/clients/resources/snowflake/use_index_resources.py +0 -188
  425. relationalai/clients/resources/snowflake/util.py +0 -387
  426. relationalai/clients/result_helpers.py +0 -420
  427. relationalai/clients/types.py +0 -118
  428. relationalai/clients/util.py +0 -356
  429. relationalai/debugging.py +0 -389
  430. relationalai/dsl.py +0 -1749
  431. relationalai/early_access/builder/__init__.py +0 -30
  432. relationalai/early_access/builder/builder/__init__.py +0 -35
  433. relationalai/early_access/builder/snowflake/__init__.py +0 -12
  434. relationalai/early_access/builder/std/__init__.py +0 -25
  435. relationalai/early_access/builder/std/decimals/__init__.py +0 -12
  436. relationalai/early_access/builder/std/integers/__init__.py +0 -12
  437. relationalai/early_access/builder/std/math/__init__.py +0 -12
  438. relationalai/early_access/builder/std/strings/__init__.py +0 -14
  439. relationalai/early_access/devtools/__init__.py +0 -12
  440. relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
  441. relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
  442. relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
  443. relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
  444. relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
  445. relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
  446. relationalai/early_access/dsl/bindings/common.py +0 -402
  447. relationalai/early_access/dsl/bindings/csv.py +0 -170
  448. relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
  449. relationalai/early_access/dsl/bindings/snowflake.py +0 -64
  450. relationalai/early_access/dsl/codegen/binder.py +0 -411
  451. relationalai/early_access/dsl/codegen/common.py +0 -79
  452. relationalai/early_access/dsl/codegen/helpers.py +0 -23
  453. relationalai/early_access/dsl/codegen/relations.py +0 -700
  454. relationalai/early_access/dsl/codegen/weaver.py +0 -417
  455. relationalai/early_access/dsl/core/builders/__init__.py +0 -47
  456. relationalai/early_access/dsl/core/builders/logic.py +0 -19
  457. relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
  458. relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
  459. relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
  460. relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
  461. relationalai/early_access/dsl/core/context.py +0 -13
  462. relationalai/early_access/dsl/core/cset.py +0 -132
  463. relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
  464. relationalai/early_access/dsl/core/exprs/relational.py +0 -18
  465. relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
  466. relationalai/early_access/dsl/core/instances.py +0 -44
  467. relationalai/early_access/dsl/core/logic/__init__.py +0 -193
  468. relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
  469. relationalai/early_access/dsl/core/logic/exists.py +0 -223
  470. relationalai/early_access/dsl/core/logic/helper.py +0 -163
  471. relationalai/early_access/dsl/core/namespaces.py +0 -32
  472. relationalai/early_access/dsl/core/relations.py +0 -276
  473. relationalai/early_access/dsl/core/rules.py +0 -112
  474. relationalai/early_access/dsl/core/std/__init__.py +0 -45
  475. relationalai/early_access/dsl/core/temporal/recall.py +0 -6
  476. relationalai/early_access/dsl/core/types/__init__.py +0 -270
  477. relationalai/early_access/dsl/core/types/concepts.py +0 -128
  478. relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
  479. relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
  480. relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
  481. relationalai/early_access/dsl/core/types/standard.py +0 -92
  482. relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
  483. relationalai/early_access/dsl/core/types/variables.py +0 -203
  484. relationalai/early_access/dsl/ir/compiler.py +0 -318
  485. relationalai/early_access/dsl/ir/executor.py +0 -260
  486. relationalai/early_access/dsl/ontologies/constraints.py +0 -88
  487. relationalai/early_access/dsl/ontologies/export.py +0 -30
  488. relationalai/early_access/dsl/ontologies/models.py +0 -453
  489. relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
  490. relationalai/early_access/dsl/ontologies/readings.py +0 -60
  491. relationalai/early_access/dsl/ontologies/relationships.py +0 -322
  492. relationalai/early_access/dsl/ontologies/roles.py +0 -87
  493. relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
  494. relationalai/early_access/dsl/orm/constraints.py +0 -438
  495. relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
  496. relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
  497. relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
  498. relationalai/early_access/dsl/orm/measures/measures.py +0 -299
  499. relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
  500. relationalai/early_access/dsl/orm/models.py +0 -256
  501. relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
  502. relationalai/early_access/dsl/orm/printer.py +0 -469
  503. relationalai/early_access/dsl/orm/reasoners.py +0 -480
  504. relationalai/early_access/dsl/orm/relations.py +0 -19
  505. relationalai/early_access/dsl/orm/relationships.py +0 -251
  506. relationalai/early_access/dsl/orm/types.py +0 -42
  507. relationalai/early_access/dsl/orm/utils.py +0 -79
  508. relationalai/early_access/dsl/orm/verb.py +0 -204
  509. relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
  510. relationalai/early_access/dsl/relations.py +0 -170
  511. relationalai/early_access/dsl/rulesets.py +0 -69
  512. relationalai/early_access/dsl/schemas/__init__.py +0 -450
  513. relationalai/early_access/dsl/schemas/builder.py +0 -48
  514. relationalai/early_access/dsl/schemas/comp_names.py +0 -51
  515. relationalai/early_access/dsl/schemas/components.py +0 -203
  516. relationalai/early_access/dsl/schemas/contexts.py +0 -156
  517. relationalai/early_access/dsl/schemas/exprs.py +0 -89
  518. relationalai/early_access/dsl/schemas/fragments.py +0 -464
  519. relationalai/early_access/dsl/serialization.py +0 -79
  520. relationalai/early_access/dsl/serialize/exporter.py +0 -163
  521. relationalai/early_access/dsl/snow/api.py +0 -105
  522. relationalai/early_access/dsl/snow/common.py +0 -76
  523. relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
  524. relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
  525. relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
  526. relationalai/early_access/dsl/types/__init__.py +0 -40
  527. relationalai/early_access/dsl/types/concepts.py +0 -12
  528. relationalai/early_access/dsl/types/entities.py +0 -135
  529. relationalai/early_access/dsl/types/values.py +0 -17
  530. relationalai/early_access/dsl/utils.py +0 -102
  531. relationalai/early_access/graphs/__init__.py +0 -13
  532. relationalai/early_access/lqp/__init__.py +0 -12
  533. relationalai/early_access/lqp/compiler/__init__.py +0 -12
  534. relationalai/early_access/lqp/constructors/__init__.py +0 -18
  535. relationalai/early_access/lqp/executor/__init__.py +0 -12
  536. relationalai/early_access/lqp/ir/__init__.py +0 -12
  537. relationalai/early_access/lqp/passes/__init__.py +0 -12
  538. relationalai/early_access/lqp/pragmas/__init__.py +0 -12
  539. relationalai/early_access/lqp/primitives/__init__.py +0 -12
  540. relationalai/early_access/lqp/types/__init__.py +0 -12
  541. relationalai/early_access/lqp/utils/__init__.py +0 -12
  542. relationalai/early_access/lqp/validators/__init__.py +0 -12
  543. relationalai/early_access/metamodel/__init__.py +0 -58
  544. relationalai/early_access/metamodel/builtins/__init__.py +0 -12
  545. relationalai/early_access/metamodel/compiler/__init__.py +0 -12
  546. relationalai/early_access/metamodel/dependency/__init__.py +0 -12
  547. relationalai/early_access/metamodel/factory/__init__.py +0 -17
  548. relationalai/early_access/metamodel/helpers/__init__.py +0 -12
  549. relationalai/early_access/metamodel/ir/__init__.py +0 -14
  550. relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
  551. relationalai/early_access/metamodel/typer/__init__.py +0 -3
  552. relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
  553. relationalai/early_access/metamodel/types/__init__.py +0 -15
  554. relationalai/early_access/metamodel/util/__init__.py +0 -15
  555. relationalai/early_access/metamodel/visitor/__init__.py +0 -12
  556. relationalai/early_access/rel/__init__.py +0 -12
  557. relationalai/early_access/rel/executor/__init__.py +0 -12
  558. relationalai/early_access/rel/rel_utils/__init__.py +0 -12
  559. relationalai/early_access/rel/rewrite/__init__.py +0 -7
  560. relationalai/early_access/solvers/__init__.py +0 -19
  561. relationalai/early_access/sql/__init__.py +0 -11
  562. relationalai/early_access/sql/executor/__init__.py +0 -3
  563. relationalai/early_access/sql/rewrite/__init__.py +0 -3
  564. relationalai/early_access/tests/logging/__init__.py +0 -12
  565. relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
  566. relationalai/early_access/tests/utils/__init__.py +0 -12
  567. relationalai/environments/__init__.py +0 -35
  568. relationalai/environments/base.py +0 -381
  569. relationalai/environments/colab.py +0 -14
  570. relationalai/environments/generic.py +0 -71
  571. relationalai/environments/ipython.py +0 -68
  572. relationalai/environments/jupyter.py +0 -9
  573. relationalai/environments/snowbook.py +0 -169
  574. relationalai/errors.py +0 -2496
  575. relationalai/experimental/SF.py +0 -38
  576. relationalai/experimental/inspect.py +0 -47
  577. relationalai/experimental/pathfinder/__init__.py +0 -158
  578. relationalai/experimental/pathfinder/api.py +0 -160
  579. relationalai/experimental/pathfinder/automaton.py +0 -584
  580. relationalai/experimental/pathfinder/bridge.py +0 -226
  581. relationalai/experimental/pathfinder/compiler.py +0 -416
  582. relationalai/experimental/pathfinder/datalog.py +0 -214
  583. relationalai/experimental/pathfinder/diagnostics.py +0 -56
  584. relationalai/experimental/pathfinder/filter.py +0 -236
  585. relationalai/experimental/pathfinder/glushkov.py +0 -439
  586. relationalai/experimental/pathfinder/options.py +0 -265
  587. relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +0 -1951
  588. relationalai/experimental/pathfinder/rpq.py +0 -344
  589. relationalai/experimental/pathfinder/transition.py +0 -200
  590. relationalai/experimental/pathfinder/utils.py +0 -26
  591. relationalai/experimental/paths/README.md +0 -107
  592. relationalai/experimental/paths/api.py +0 -143
  593. relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
  594. relationalai/experimental/paths/code_organization.md +0 -2
  595. relationalai/experimental/paths/examples/Movies.ipynb +0 -16328
  596. relationalai/experimental/paths/examples/basic_example.py +0 -40
  597. relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
  598. relationalai/experimental/paths/examples/movie_example.py +0 -77
  599. relationalai/experimental/paths/examples/movies_data/actedin.csv +0 -193
  600. relationalai/experimental/paths/examples/movies_data/directed.csv +0 -45
  601. relationalai/experimental/paths/examples/movies_data/follows.csv +0 -7
  602. relationalai/experimental/paths/examples/movies_data/movies.csv +0 -39
  603. relationalai/experimental/paths/examples/movies_data/person.csv +0 -134
  604. relationalai/experimental/paths/examples/movies_data/produced.csv +0 -16
  605. relationalai/experimental/paths/examples/movies_data/ratings.csv +0 -10
  606. relationalai/experimental/paths/examples/movies_data/wrote.csv +0 -11
  607. relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
  608. relationalai/experimental/paths/examples/paths_example.py +0 -116
  609. relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
  610. relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
  611. relationalai/experimental/paths/graph.py +0 -185
  612. relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
  613. relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
  614. relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
  615. relationalai/experimental/paths/path_algorithms/single.py +0 -59
  616. relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
  617. relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
  618. relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
  619. relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
  620. relationalai/experimental/paths/path_algorithms/usp.py +0 -150
  621. relationalai/experimental/paths/product_graph.py +0 -93
  622. relationalai/experimental/paths/rpq/automaton.py +0 -584
  623. relationalai/experimental/paths/rpq/diagnostics.py +0 -56
  624. relationalai/experimental/paths/rpq/rpq.py +0 -378
  625. relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
  626. relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
  627. relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
  628. relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
  629. relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
  630. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
  631. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
  632. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
  633. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
  634. relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
  635. relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
  636. relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
  637. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
  638. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
  639. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
  640. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
  641. relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
  642. relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
  643. relationalai/experimental/paths/tree_agg.py +0 -168
  644. relationalai/experimental/paths/utilities/iterators.py +0 -27
  645. relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
  646. relationalai/experimental/solvers.py +0 -1095
  647. relationalai/loaders/csv.py +0 -195
  648. relationalai/loaders/loader.py +0 -177
  649. relationalai/loaders/types.py +0 -23
  650. relationalai/rel_emitter.py +0 -373
  651. relationalai/rel_utils.py +0 -185
  652. relationalai/semantics/designs/query_builder/identify_by.md +0 -106
  653. relationalai/semantics/devtools/benchmark_lqp.py +0 -535
  654. relationalai/semantics/devtools/compilation_manager.py +0 -294
  655. relationalai/semantics/devtools/extract_lqp.py +0 -110
  656. relationalai/semantics/internal/internal.py +0 -3785
  657. relationalai/semantics/internal/snowflake.py +0 -329
  658. relationalai/semantics/lqp/README.md +0 -34
  659. relationalai/semantics/lqp/algorithms.py +0 -173
  660. relationalai/semantics/lqp/builtins.py +0 -213
  661. relationalai/semantics/lqp/compiler.py +0 -22
  662. relationalai/semantics/lqp/constructors.py +0 -68
  663. relationalai/semantics/lqp/executor.py +0 -518
  664. relationalai/semantics/lqp/export_rewriter.py +0 -40
  665. relationalai/semantics/lqp/intrinsics.py +0 -24
  666. relationalai/semantics/lqp/ir.py +0 -150
  667. relationalai/semantics/lqp/model2lqp.py +0 -1056
  668. relationalai/semantics/lqp/passes.py +0 -38
  669. relationalai/semantics/lqp/primitives.py +0 -252
  670. relationalai/semantics/lqp/result_helpers.py +0 -266
  671. relationalai/semantics/lqp/rewrite/__init__.py +0 -32
  672. relationalai/semantics/lqp/rewrite/algorithm.py +0 -385
  673. relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -69
  674. relationalai/semantics/lqp/rewrite/cdc.py +0 -216
  675. relationalai/semantics/lqp/rewrite/constants_to_vars.py +0 -70
  676. relationalai/semantics/lqp/rewrite/deduplicate_vars.py +0 -104
  677. relationalai/semantics/lqp/rewrite/eliminate_data.py +0 -108
  678. relationalai/semantics/lqp/rewrite/extract_common.py +0 -340
  679. relationalai/semantics/lqp/rewrite/extract_keys.py +0 -577
  680. relationalai/semantics/lqp/rewrite/flatten_script.py +0 -301
  681. relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
  682. relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -348
  683. relationalai/semantics/lqp/rewrite/period_math.py +0 -77
  684. relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -339
  685. relationalai/semantics/lqp/rewrite/splinter.py +0 -76
  686. relationalai/semantics/lqp/rewrite/unify_definitions.py +0 -323
  687. relationalai/semantics/lqp/types.py +0 -101
  688. relationalai/semantics/lqp/utils.py +0 -170
  689. relationalai/semantics/lqp/validators.py +0 -70
  690. relationalai/semantics/metamodel/compiler.py +0 -134
  691. relationalai/semantics/metamodel/dependency.py +0 -880
  692. relationalai/semantics/metamodel/executor.py +0 -78
  693. relationalai/semantics/metamodel/factory.py +0 -287
  694. relationalai/semantics/metamodel/helpers.py +0 -368
  695. relationalai/semantics/metamodel/ir.py +0 -924
  696. relationalai/semantics/metamodel/rewrite/__init__.py +0 -8
  697. relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
  698. relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -220
  699. relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
  700. relationalai/semantics/metamodel/rewrite/flatten.py +0 -590
  701. relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -256
  702. relationalai/semantics/metamodel/rewrite/handle_aggregations_and_ranks.py +0 -237
  703. relationalai/semantics/metamodel/typer/checker.py +0 -355
  704. relationalai/semantics/metamodel/typer/typer.py +0 -1396
  705. relationalai/semantics/metamodel/util.py +0 -506
  706. relationalai/semantics/metamodel/visitor.py +0 -945
  707. relationalai/semantics/reasoners/__init__.py +0 -10
  708. relationalai/semantics/reasoners/graph/README.md +0 -620
  709. relationalai/semantics/reasoners/graph/__init__.py +0 -37
  710. relationalai/semantics/reasoners/graph/core.py +0 -9019
  711. relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +0 -797
  712. relationalai/semantics/reasoners/graph/tests/README.md +0 -21
  713. relationalai/semantics/reasoners/optimization/__init__.py +0 -68
  714. relationalai/semantics/reasoners/optimization/common.py +0 -88
  715. relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
  716. relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1407
  717. relationalai/semantics/rel/builtins.py +0 -40
  718. relationalai/semantics/rel/compiler.py +0 -994
  719. relationalai/semantics/rel/executor.py +0 -363
  720. relationalai/semantics/rel/rel.py +0 -482
  721. relationalai/semantics/rel/rel_utils.py +0 -276
  722. relationalai/semantics/snowflake/__init__.py +0 -3
  723. relationalai/semantics/sql/compiler.py +0 -2503
  724. relationalai/semantics/sql/executor/duck_db.py +0 -52
  725. relationalai/semantics/sql/executor/result_helpers.py +0 -64
  726. relationalai/semantics/sql/executor/snowflake.py +0 -149
  727. relationalai/semantics/sql/rewrite/denormalize.py +0 -222
  728. relationalai/semantics/sql/rewrite/double_negation.py +0 -49
  729. relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
  730. relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
  731. relationalai/semantics/sql/sql.py +0 -504
  732. relationalai/semantics/std/pragmas.py +0 -11
  733. relationalai/semantics/std/std.py +0 -14
  734. relationalai/semantics/tests/lqp/algorithms.py +0 -345
  735. relationalai/semantics/tests/test_snapshot_abstract.py +0 -144
  736. relationalai/semantics/tests/test_snapshot_base.py +0 -9
  737. relationalai/semantics/tests/utils.py +0 -46
  738. relationalai/std/__init__.py +0 -70
  739. relationalai/tools/cli.py +0 -2089
  740. relationalai/tools/cli_controls.py +0 -1975
  741. relationalai/tools/cli_helpers.py +0 -802
  742. relationalai/tools/debugger_client.py +0 -109
  743. relationalai/tools/debugger_server.py +0 -302
  744. relationalai/tools/dev.py +0 -685
  745. relationalai/tools/notes +0 -7
  746. relationalai/tools/qb_debugger.py +0 -425
  747. relationalai/tools/txn_progress.py +0 -188
  748. relationalai/util/clean_up_databases.py +0 -95
  749. relationalai/util/list_databases.py +0 -9
  750. relationalai/util/otel_configuration.py +0 -26
  751. relationalai/util/otel_handler.py +0 -484
  752. relationalai/util/snowflake_handler.py +0 -88
  753. relationalai/util/span_format_test.py +0 -43
  754. relationalai/util/span_tracker.py +0 -207
  755. relationalai/util/spans_file_handler.py +0 -72
  756. relationalai/util/tracing_handler.py +0 -34
  757. relationalai-0.13.5.dist-info/METADATA +0 -74
  758. relationalai-0.13.5.dist-info/RECORD +0 -473
  759. relationalai-0.13.5.dist-info/WHEEL +0 -4
  760. relationalai-0.13.5.dist-info/entry_points.txt +0 -3
  761. relationalai-0.13.5.dist-info/licenses/LICENSE +0 -202
  762. relationalai_test_util/__init__.py +0 -4
  763. relationalai_test_util/fixtures.py +0 -233
  764. relationalai_test_util/snapshot.py +0 -252
  765. relationalai_test_util/traceback.py +0 -118
  766. /relationalai/{analysis → semantics/frontend}/__init__.py +0 -0
  767. /relationalai/{auth/__init__.py → semantics/metamodel/metamodel_compiler.py} +0 -0
  768. /relationalai/{early_access → shims}/__init__.py +0 -0
  769. {relationalai/early_access/dsl/adapters → v0/relationalai/analysis}/__init__.py +0 -0
  770. {relationalai → v0/relationalai}/analysis/mechanistic.py +0 -0
  771. {relationalai → v0/relationalai}/analysis/whynot.py +0 -0
  772. {relationalai/early_access/dsl/adapters/orm → v0/relationalai/auth}/__init__.py +0 -0
  773. {relationalai → v0/relationalai}/auth/jwt_generator.py +0 -0
  774. {relationalai → v0/relationalai}/auth/oauth_callback_server.py +0 -0
  775. {relationalai → v0/relationalai}/auth/token_handler.py +0 -0
  776. {relationalai → v0/relationalai}/auth/util.py +0 -0
  777. {relationalai/clients/resources/snowflake → v0/relationalai/clients}/cache_store.py +0 -0
  778. {relationalai → v0/relationalai}/compiler.py +0 -0
  779. {relationalai → v0/relationalai}/dependencies.py +0 -0
  780. {relationalai → v0/relationalai}/docutils.py +0 -0
  781. {relationalai/early_access/dsl/adapters/owl → v0/relationalai/early_access}/__init__.py +0 -0
  782. {relationalai → v0/relationalai}/early_access/dsl/__init__.py +0 -0
  783. {relationalai/early_access/dsl/bindings → v0/relationalai/early_access/dsl/adapters}/__init__.py +0 -0
  784. {relationalai/early_access/dsl/bindings/legacy → v0/relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
  785. {relationalai → v0/relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
  786. {relationalai/early_access/dsl/codegen → v0/relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
  787. {relationalai → v0/relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
  788. {relationalai/early_access/dsl/core/temporal → v0/relationalai/early_access/dsl/bindings}/__init__.py +0 -0
  789. {relationalai/early_access/dsl/ir → v0/relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
  790. {relationalai/early_access/dsl/ontologies → v0/relationalai/early_access/dsl/codegen}/__init__.py +0 -0
  791. {relationalai → v0/relationalai}/early_access/dsl/constants.py +0 -0
  792. {relationalai → v0/relationalai}/early_access/dsl/core/__init__.py +0 -0
  793. {relationalai → v0/relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
  794. {relationalai → v0/relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
  795. {relationalai → v0/relationalai}/early_access/dsl/core/stack.py +0 -0
  796. {relationalai/early_access/dsl/orm → v0/relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
  797. {relationalai → v0/relationalai}/early_access/dsl/core/utils.py +0 -0
  798. {relationalai/early_access/dsl/orm/measures → v0/relationalai/early_access/dsl/ir}/__init__.py +0 -0
  799. {relationalai/early_access/dsl/physical_metadata → v0/relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
  800. {relationalai → v0/relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
  801. {relationalai/early_access/dsl/serialize → v0/relationalai/early_access/dsl/orm}/__init__.py +0 -0
  802. {relationalai/early_access/dsl/snow → v0/relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
  803. {relationalai → v0/relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
  804. {relationalai/loaders → v0/relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
  805. {relationalai/semantics/tests → v0/relationalai/early_access/dsl/serialize}/__init__.py +0 -0
  806. {relationalai → v0/relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
  807. {relationalai → v0/relationalai}/early_access/dsl/serialize/model.py +0 -0
  808. {relationalai/semantics/tests/lqp → v0/relationalai/early_access/dsl/snow}/__init__.py +0 -0
  809. {relationalai → v0/relationalai}/early_access/tests/__init__.py +0 -0
  810. {relationalai → v0/relationalai}/environments/ci.py +0 -0
  811. {relationalai → v0/relationalai}/environments/hex.py +0 -0
  812. {relationalai → v0/relationalai}/environments/terminal.py +0 -0
  813. {relationalai → v0/relationalai}/experimental/__init__.py +0 -0
  814. {relationalai → v0/relationalai}/experimental/graphs.py +0 -0
  815. {relationalai → v0/relationalai}/experimental/paths/__init__.py +0 -0
  816. {relationalai → v0/relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
  817. {relationalai → v0/relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
  818. {relationalai → v0/relationalai}/experimental/paths/rpq/__init__.py +0 -0
  819. {relationalai → v0/relationalai}/experimental/paths/rpq/filter.py +0 -0
  820. {relationalai → v0/relationalai}/experimental/paths/rpq/glushkov.py +0 -0
  821. {relationalai → v0/relationalai}/experimental/paths/rpq/transition.py +0 -0
  822. {relationalai → v0/relationalai}/experimental/paths/utilities/__init__.py +0 -0
  823. {relationalai → v0/relationalai}/experimental/paths/utilities/utilities.py +0 -0
  824. {relationalai/tools → v0/relationalai/loaders}/__init__.py +0 -0
  825. {relationalai → v0/relationalai}/metagen.py +0 -0
  826. {relationalai → v0/relationalai}/metamodel.py +0 -0
  827. {relationalai → v0/relationalai}/rel.py +0 -0
  828. {relationalai → v0/relationalai}/semantics/devtools/__init__.py +0 -0
  829. {relationalai → v0/relationalai}/semantics/internal/__init__.py +0 -0
  830. {relationalai → v0/relationalai}/semantics/internal/annotations.py +0 -0
  831. {relationalai → v0/relationalai}/semantics/lqp/__init__.py +0 -0
  832. {relationalai → v0/relationalai}/semantics/lqp/pragmas.py +0 -0
  833. {relationalai → v0/relationalai}/semantics/metamodel/dataflow.py +0 -0
  834. {relationalai → v0/relationalai}/semantics/metamodel/typer/__init__.py +0 -0
  835. {relationalai → v0/relationalai}/semantics/metamodel/types.py +0 -0
  836. {relationalai → v0/relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
  837. {relationalai → v0/relationalai}/semantics/rel/__init__.py +0 -0
  838. {relationalai → v0/relationalai}/semantics/sql/__init__.py +0 -0
  839. {relationalai → v0/relationalai}/semantics/sql/executor/__init__.py +0 -0
  840. {relationalai → v0/relationalai}/semantics/sql/rewrite/__init__.py +0 -0
  841. {relationalai → v0/relationalai}/semantics/tests/logging.py +0 -0
  842. {relationalai → v0/relationalai}/std/aggregates.py +0 -0
  843. {relationalai → v0/relationalai}/std/dates.py +0 -0
  844. {relationalai → v0/relationalai}/std/graphs.py +0 -0
  845. {relationalai → v0/relationalai}/std/inspect.py +0 -0
  846. {relationalai → v0/relationalai}/std/math.py +0 -0
  847. {relationalai → v0/relationalai}/std/re.py +0 -0
  848. {relationalai → v0/relationalai}/std/strings.py +0 -0
  849. {relationalai → v0/relationalai}/tools/cleanup_snapshots.py +0 -0
  850. {relationalai → v0/relationalai}/tools/constants.py +0 -0
  851. {relationalai → v0/relationalai}/tools/query_utils.py +0 -0
  852. {relationalai → v0/relationalai}/tools/snapshot_viewer.py +0 -0
  853. {relationalai → v0/relationalai}/util/__init__.py +0 -0
  854. {relationalai → v0/relationalai}/util/constants.py +0 -0
  855. {relationalai → v0/relationalai}/util/graph.py +0 -0
  856. {relationalai → v0/relationalai}/util/timeout.py +0 -0
@@ -0,0 +1,1186 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import defaultdict
4
+ from dataclasses import dataclass
5
+ from typing import Optional, Union, Iterable, TypeVar, Tuple
6
+
7
+ from ...util import tracing
8
+ from ...util.error import err, exc, Source, Part
9
+ from ...util.naming import sanitize
10
+ from ...util.structures import OrderedSet
11
+
12
+ from . import metamodel as mm, builtins as bt
13
+ from .rewriter import Walker, Rewriter, NO_WALK
14
+ from .builtins import builtins as b
15
+
16
+ #--------------------------------------------------
17
+ # Typer
18
+ #--------------------------------------------------
19
+
20
+ T = TypeVar("T", bound=mm.Model | mm.Task)
21
+
22
+ class Typer:
23
+ def __init__(self, enforce=False):
24
+ self.enforce = enforce
25
+ self.model_net: Optional[PropagationNetwork] = None
26
+ # TODO: remove this once we are using spans for the debugger
27
+ self.last_net: Optional[PropagationNetwork] = None
28
+
29
+ def infer_model(self, model: mm.Model) -> mm.Model:
30
+ """
31
+ Infer types for the given model, returning a new model with updated types and
32
+ storing the results within this typer so that subsequent calls can reuse them.
33
+ """
34
+ # create a brand new network with data from this model
35
+ self.model_net = PropagationNetwork(model)
36
+ return self._infer(model, self.model_net)
37
+
38
+ def infer_query(self, query: mm.Task) -> mm.Task:
39
+ """
40
+ Infer types for the given query, returning a new query with updated types. If the
41
+ typer was used to infer a model previously, the information from that model analysis
42
+ will be reused.
43
+ """
44
+ if self.model_net is not None:
45
+ net = PropagationNetwork(self.model_net.model)
46
+ net.load_types(self.model_net.resolved_types)
47
+ else:
48
+ net = PropagationNetwork(mm.Model(root=query))
49
+ self.last_net = net
50
+ return self._infer(query, net)
51
+
52
+ #--------------------------------------------------
53
+ # Internal implementation
54
+ #--------------------------------------------------
55
+
56
+ def _infer(self, node: T, net: PropagationNetwork) -> T:
57
+ # build the propagation network by analyzing the model
58
+ with tracing.span("typer.analyze"):
59
+ Analyzer(net).analyze(node)
60
+
61
+ # propagate the types through the network
62
+ with tracing.span("typer.propagate") as span:
63
+ net.propagate()
64
+ # span["type_graph"] = net.to_mermaid()
65
+
66
+ # replace the fields in the model with the new types
67
+ with tracing.span("typer.replace"):
68
+ replacer = Replacer(net)
69
+ final = replacer.rewrite(node)
70
+
71
+ # report any errors found during typing
72
+ if net.errors:
73
+ for error in net.errors:
74
+ error.report()
75
+ if self.enforce:
76
+ exc("TyperError", "Type errors detected during type inference.")
77
+ return final
78
+
79
+
80
+ #--------------------------------------------------
81
+ # Propagation Network
82
+ #--------------------------------------------------
83
+
84
+ # The core idea of the typer is to build a propagation network where nodes
85
+ # are vars, fields, or overloaded lookups/updates/aggregates. The intuition
86
+ # is that _all_ types in the IR ultimately flow from relation fields, so if
87
+ # we figure those out we just need to propagate their types to unknown vars, which
88
+ # may then flow into other fields and so on.
89
+
90
+ # This means the network only needs to contain nodes that either directly flow into
91
+ # an abstract node or are themselves abstract. We need to track overloads because
92
+ # their arguments effectively act like abstract vars until we've resolved the final types.
93
+
94
+ Node = Union[mm.Var, mm.Field, mm.Literal, mm.Lookup, mm.Update, mm.Aggregate]
95
+
96
+ class PropagationNetwork():
97
+ def __init__(self, model: mm.Model):
98
+ # the model that is either being analyzed or that is the context for a query
99
+ self.model = model
100
+
101
+ # the resolved types of nodes in the network
102
+ self.resolved_types: dict[Node, mm.Type] = {}
103
+
104
+ # map from unresolved placeholder relations to their potential target replacements
105
+ self.potential_targets: dict[mm.Relation, list[mm.Relation]] = {}
106
+
107
+ # track the set of nodes that represent entry points into the network
108
+ self.roots = OrderedSet()
109
+ # we separately want to track nodes that were loaded from a previous run
110
+ # so that even if we have edges to them, we _still_ consider them roots
111
+ # and properly propagate types from them at the beginning
112
+ self.loaded_roots = set()
113
+
114
+ # edges in the propagation network, from one node to potentially many
115
+ self.edges:dict[Node, OrderedSet[Node]] = defaultdict(lambda: OrderedSet())
116
+ self.back_edges:dict[Node, OrderedSet[Node]] = defaultdict(lambda: OrderedSet())
117
+ # all nodes that are the target of an edge (to find roots)
118
+ self.has_incoming = set()
119
+
120
+ # type requirements: for a var with abstract declared type, the set of fields that
121
+ # it must match the type of because it flows into them
122
+ self.type_requirements:dict[mm.Var, OrderedSet[mm.Field]] = defaultdict(lambda: OrderedSet())
123
+
124
+ # all errors collected during inference
125
+ self.errors:list[TyperError] = []
126
+
127
+ # overloads resolved for a lookup/update/aggregate, by node id. This is only for
128
+ # relations that declare overloads
129
+ self.resolved_overload:dict[int, mm.Overload] = {}
130
+ # placeholders resolved for a lookup, by node id. This is only for relations that
131
+ # are placeholders (i.e. only Any fields) and will be replaced by references to
132
+ # these concrete relations. E.g. a query for "name(Any, Any)" may be replaced by
133
+ # the union of "name(Dog, String)" and name(Cat, String)".
134
+ self.resolved_placeholder:dict[int, list[mm.Relation]] = {}
135
+ # for a given lookup/update/aggregate that involves numbers, the specific number
136
+ # type resolved for it.
137
+ self.resolved_number:dict[int, mm.NumberType] = {}
138
+ # keep track of nodes already resolved to avoid re-resolving
139
+ self.resolved_nodes:set[int] = set()
140
+
141
+ #--------------------------------------------------
142
+ # Error reporting
143
+ #--------------------------------------------------
144
+
145
+ def type_mismatch(self, node: Node, expected: mm.Type, actual: mm.Type):
146
+ self.errors.append(TypeMismatch(node, expected, actual))
147
+
148
+ def invalid_type(self, node: Node, type: mm.Type):
149
+ self.errors.append(InvalidType(node, type))
150
+
151
+ def unresolved_overload(self, node: mm.Lookup|mm.Aggregate):
152
+ # TODO - consider renaming this to UnresolvedReference
153
+ self.errors.append(UnresolvedOverload(node, [self.resolve(a) for a in node.args]))
154
+
155
+ def unresolved_type(self, node: Node):
156
+ self.errors.append(UnresolvedType(node))
157
+
158
+ def has_errors(self, node: Node) -> bool:
159
+ for mismatch in self.errors:
160
+ if mismatch.node == node:
161
+ return True
162
+ return False
163
+
164
+ #--------------------------------------------------
165
+ # Types and Edges
166
+ #--------------------------------------------------
167
+
168
+ def add_edge(self, source: Node, target: Node):
169
+ # manage roots
170
+ if target in self.roots and target not in self.loaded_roots:
171
+ self.roots.remove(target)
172
+ if source not in self.has_incoming:
173
+ self.roots.add(source)
174
+ # register edge
175
+ self.edges[source].add(target)
176
+ self.back_edges[target].add(source)
177
+ self.has_incoming.add(target)
178
+
179
+ def add_resolved_type(self, node: Node, type: mm.Type):
180
+ """ Register that this node was resolved to have this type. """
181
+ if node in self.resolved_types:
182
+ self.resolved_types[node] = merge_types(self.resolved_types[node], type)
183
+ else:
184
+ self.resolved_types[node] = type
185
+
186
+ def add_type_requirement(self, source: mm.Var, field: mm.Field):
187
+ """ Register that this var, which has an abstract declared type, must match the type
188
+ of this field as it flows into it.. """
189
+ self.type_requirements[source].add(field)
190
+
191
+ #--------------------------------------------------
192
+ # Load previous types
193
+ #--------------------------------------------------
194
+
195
+ def load_types(self, type_dict: dict[Node, mm.Type]):
196
+ for node, type in type_dict.items():
197
+ if isinstance(node, (mm.Field)):
198
+ self.add_resolved_type(node, type)
199
+ self.loaded_roots.add(node)
200
+ self.roots.add(node)
201
+
202
+ #--------------------------------------------------
203
+ # Resolve Values
204
+ #--------------------------------------------------
205
+
206
+ def resolve(self, value: Node|mm.Value) -> mm.Type:
207
+ if isinstance(value, (mm.Var, mm.Field, mm.Literal)):
208
+ return self.resolved_types.get(value) or to_type(value)
209
+ assert not isinstance(value, (mm.Lookup, mm.Update, mm.Aggregate)), "Should never try to resolve a task"
210
+ return to_type(value)
211
+
212
+ #--------------------------------------------------
213
+ # Resolve References
214
+ #--------------------------------------------------
215
+
216
+ def resolve_reference(self, op: mm.Lookup|mm.Aggregate) -> Optional[mm.Overload|list[mm.Relation]]:
217
+ # check if all dependencies required to resolve this reference are met
218
+ if not self.all_dependencies_resolved(op):
219
+ return None
220
+
221
+ relation = get_relation(op)
222
+ if bt.is_placeholder(relation):
223
+ # when replacing a placeholder, we may have multiple matches
224
+ matches = []
225
+ resolved_args = [self.resolve(arg) for arg in op.args]
226
+ for target in self.potential_targets.get(relation, []):
227
+ fields = get_relation_fields(target, relation.name)
228
+ if all(type_matches(arg, self.resolve(field))
229
+ for arg, field in zip(resolved_args, fields)):
230
+ matches.append(target)
231
+
232
+ return matches
233
+
234
+ elif relation.overloads:
235
+ # when resolving an overload for a concrete relation, we can only have one
236
+ resolved_args = [self.resolve(arg) for arg in op.args]
237
+ is_function = bt.is_function(relation)
238
+ for overload in relation.overloads:
239
+ # note that for functions we only consider input fields when matching
240
+ if all(type_matches(arg, field_type) or conversion_allowed(arg, field_type)
241
+ for arg, field, field_type in zip(resolved_args, relation.fields, overload.types)
242
+ if field.input or not is_function):
243
+ self.resolved_overload[op.id] = overload
244
+ return overload
245
+ return [] # no matches found
246
+ else:
247
+ # this is a relation with type vars or numbers that needs to be specialized
248
+ return [relation]
249
+
250
+
251
+ def all_dependencies_resolved(self, op:mm.Lookup|mm.Aggregate):
252
+ # if this is a placeholder, we need assume all possible args were resolved
253
+ if bt.is_placeholder(get_relation(op)):
254
+ return True
255
+ # else, find whether all back-edges were resolved
256
+ for node in self.back_edges[op]:
257
+ if isinstance(node, (mm.Var, mm.Field, mm.Literal)):
258
+ node_type = self.resolve(node)
259
+ if bt.is_abstract(node_type):
260
+ return False
261
+ return True
262
+
263
+
264
+ #--------------------------------------------------
265
+ # Propagation
266
+ #--------------------------------------------------
267
+
268
+ def propagate(self):
269
+ edges = self.edges
270
+ work_list = []
271
+
272
+ # go through all the roots and find any that are not abstract, they'll be the first
273
+ # nodes to push types through the network
274
+ unhandled_roots = OrderedSet()
275
+ for node in self.roots:
276
+ if not isinstance(node, (mm.Var, mm.Field, mm.Literal)):
277
+ continue
278
+ node_type = self.resolve(node)
279
+ if not bt.is_abstract(node_type):
280
+ work_list.append(node)
281
+ else:
282
+ unhandled_roots.add(node)
283
+
284
+ # push known type nodes through the edges
285
+ while work_list:
286
+ source = work_list.pop(0)
287
+ self.resolved_nodes.add(source.id)
288
+ if source in unhandled_roots:
289
+ unhandled_roots.remove(source)
290
+ source_type = self.resolve(source)
291
+ # check to see if the source has ended up with a set of types that
292
+ # aren't valid, e.g. a union of primitives
293
+ if invalid_type(source_type):
294
+ self.invalid_type(source, source_type)
295
+
296
+ # propagate our type to each outgoing edge
297
+ for out in edges.get(source, []):
298
+ # if this is an overload then we need to try and resolve it
299
+ if isinstance(out, (mm.Lookup, mm.Aggregate)):
300
+ if not out.id in self.resolved_nodes:
301
+ found = self.resolve_reference(out)
302
+ if found is not None:
303
+ self.resolved_nodes.add(out.id)
304
+ self.propagate_reference(out, found)
305
+ for arg in out.args:
306
+ if arg not in work_list:
307
+ work_list.append(arg)
308
+ # otherwise, we just add to the outgoing node's type and if it
309
+ # changes we add it to the work list
310
+ elif start := self.resolve(out):
311
+ self.add_resolved_type(out, source_type)
312
+ if out not in work_list and (start != self.resolve(out) or not out.id in self.resolved_nodes):
313
+ work_list.append(out)
314
+
315
+ for source in unhandled_roots:
316
+ self.unresolved_type(source)
317
+
318
+ # now that we've pushed all the types through the network, we need to validate
319
+ # that all type requirements of those nodes are met
320
+ for node, fields in self.type_requirements.items():
321
+ node_type = self.resolve(node)
322
+ for field in fields:
323
+ field_type = self.resolve(field)
324
+ if not type_matches(node_type, field_type) and not conversion_allowed(node_type, field_type):
325
+ self.type_mismatch(node, field_type, node_type)
326
+
327
+
328
+ def propagate_reference(self, task:mm.Lookup|mm.Aggregate, references:mm.Overload|list[mm.Relation]):
329
+ # TODO: distinguish between overloads and placeholders better when raising errors
330
+ if not references:
331
+ return self.unresolved_overload(task)
332
+
333
+ resolved_args = [self.resolve(arg) for arg in task.args]
334
+
335
+ # we need to determine the final types of our args by taking all the references
336
+ # and adding the type of their fields back to the args.
337
+ relation = get_relation(task)
338
+
339
+ if bt.is_placeholder(relation):
340
+ assert(references and isinstance(references, list))
341
+ # we've resolved the placeholder, so store that
342
+ self.resolved_placeholder[task.id] = references
343
+
344
+ # we need to determine the final types of our args by taking all the references
345
+ # and adding the type of their fields back to the args.
346
+ for reference in references:
347
+ resolved_fields = [self.resolve(f) for f in get_relation_fields(reference, relation.name)]
348
+ for field_type, arg_type, arg in zip(resolved_fields, resolved_args, task.args):
349
+ if bt.is_abstract(arg_type) and isinstance(arg, mm.Var):
350
+ self.add_resolved_type(arg, field_type)
351
+ else:
352
+ if isinstance(references, mm.Overload):
353
+ # we resolved an overload, use its types
354
+ types = list(references.types)
355
+ else:
356
+ # we resolved to a single concrete relation that contains type vars or needs
357
+ # number specialization, so use that relation's field types
358
+ types = list([self.resolve(f) for f in relation.fields])
359
+
360
+ # if our overload preserves types, we check to see if there's a preserved
361
+ # output type given the inputs and if so, shadow the field's type with the
362
+ # preserved type
363
+ resolved_fields = types
364
+ if bt.is_function(relation) and len(set(resolved_fields)) == 1:
365
+ input_types = set([arg_type for field, arg_type
366
+ in zip(relation.fields, resolved_args) if field.input])
367
+ if out_type := self.try_preserve_type(input_types):
368
+ resolved_fields = [field_type if field.input else out_type
369
+ for field, field_type in zip(relation.fields, types)]
370
+
371
+ # TODO - we also need to make sure the type vars are constently resolved here
372
+ # i.e. if types contain typevars, check that the args that are bound to those
373
+ # typevars are consistent
374
+ if b.core.Number in types or (b.core.TypeVar in types and any(bt.is_number(t) for t in resolved_args)):
375
+ # this overload contains generic numbers or typevars bound to numbers, so
376
+ # find which specific type of number to use given the arguments being passed
377
+ number, resolved_fields = self.specialize_number(relation, resolved_fields, resolved_args)
378
+ self.resolved_number[task.id] = number
379
+ # push the specialized number to the outputs
380
+ for field, field_type, arg in zip(relation.fields, resolved_fields, task.args):
381
+ if not field.input and isinstance(arg, mm.Var):
382
+ self.add_resolved_type(arg, field_type)
383
+ else:
384
+ for field_type, arg, arg_type in zip(resolved_fields, task.args, resolved_args):
385
+ if bt.is_abstract(arg_type) and isinstance(arg, mm.Var):
386
+ self.add_resolved_type(arg, field_type)
387
+
388
+
389
+ def try_preserve_type(self, types:set[mm.Type]) -> Optional[mm.Type]:
390
+ # we keep the input type as the output type if either all inputs
391
+ # are the exact same type or there's one nominal and its base primitive
392
+ # type, e.g. USD + Decimal
393
+ if len(types) == 1:
394
+ return next(iter(types))
395
+ if len(types) == 2:
396
+ t1, t2 = types
397
+ t1_base = bt.get_primitive_supertype(t1)
398
+ t2_base = bt.get_primitive_supertype(t2)
399
+ if t1_base is None or t2_base is None:
400
+ base_equivalent = type_matches(t1, t2, accept_expected_super_types=True)
401
+ else:
402
+ base_equivalent = type_matches(t1_base, t2_base)
403
+ if base_equivalent:
404
+ # as long as one of the types is a base primitive, we can use the
405
+ # other type as final preserved type
406
+ if bt.is_primitive(t1):
407
+ return t2
408
+ elif bt.is_primitive(t2):
409
+ return t1
410
+ return None
411
+
412
+ def specialize_number(self, op, field_types:list[mm.Type], arg_types:list[mm.Type]) -> Tuple[mm.NumberType, list[mm.Type]]:
413
+ """
414
+ Find the number type to use for an overload that has Number in its field_types,
415
+ and which is being referred to with these arg_types.
416
+
417
+ Return a tuple where the first element is the specialized number type, and the second
418
+ element is a new list that contains the same types as field_types but with
419
+ Number replaced by this specialized number.
420
+ """
421
+ if op == b.core.div:
422
+ # see https://docs.snowflake.com/en/sql-reference/operators-arithmetic#division
423
+ numerator, denominator = get_number_type(arg_types[0]), get_number_type(arg_types[1])
424
+ s = max(numerator.scale, min(numerator.scale + 6, 12))
425
+ number = mm.NumberType(name=f"Number(38,{s})", precision=38, scale=s, source=op.source, super_types=(b.core.Numeric,))
426
+ return number, [numerator, denominator, number]
427
+ elif op == b.core.mul:
428
+ # see https://docs.snowflake.com/en/sql-reference/operators-arithmetic#multiplication
429
+ t1, t2 = get_number_type(arg_types[0]), get_number_type(arg_types[1])
430
+ S1 = t1.scale
431
+ S2 = t2.scale
432
+ s = min(S1 + S2, max(S1, S2, 12))
433
+ number = mm.NumberType(name=f"Number(38,{s})", precision=38, scale=s, source=op.source, super_types=(b.core.Numeric,))
434
+ return number, [t1, t2, number]
435
+ elif op == b.aggregates.avg:
436
+ # TODO!! - implement proper avg specialization
437
+ pass
438
+
439
+ number = None
440
+ for arg_type in arg_types:
441
+ x = bt.get_number_supertype(arg_type)
442
+ if isinstance(x, mm.NumberType):
443
+ # the current specialization policy is to select the number with largest
444
+ # scale and, if there multiple with the largest scale, the one with the
445
+ # largest precision. This is safe because when converting a number to the
446
+ # specialized number, we never truncate fractional digits (because we
447
+ # selected the largest scale) and, if the non-fractional digits are too
448
+ # large to fit the specialized number, we will have a runtime overflow,
449
+ # which should alert the user of the problem.
450
+ #
451
+ # In the future we can implement more complex policies. For example,
452
+ # snowflake has well documented behavior for how the output of operations
453
+ # behave in face of different number types, and we may use that:
454
+ # https://docs.snowflake.com/en/sql-reference/operators-arithmetic#scale-and-precision-in-arithmetic-operations
455
+ if number is None or x.scale > number.scale or (x.scale == number.scale and x.precision > number.precision):
456
+ number = x
457
+ if number is None:
458
+ number = b.core.DefaultNumber
459
+ # assert(isinstance(number, mm.NumberType))
460
+ return number, [number if bt.is_number(field_type) else field_type
461
+ for field_type in field_types]
462
+
463
+
464
+ #--------------------------------------------------
465
+ # Display
466
+ #--------------------------------------------------
467
+
468
+ # draw the network as a mermaid graph for the debugger
469
+ def to_mermaid(self, max_edges=500) -> str:
470
+
471
+ # add links for edges while collecting nodes
472
+ nodes = OrderedSet()
473
+ link_strs = []
474
+ # edges
475
+ for src, dsts in self.edges.items():
476
+ nodes.add(src)
477
+ for dst in dsts:
478
+ if len(link_strs) > max_edges:
479
+ break
480
+ nodes.add(dst)
481
+ link_strs.append(f"n{src.id} --> n{dst.id}")
482
+ if len(link_strs) > max_edges:
483
+ break
484
+ # type requirements
485
+ for src, dsts in self.type_requirements.items():
486
+ nodes.add(src)
487
+ for dst in dsts:
488
+ if len(link_strs) > max_edges:
489
+ break
490
+ nodes.add(dst)
491
+ link_strs.append(f"n{src.id} -.-> n{dst.id}")
492
+ if len(link_strs) > max_edges:
493
+ break
494
+
495
+ def type_span(t:mm.Type) -> str:
496
+ type_str = t.name if isinstance(t, mm.ScalarType) else str(t)
497
+ return f"<span style='color:cyan;'>{type_str.strip()}</span>"
498
+
499
+ def reference_span(rel:mm.Relation, arg_types:list[mm.Type], root:str) -> str:
500
+ args = []
501
+ for field, arg_type in zip(rel.fields, arg_types):
502
+ field_type = self.resolve(field)
503
+ if not type_matches(arg_type, field_type) and not conversion_allowed(arg_type, field_type) and not bt.is_abstract(field_type):
504
+ args.append(f"<span style='color:yellow;'>{str(arg_type).strip()} -> {str(field_type).strip()}</span>")
505
+ elif isinstance(arg_type, mm.UnionType):
506
+ args.append(type_span(field_type))
507
+ else:
508
+ args.append(type_span(arg_type))
509
+ return f'{rel.name}{root}({", ".join(args)})'
510
+
511
+ resolved = self.resolved_types
512
+ node_strs = []
513
+ for node in nodes:
514
+ klass = ""
515
+ root = "(*)" if node in self.roots else ""
516
+ if isinstance(node, mm.Var):
517
+ ir_type = resolved.get(node) or self.resolve(node)
518
+ type_str = type_span(ir_type)
519
+ label = f'(["{node.name}{root}:{type_str}"])'
520
+ elif isinstance(node, mm.Literal):
521
+ ir_type = resolved.get(node) or self.resolve(node)
522
+ type_str = type_span(ir_type)
523
+ klass = ":::literal"
524
+ label = f'[/"{node.value}{root}: {type_str}"\\]'
525
+ elif isinstance(node, mm.Field):
526
+ ir_type = resolved.get(node) or self.resolve(node)
527
+ type_str = type_span(ir_type)
528
+ klass = ":::field"
529
+ rel = node._relation
530
+ if rel is not None:
531
+ rel = str(node._relation)
532
+ label = f'{{{{"{node.name}{root}:{type_str}\nfrom {rel}"}}}}'
533
+ else:
534
+ label = f'{{{{"{node.name}{root}:\n{type_str}"}}}}'
535
+ elif isinstance(node, (mm.Lookup, mm.Update, mm.Aggregate)):
536
+ arg_types = [self.resolve(arg) for arg in node.args]
537
+ if node.id in self.resolved_placeholder:
538
+ overloads = self.resolved_placeholder[node.id]
539
+ content = "<br/>".join([reference_span(o, arg_types, root) for o in overloads])
540
+ else:
541
+ content = reference_span(get_relation(node), arg_types, root)
542
+ label = f'[/"{content}"/]'
543
+ # elif isinstance(node, mm.Relation):
544
+ # label = f'[("{node}")]'
545
+ else:
546
+ raise NotImplementedError(f"Unknown node type: {type(node)}")
547
+ if self.has_errors(node):
548
+ klass = ":::error"
549
+ node_strs.append(f'n{node.id}{label}{klass}')
550
+
551
+ node_str = "\n ".join(node_strs)
552
+ link_str = "\n ".join(link_strs)
553
+ template = f"""
554
+ %%{{init: {{'theme':'dark', 'flowchart':{{'useMaxWidth':false, 'htmlLabels': true}}}}}}%%
555
+ flowchart TD
556
+ linkStyle default stroke:#666
557
+ classDef field fill:#245,stroke:#478
558
+ classDef literal fill:#452,stroke:#784
559
+ classDef error fill:#624,stroke:#945,color:#f9a
560
+ classDef default stroke:#444,stroke-width:2px, font-size:12px
561
+
562
+ %% nodes
563
+ {node_str}
564
+
565
+ %% edges
566
+ {link_str}
567
+ """
568
+ return template
569
+
570
+ #--------------------------------------------------
571
+ # Analyzer
572
+ #--------------------------------------------------
573
+
574
+ class Analyzer(Walker):
575
+ """ Walks the metamodel and builds the propagation network. """
576
+
577
+ def __init__(self, net:PropagationNetwork):
578
+ super().__init__()
579
+ self.net = net
580
+
581
+ def analyze(self, node: mm.Node):
582
+ self(node)
583
+
584
+ # TODO - ignoring requires for now because the typing of constraints seems incorrect
585
+ def enter_require(self, require: mm.Require):
586
+ return NO_WALK
587
+
588
+ def compute_potential_targets(self, relation: mm.Relation):
589
+ # register potential targets for placeholders
590
+ if bt.is_placeholder(relation):
591
+ self.net.potential_targets[relation] = get_potential_targets(self.net.model, relation)
592
+
593
+ #--------------------------------------------------
594
+ # Walk Update
595
+ #--------------------------------------------------
596
+
597
+ def update(self, node: mm.Update):
598
+ rel = node.relation
599
+ self.compute_potential_targets(rel)
600
+
601
+ # if this is a type relation, the update is asserting that the argument is of that
602
+ # type; so, it's fine to pass a super-type in to the population e.g. Employee(Person)
603
+ # should be a valid way to populate that a particular Person is also an Employee.
604
+ is_type_relation = isinstance(rel, mm.TypeNode)
605
+ for arg, field in zip(node.args, rel.fields):
606
+ field_type = field.type
607
+ arg_type = self.net.resolve(arg)
608
+
609
+ # if the arg is abstract, but the field isn't, then we need to make sure that
610
+ # once the arg is resolved we check that it matches the field type
611
+ if isinstance(arg, mm.Var) and bt.is_abstract(arg_type) and bt.is_concrete(field_type):
612
+ self.net.add_type_requirement(arg, field)
613
+
614
+ if bt.is_abstract(field_type) and isinstance(arg, (mm.Var, mm.Literal)):
615
+ # if the field is abstract, then eventually this arg will help determine
616
+ # the field's type, so add an edge from the arg to the field
617
+ self.net.add_edge(arg, field)
618
+ elif not type_matches(arg_type, field_type, accept_expected_super_types=is_type_relation):
619
+ if not conversion_allowed(arg_type, field_type):
620
+ self.net.type_mismatch(node, field_type, arg_type)
621
+
622
+ #--------------------------------------------------
623
+ # Walk Lookups + Aggregates
624
+ #--------------------------------------------------
625
+
626
+ def lookup(self, node: mm.Lookup):
627
+ self.compute_potential_targets(node.relation)
628
+ self.visit_rel_op(node)
629
+
630
+ def aggregate(self, node: mm.Aggregate):
631
+ self.visit_rel_op(node)
632
+
633
+ def visit_rel_op(self, node: mm.Lookup|mm.Aggregate):
634
+ rel = get_relation(node)
635
+
636
+ # special case eq lookups
637
+ if isinstance(node, mm.Lookup) and rel == b.core.eq:
638
+ # if both args for an eq are abstract, link them, otherwise do normal processing
639
+ (left, right) = node.args
640
+ left_type = self.net.resolve(left)
641
+ right_type = self.net.resolve(right)
642
+ if bt.is_abstract(left_type) and bt.is_abstract(right_type):
643
+ assert isinstance(left, mm.Var) and isinstance(right, mm.Var)
644
+ # if both sides are abstract, then whatever we find out about
645
+ # either should propagate to the other
646
+ self.net.add_edge(left, right)
647
+ self.net.add_edge(right, left)
648
+ return
649
+
650
+ # special case when the relation needs to be resolved as there are overloads, placeholders,
651
+ # type vars or it needs number specialization
652
+ if self.requires_resolution(rel):
653
+ return self.visit_unresolved_reference(node)
654
+
655
+ # if this is a population check, then it's fine to pass a subtype in to do the check
656
+ # e.g. Employee(Person) is a valid way to check if a person is an employee
657
+ is_population_lookup = isinstance(rel, mm.TypeNode)
658
+ for arg, field in zip(node.args, rel.fields):
659
+ field_type = self.net.resolve(field)
660
+ arg_type = self.net.resolve(arg)
661
+ if not type_matches(arg_type, field_type, is_population_lookup):
662
+ # Do not complain if we can convert the arg to the field type.
663
+ if not conversion_allowed(arg_type, field_type):
664
+ # if the arg is a var and it matches when allowing for super types of
665
+ # the expected we can expect to refine it later; but we add a type
666
+ # requirement to check at the end
667
+ if isinstance(arg, mm.Var) and type_matches(arg_type, field_type, True):
668
+ self.net.add_type_requirement(arg, field)
669
+ else:
670
+ self.net.type_mismatch(node, field_type, arg_type)
671
+ # if we have an abstract var then this field will ultimately propagate to that
672
+ # var's type; also, if this is a population lookup, the type of the population
673
+ # being looked up will flow back to the var
674
+ if isinstance(arg, mm.Var):
675
+ if not field.input:
676
+ self.net.add_edge(field, arg)
677
+ else:
678
+ self.net.add_type_requirement(arg, field)
679
+
680
+
681
+ def requires_resolution(self, rel: mm.Relation) -> bool:
682
+ # has overloads or is a placeholder relation that needs replacement
683
+ if rel.overloads or bt.is_placeholder(rel):
684
+ return True
685
+ # there are type vars or numbers in the fields that need specialization
686
+ for field in rel.fields:
687
+ t = self.net.resolve(field)
688
+ if bt.is_type_var(t) or t == b.core.Number:
689
+ return True
690
+ return False
691
+
692
+
693
+ def visit_unresolved_reference(self, node: mm.Lookup|mm.Aggregate):
694
+ relation = get_relation(node)
695
+ # functions have their outputs determined by their inputs
696
+ is_function = bt.is_function(relation)
697
+ is_placeholder = bt.is_placeholder(relation)
698
+ # add edges between args and the relation based on input/output
699
+ for field, arg in zip(relation.fields, node.args):
700
+ if isinstance(arg, (mm.Var, mm.Literal)):
701
+ if field.input:
702
+ # the arg type will flow into the input
703
+ self.net.add_edge(arg, node)
704
+ else:
705
+ if is_function:
706
+ # this is an output of a function, so the field type will flow to the arg
707
+ self.net.add_edge(node, arg)
708
+ else:
709
+ if is_placeholder:
710
+ self.net.add_edge(arg, node)
711
+ if bt.is_abstract(field.type) and not is_placeholder:
712
+ self.net.add_edge(field, node)
713
+
714
+ if bt.is_abstract(self.net.resolve(arg)):
715
+ self.net.add_edge(node, arg)
716
+
717
+
718
+ #--------------------------------------------------
719
+ # Replacer
720
+ #--------------------------------------------------
721
+
722
+ # Once we've pushed all the types through the network, we need to replace the types of
723
+ # fields and vars that we may have discovered. We also need to replace placeholder lookups
724
+ # with the chosen relations and do any conversions that are needed.
725
+ class Replacer(Rewriter):
726
+ def __init__(self, net:PropagationNetwork):
727
+ super().__init__()
728
+ self.net = net
729
+
730
+ def rewrite(self, model: T) -> T:
731
+ return self(model) # type: ignore
732
+
733
+ def logical(self, logical: mm.Logical):
734
+ if len(logical.body) == 0:
735
+ return logical
736
+ # inline logicals that are just there to group other nodes during rewrite
737
+ body = []
738
+ for child in logical.body:
739
+ if isinstance(child, mm.Logical) and not child.optional and not child.scope:
740
+ body.extend(child.body)
741
+ else:
742
+ body.append(child)
743
+ return logical.mut(body = tuple(body))
744
+
745
+ #--------------------------------------------------
746
+ # Rewriter handlers
747
+ #--------------------------------------------------
748
+
749
+ def field(self, node: mm.Field):
750
+ # TODO - this is only modifying the relation in the model, but then we have a new
751
+ # relation there, which is different than the object referenced by tasks.
752
+ if node in self.net.resolved_types:
753
+ return mm.Field(node.name, self.net.resolved_types[node], node.input)
754
+ return node
755
+
756
+ def var(self, node: mm.Var):
757
+ if node in self.net.resolved_types:
758
+ return mm.Var(self.net.resolved_types[node], node.name)
759
+ return node
760
+
761
+ def literal(self, node: mm.Literal):
762
+ if node in self.net.resolved_types:
763
+ return mm.Literal(self.net.resolved_types[node], node.value)
764
+ return node
765
+
766
+ def update(self, node: mm.Update):
767
+ return self.convert_arguments(node, node.relation)
768
+
769
+ def lookup(self, node: mm.Lookup):
770
+ # We need to handle eq specially because its arguments can be converted symmetrically
771
+ if node.relation == b.core.eq:
772
+ return self.visit_eq_lookup(node)
773
+
774
+ args = types = None
775
+ if node.id in self.net.resolved_placeholder:
776
+ resolved_relations = self.net.resolved_placeholder[node.id]
777
+ args = get_lookup_args(node, resolved_relations[0])
778
+ types = [f.type for f in resolved_relations[0].fields]
779
+ elif node.id in self.net.resolved_overload:
780
+ resolved_relations = [node.relation]
781
+ types = self.net.resolved_overload[node.id].types
782
+ else:
783
+ resolved_relations = [node.relation]
784
+
785
+ if len(resolved_relations) == 1:
786
+ x = self.convert_arguments(node, resolved_relations[0], args, types)
787
+ if isinstance(x, mm.Logical) and len(x.body) == 1:
788
+ return x.body[0]
789
+ else:
790
+ return x
791
+
792
+ branches:list = []
793
+ for target in resolved_relations:
794
+ args = get_lookup_args(node, target)
795
+ types = [f.type for f in get_relation_fields(resolved_relations[0], node.relation.name)]
796
+ # adding this logical to avoid issues in the old backend
797
+ branches.append(mm.Logical((self.convert_arguments(node, target, args, types=types),)))
798
+ return mm.Union(tuple(branches))
799
+
800
+ def convert_arguments(self, node: mm.Lookup|mm.Update, relation: mm.Relation, args: Iterable[mm.Value]|None=None, types: Iterable[mm.Type]|None=None) -> mm.Logical|mm.Lookup|mm.Update:
801
+ args = args or node.args
802
+ types = types or [self.net.resolve(f) for f in relation.fields]
803
+ number_type = self.net.resolved_number.get(node.id)
804
+ is_function = bt.is_function(relation)
805
+ tasks = []
806
+ final_args = []
807
+ for arg, field, field_type in zip(args, relation.fields, types):
808
+ if isinstance(arg, (mm.Var, mm.Literal)) and (not is_function or field.input):
809
+ arg_type = to_type(arg)
810
+ if number_type and bt.is_number(arg_type) and not arg_type == b.core.ScaledNumber:
811
+ field_type = number_type
812
+ # the typer previously made sure that this should be valid so a type mismatch
813
+ # means we need to convert
814
+ if not type_matches(arg_type, field_type):
815
+ final_args.append(convert(arg, field_type, tasks))
816
+ else:
817
+ final_args.append(arg)
818
+ else:
819
+ final_args.append(arg)
820
+ if isinstance(node, mm.Lookup):
821
+ tasks.append(node.mut(relation = relation, args = tuple(final_args)))
822
+ else:
823
+ tasks.append(node.mut(relation = relation, args = tuple(final_args)))
824
+ if len(tasks) == 1:
825
+ return tasks[0]
826
+ return mm.Logical(tuple(tasks))
827
+
828
+
829
+ def visit_eq_lookup(self, node: mm.Lookup):
830
+ (left, right) = node.args
831
+ left_type = to_type(left)
832
+ right_type = to_type(right)
833
+
834
+ if type_matches(left_type, right_type):
835
+ return node
836
+
837
+ assert isinstance(left, (mm.Var, mm.Literal)) and isinstance(right, (mm.Var, mm.Literal))
838
+ final_args = []
839
+ tasks = []
840
+ if conversion_allowed(left_type, right_type):
841
+ final_args = [convert(left, right_type, tasks), right]
842
+ elif conversion_allowed(right_type, left_type):
843
+ final_args = [left, convert(right, left_type, tasks)]
844
+ else:
845
+ self.net.type_mismatch(node, left_type, right_type)
846
+ return node
847
+
848
+ tasks.append(mm.Lookup(b.core.eq, tuple(final_args)))
849
+ return mm.Logical(tuple(tasks))
850
+
851
+ #--------------------------------------------------
852
+ # Helpers
853
+ #--------------------------------------------------
854
+
855
+ def get_relation(node: mm.Lookup|mm.Update|mm.Aggregate) -> mm.Relation:
856
+ if isinstance(node, mm.Aggregate):
857
+ return node.aggregation
858
+ return node.relation
859
+
860
+ def get_name(type: mm.Type) -> str:
861
+ if isinstance(type, mm.ScalarType):
862
+ return type.name
863
+ elif isinstance(type, mm.UnionType):
864
+ return '|'.join([get_name(t) for t in type.types])
865
+ elif isinstance(type, mm.ListType):
866
+ return f'List[{get_name(type.element_type)}]'
867
+ elif isinstance(type, mm.TupleType):
868
+ return f'Tuple[{", ".join([get_name(t) for t in type.element_types])}]'
869
+ else:
870
+ raise TypeError(f"Unknown type: {type}")
871
+
872
+ #--------------------------------------------------
873
+ # Type and Relation helpers
874
+ #--------------------------------------------------
875
+
876
+ def get_relation_fields(relation: mm.Relation, name: str) -> Iterable[mm.Field]:
877
+ """ Get the fields of this relation, potentially reordered to match the reading with the given name."""
878
+ if name == relation.name:
879
+ return relation.fields
880
+ for reading in relation.readings:
881
+ if reading.name == name:
882
+ # reorder the fields to match the correct ordering
883
+ fields = []
884
+ for idx in reading.field_order:
885
+ fields.append(relation.fields[idx])
886
+ return fields
887
+ return []
888
+
889
+ def get_lookup_args(node: mm.Lookup, target: mm.Relation):
890
+ """ Get the args of this lookup, potentially reordered to match the reading with the given name."""
891
+ for reading in target.readings:
892
+ if reading.name == node.relation.name:
893
+ # reorder the args to match the correct ordering
894
+ args = []
895
+ for idx in reading.field_order:
896
+ args.append(node.args[idx])
897
+ return args
898
+ return node.args
899
+
900
+ def get_number_type(t: mm.Type) -> mm.NumberType:
901
+ # Get a number type from the given type, if it is Number return the default number
902
+ x = bt.get_number_supertype(t)
903
+ if isinstance(x, mm.NumberType):
904
+ return x
905
+ return b.core.DefaultNumber
906
+
907
+ def is_potential_target(placeholder: mm.Relation, target: mm.Relation) -> bool:
908
+ """ Whether this target is matches the placeholder signature and, thus, can be a potential target. """
909
+ if placeholder != target and len(placeholder.fields) == len(target.fields) and not bt.is_placeholder(target):
910
+ return placeholder.name == target.name or any(placeholder.name == reading.name for reading in target.readings)
911
+ return False
912
+
913
+ def get_potential_targets(model: mm.Model, placeholder: mm.Relation) -> list[mm.Relation]:
914
+ """ Get all potential target relations in the model that match the placeholder signature. """
915
+ return list(filter(lambda r: is_potential_target(placeholder, r), model.relations))
916
+
917
+ def to_type(value: mm.Value|mm.Field|mm.Literal) -> mm.Type:
918
+ if isinstance(value, (mm.Var, mm.Field, mm.Literal)):
919
+ return value.type
920
+
921
+ if isinstance(value, mm.Type):
922
+ return b.core.Type
923
+
924
+ if isinstance(value, tuple):
925
+ return mm.TupleType(element_types=tuple(to_type(v) for v in value))
926
+
927
+ raise TypeError(f"Cannot determine IR type for value: {value} of type {type(value).__name__}")
928
+
929
+
930
+ def convert(value: mm.Var|mm.Literal, to_type: mm.Type, tasks: list[mm.Task]) -> mm.Value:
931
+ # if the arg is a literal, we can just change its type
932
+ # TODO - we may want to check that the value is actually convertible
933
+ if isinstance(value, mm.Literal):
934
+ return mm.Literal(to_type, value.value)
935
+
936
+ # otherise we need to add a cast
937
+ name = sanitize(value.name + "_" + get_name(to_type))
938
+ to_type_base = bt.get_primitive_supertype(to_type) or to_type
939
+ new_value = mm.Var(to_type_base, name)
940
+ tasks.append(mm.Lookup(b.core.cast, (to_type_base, value, new_value)))
941
+ return new_value
942
+
943
+
944
+ def conversion_allowed(from_type: mm.Type, to_type: mm.Type) -> bool:
945
+ # value type conversion is allowed
946
+ x = bt.get_primitive_supertype(from_type)
947
+ y = bt.get_primitive_supertype(to_type)
948
+ if x and y and (x != from_type or y != to_type) and conversion_allowed(x, y):
949
+ return True
950
+
951
+ # numbers can be converted to floats
952
+ if bt.is_numeric(from_type) and to_type == b.core.Float:
953
+ return True
954
+
955
+ # a number can be converted to another number of larger scale
956
+ if isinstance(from_type, mm.NumberType) and isinstance(to_type, mm.NumberType):
957
+ if to_type.scale > from_type.scale:
958
+ return True
959
+
960
+ if from_type == b.core.Number and isinstance(to_type, mm.NumberType):
961
+ return True
962
+
963
+ return False
964
+
965
+ def type_matches(actual: mm.Type, expected: mm.Type, accept_expected_super_types=False) -> bool:
966
+ """
967
+ True iff we can use a value of the actual type when expecting the expected type, without
968
+ conversions.
969
+
970
+ Any super-type of `actual` can match `expected`. For example if we expect a `Person`, we
971
+ can use an `Employee` if `Employee < Person`.
972
+
973
+ In general, the other way around is not true: if we expect an `Employee` we cannot use a
974
+ `Person` instead.
975
+
976
+ However, when the relation is a Type (previsously known as "population relations"), it
977
+ is valid to provide sub-types of the expected type. For example, `Employee(Person)` is
978
+ a valid way to check that `Person` is an `Employee` on a `Lookup`, or to assert that a
979
+ particular `Person` is an `Employee` on an `Update`.
980
+ """
981
+ # exact match
982
+ if actual == expected:
983
+ return True
984
+
985
+ # any matches anything
986
+ if actual == b.core.Any or expected == b.core.Any:
987
+ return True
988
+
989
+ # type vars match anything
990
+ if expected == b.core.TypeVar:
991
+ return True
992
+
993
+ # TODO - remove this once we make them singletons per precision/scale
994
+ if isinstance(actual, mm.NumberType) and isinstance(expected, mm.NumberType):
995
+ if actual.precision == expected.precision and actual.scale == expected.scale:
996
+ return True
997
+
998
+ # if an entity type var or any entity is expected, it matches any actual entity type
999
+ if (expected == b.core.EntityTypeVar or bt.extends(expected, b.core.AnyEntity)) and not bt.is_primitive(actual):
1000
+ return True
1001
+
1002
+ # the abstract Number type and the number type variable match any number type
1003
+ if (expected == b.core.Number) and bt.is_number(actual):
1004
+ return True
1005
+
1006
+ if (expected == b.core.Numeric) and bt.is_numeric(actual):
1007
+ return True
1008
+
1009
+ # if actual is scalar, any of its parents may match the expected type
1010
+ if isinstance(actual, mm.ScalarType) and any([type_matches(parent, expected) for parent in actual.super_types]):
1011
+ return True
1012
+
1013
+ # if expected is a value type or this is a check for a type relation, any of the expected type's parents may match the actual type
1014
+ if (accept_expected_super_types or bt.is_value_type(expected)) and isinstance(expected, mm.ScalarType) and any([type_matches(actual, parent, accept_expected_super_types) for parent in expected.super_types]):
1015
+ return True
1016
+
1017
+ # if we expect a union, the actual can match any of its types
1018
+ if isinstance(expected, mm.UnionType):
1019
+ for t in expected.types:
1020
+ if type_matches(t, actual, accept_expected_super_types):
1021
+ return True
1022
+
1023
+ # if actual is a union, every one of its types must match the expected type
1024
+ # if isinstance(actual, mm.UnionType):
1025
+ # for t in actual.types:
1026
+ # if not type_matches(t, expected, accept_expected_super_types):
1027
+ # return False
1028
+ # return True
1029
+ # TODO - we have to distinguish between when we are checking that a specific arg matches
1030
+ # a relation vs when we are selecting relations for the placeholders; then we have to
1031
+ # decide between the above and this.
1032
+ if isinstance(actual, mm.UnionType):
1033
+ for t in actual.types:
1034
+ if type_matches(t, expected, accept_expected_super_types):
1035
+ return True
1036
+
1037
+ # a list type matches if their element types match
1038
+ if isinstance(actual, (mm.ListType, mm.TupleType)) and isinstance(expected, mm.ListType):
1039
+ if isinstance(actual, mm.TupleType):
1040
+ for et in actual.element_types:
1041
+ if not type_matches(et, expected.element_type):
1042
+ return False
1043
+ return True
1044
+ return type_matches(actual.element_type, expected.element_type)
1045
+
1046
+ # a tuple types match if any of all their types match
1047
+ if isinstance(actual, mm.TupleType) and isinstance(expected, mm.TupleType):
1048
+ return all([type_matches(ae, ee) for ae, ee in zip(actual.element_types, expected.element_types)])
1049
+
1050
+ # accept tuples with a single element type to match a list with that type
1051
+ if isinstance(actual, mm.TupleType) and isinstance(expected, mm.ListType):
1052
+ if len(set(actual.element_types)) == 1:
1053
+ return type_matches(actual.element_types[0], expected.element_type)
1054
+
1055
+ # otherwise no match
1056
+ return False
1057
+
1058
+
1059
+ def merge_types(type1: mm.Type, type2: mm.Type) -> mm.Type:
1060
+ if type1 == type2:
1061
+ return type1
1062
+ types_to_process = [type1, type2]
1063
+
1064
+ # if one of them is the abstract Number type, pick the other
1065
+ if type1 == b.core.Number and isinstance(type2, mm.NumberType):
1066
+ return type2
1067
+ if type2 == b.core.Number and isinstance(type1, mm.NumberType):
1068
+ return type1
1069
+
1070
+ # if both are number types, pick the one with larger scale/precision
1071
+ if isinstance(type1, mm.NumberType) and isinstance(type2, mm.NumberType):
1072
+ if type1.scale > type2.scale or (type1.scale == type2.scale and type1.precision > type2.precision):
1073
+ return type1
1074
+ else:
1075
+ return type2
1076
+
1077
+ # if we are overriding a number with a float, pick float
1078
+ if isinstance(type1, mm.NumberType) and type2 == b.core.Float:
1079
+ return type2
1080
+
1081
+ # if one extends the other, pick the most specific one
1082
+ if bt.extends(type1, type2):
1083
+ return type1
1084
+ if bt.extends(type2, type1):
1085
+ return type2
1086
+
1087
+ # give precedence to nominal types (e.g. merging USD(decimal) with decimal gives USD(decimal))
1088
+ base_primitive_type1 = bt.get_primitive_supertype(type1)
1089
+ base_primitive_type2 = bt.get_primitive_supertype(type2)
1090
+ if base_primitive_type1 == base_primitive_type2:
1091
+ if bt.is_primitive(type1):
1092
+ return type2
1093
+ elif bt.is_primitive(type2):
1094
+ return type1
1095
+
1096
+ combined = OrderedSet()
1097
+ # Iterative flattening of union types
1098
+ while types_to_process:
1099
+ t = types_to_process.pop()
1100
+ if isinstance(t, mm.UnionType):
1101
+ types_to_process.extend(t.types)
1102
+ else:
1103
+ combined.add(t)
1104
+
1105
+ # If we have multiple types and Any or AnyEntity is one of them, remove Any
1106
+ if len(combined) > 1:
1107
+ if b.core.Any in combined:
1108
+ combined.remove(b.core.Any)
1109
+ if b.core.AnyEntity in combined:
1110
+ combined.remove(b.core.AnyEntity)
1111
+
1112
+ # If we still have multiple types, make sure supertypes are removed to keep only the
1113
+ # most specific types
1114
+ if len(combined) > 1:
1115
+ to_remove = set()
1116
+ for t1 in combined:
1117
+ for t2 in combined:
1118
+ if t1 != t2 and bt.extends(t1, t2):
1119
+ to_remove.add(t2)
1120
+ for r in to_remove:
1121
+ combined.remove(r)
1122
+
1123
+ # Return single type or create a union
1124
+ return next(iter(combined)) if len(combined) == 1 else mm.UnionType(types=tuple(combined))
1125
+
1126
+ def invalid_type(type:mm.Type) -> bool:
1127
+ if isinstance(type, mm.UnionType):
1128
+ # if there are multiple primitives, or a primitive and a non-primitive
1129
+ # then we have an invalid type
1130
+ if len(type.types) > 1:
1131
+ return any([bt.is_primitive(t) for t in type.types])
1132
+ return False
1133
+
1134
+
1135
+ #--------------------------------------------------
1136
+ # Type Errors
1137
+ #--------------------------------------------------
1138
+
1139
+ @dataclass
1140
+ class TyperError():
1141
+ node: mm.Node
1142
+
1143
+ def report(self):
1144
+ err(self.name(), self.message(), self.parts())
1145
+
1146
+ def name(self) -> str:
1147
+ return type(self).__name__
1148
+
1149
+ def message(self) -> str:
1150
+ raise NotImplementedError()
1151
+
1152
+ def parts(self) -> list[Part]:
1153
+ if self.node.source is None:
1154
+ return [str(self.node)]
1155
+ return [str(self.node), Source(self.node.source)]
1156
+
1157
+ @dataclass
1158
+ class TypeMismatch(TyperError):
1159
+ expected: mm.Type
1160
+ actual: mm.Type
1161
+
1162
+ def message(self) -> str:
1163
+ return f"Expected {get_name(self.expected)}, got {get_name(self.actual)}"
1164
+
1165
+ @dataclass
1166
+ class InvalidType(TyperError):
1167
+ type: mm.Type
1168
+
1169
+ def message(self) -> str:
1170
+ return f"Incompatible types infered: {get_name(self.type)}"
1171
+
1172
+ @dataclass
1173
+ class UnresolvedOverload(TyperError):
1174
+ arg_types: list[mm.Type]
1175
+
1176
+ def message(self) -> str:
1177
+ assert isinstance(self.node, (mm.Lookup, mm.Update, mm.Aggregate))
1178
+ rel = get_relation(self.node)
1179
+ types = ', '.join([get_name(t) for t in self.arg_types])
1180
+ return f"Unresolved overload: {rel.name}({types})"
1181
+
1182
+ @dataclass
1183
+ class UnresolvedType(TyperError):
1184
+
1185
+ def message(self) -> str:
1186
+ return "Unable to determine concrete type."