relationalai 0.13.5__py3-none-any.whl → 1.0.0a2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +1719 -0
  20. relationalai/semantics/frontend/core.py +179 -0
  21. relationalai/semantics/frontend/front_compiler.py +1316 -0
  22. relationalai/semantics/frontend/pprint.py +408 -0
  23. relationalai/semantics/metamodel/__init__.py +6 -40
  24. relationalai/semantics/metamodel/builtins.py +206 -772
  25. relationalai/semantics/metamodel/metamodel.py +465 -0
  26. relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
  27. relationalai/semantics/metamodel/pprint.py +414 -0
  28. relationalai/semantics/metamodel/rewriter.py +266 -0
  29. relationalai/semantics/metamodel/typer.py +1213 -0
  30. relationalai/semantics/std/__init__.py +60 -40
  31. relationalai/semantics/std/aggregates.py +148 -0
  32. relationalai/semantics/std/common.py +44 -0
  33. relationalai/semantics/std/constraints.py +37 -43
  34. relationalai/semantics/std/datetime.py +249 -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 +179 -0
  43. relationalai/shims/helpers.py +126 -0
  44. relationalai/shims/hoister.py +221 -0
  45. relationalai/shims/mm2v0.py +1394 -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.0a2.dist-info/METADATA +44 -0
  67. relationalai-1.0.0a2.dist-info/RECORD +489 -0
  68. relationalai-1.0.0a2.dist-info/WHEEL +5 -0
  69. relationalai-1.0.0a2.dist-info/entry_points.txt +3 -0
  70. relationalai-1.0.0a2.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 +2478 -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 +325 -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 +474 -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 +877 -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 +490 -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 +776 -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 +554 -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 +9019 -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,1213 @@
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 that represents data
85
+ # dependencies between nodes. Nodes can be variables, literals, fields and tasks that refer
86
+ # to relations (lookups, aggregates, updates). An edge from node A to node B means that
87
+ # in order to resolve references and types for node B we need to know the type of node A.
88
+ #
89
+ # After building the network, we start with roots that have known types (literals, fields
90
+ # loaded from previous analysis and vars with concrete types) and propagate their types via
91
+ # the edges to other nodes. It is possible that the network contains cycles, so we iterate
92
+ # on the work list until fixpoint is reached, i.e. when we cannot propagate any new type.
93
+ #
94
+ # During the propagation, we are only gathering information about the types and references
95
+ # of nodes. Once we reach a fixpoint, we do a final pass to rewrite the model with the new
96
+ # type information, which may include adding casts to convert between types where needed.
97
+ #
98
+ Node = Union[mm.Var, mm.Field, mm.Literal, mm.Lookup, mm.Aggregate, mm.Update]
99
+
100
+ class PropagationNetwork():
101
+ def __init__(self, model: mm.Model):
102
+ # the model that is either being analyzed or that is the context for a query
103
+ self.model = model
104
+
105
+ # the resolved types of nodes in the network
106
+ self.resolved_types: dict[Node, mm.Type] = {}
107
+
108
+ # map from unresolved placeholder relations to their potential target replacements
109
+ self.potential_targets: dict[mm.Relation, list[mm.Relation]] = {}
110
+
111
+ # nodes loaded from previous analysis, which we use as roots to start propagation
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
+
118
+ # all errors collected during inference
119
+ self.errors:list[TyperError] = []
120
+
121
+ # overloads resolved for a lookup/update/aggregate, by node id. This is only for
122
+ # relations that declare overloads
123
+ self.resolved_overload:dict[int, mm.Overload] = {}
124
+
125
+ # placeholders resolved for a lookup, by node id. This is only for relations that
126
+ # are placeholders (i.e. only Any fields) and will be replaced by references to
127
+ # these concrete relations. E.g. a query for "name(Any, Any)" may be replaced by
128
+ # the union of "name(Dog, String)" and name(Cat, String)".
129
+ self.resolved_placeholder:dict[int, list[mm.Relation]] = {}
130
+
131
+ # for a given lookup/update/aggregate that involves numbers, the specific number
132
+ # type resolved for it.
133
+ self.resolved_number:dict[int, mm.NumberType] = {}
134
+
135
+ #--------------------------------------------------
136
+ # Error reporting
137
+ #--------------------------------------------------
138
+
139
+ def type_mismatch(self, node: Node|mm.Update, expected: mm.Type, actual: mm.Type):
140
+ self.errors.append(TypeMismatch(node, expected, actual))
141
+
142
+ def invalid_type(self, node: Node, type: mm.Type):
143
+ self.errors.append(InvalidType(node, type))
144
+
145
+ def unresolved_overload(self, node: mm.Lookup|mm.Aggregate):
146
+ # TODO - consider renaming this to UnresolvedReference
147
+ self.errors.append(UnresolvedOverload(node, [self.resolve(a) for a in node.args]))
148
+
149
+ def unresolved_type(self, node: Node):
150
+ # TODO - this is not being used yet, we need a pass at the end to check for any node
151
+ # that we could not resolve
152
+ self.errors.append(UnresolvedType(node))
153
+
154
+ def has_errors(self, node: Node) -> bool:
155
+ for mismatch in self.errors:
156
+ if mismatch.node == node:
157
+ return True
158
+ return False
159
+
160
+ #--------------------------------------------------
161
+ # Types and Edges
162
+ #--------------------------------------------------
163
+
164
+ def add_edge(self, source: Node, target: Node):
165
+ self.edges[source].add(target)
166
+ self.back_edges[target].add(source)
167
+
168
+ def add_resolved_type(self, node: Node, type: mm.Type):
169
+ """ Register that this node was resolved to have this type. """
170
+ if node in self.resolved_types:
171
+ self.resolved_types[node] = merge_types(self.resolved_types[node], type)
172
+ else:
173
+ self.resolved_types[node] = type
174
+
175
+ #--------------------------------------------------
176
+ # Load types from a previous analysis
177
+ #--------------------------------------------------
178
+
179
+ def load_types(self, type_dict: dict[Node, mm.Type]):
180
+ for node, type in type_dict.items():
181
+ if isinstance(node, (mm.Field)):
182
+ self.add_resolved_type(node, type)
183
+ self.loaded_roots.add(node)
184
+
185
+ #--------------------------------------------------
186
+ # Resolve Values
187
+ #--------------------------------------------------
188
+
189
+ def resolve(self, value: mm.Value) -> mm.Type:
190
+ if isinstance(value, (mm.Var, mm.Literal)):
191
+ return self.resolved_types.get(value) or to_type(value)
192
+ if isinstance(value, mm.Field):
193
+ return self.resolved_types.get(value) or value.type
194
+ assert not isinstance(value, (mm.Task)), "Should never try to resolve a task"
195
+ return to_type(value)
196
+
197
+ #--------------------------------------------------
198
+ # Resolve References
199
+ #--------------------------------------------------
200
+
201
+ def all_dependencies_resolved(self, op:mm.Lookup|mm.Aggregate):
202
+ """ True iff all dependencies required to resolve this reference are met. """
203
+ rel = get_relation(op)
204
+ # if this is a placeholder, eq or cast, we assume all possible args were resolved
205
+ if bt.is_placeholder(rel) or rel == b.core.eq or rel == b.core.cast:
206
+ return True
207
+
208
+ # else, find whether all back-edges were resolved
209
+ for node in self.back_edges[op]:
210
+ # cannot resolve if a required var, literal or input field is still abstract
211
+ if isinstance(node, (mm.Var, mm.Literal)) or (isinstance(node, mm.Field) and node.input):
212
+ node_type = self.resolve(node)
213
+ if bt.is_abstract(node_type):
214
+ return False
215
+ return True
216
+
217
+ def resolve_reference(self, op: mm.Lookup|mm.Aggregate) -> Optional[mm.Overload|list[mm.Relation]]:
218
+ # check if all dependencies required to resolve this reference are met
219
+ if not self.all_dependencies_resolved(op):
220
+ return None
221
+
222
+ relation = get_relation(op)
223
+ if bt.is_placeholder(relation):
224
+ # when replacing a placeholder, we may have multiple matches
225
+ matches = []
226
+ resolved_args = [self.resolve(arg) for arg in op.args]
227
+ for target in self.potential_targets.get(relation, []):
228
+ fields = get_relation_fields(target, relation.name)
229
+ if all(type_matches(arg, self.resolve(field))
230
+ for arg, field in zip(resolved_args, fields)):
231
+ matches.append(target)
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
+
247
+ else:
248
+ # this is a relation with type vars or numbers that needs to be specialized
249
+ return [relation]
250
+
251
+ #--------------------------------------------------
252
+ # Propagation
253
+ #--------------------------------------------------
254
+
255
+ def propagate(self):
256
+ edges = self.edges
257
+ work_list = []
258
+
259
+ # start with the loaded roots + all literals + sources of edges without back edges
260
+ work_list.extend(self.loaded_roots)
261
+ for source in self.edges.keys():
262
+ if isinstance(source, (mm.Literal)) or not source in self.back_edges:
263
+ work_list.append(source)
264
+
265
+ # limit the number of iterations to avoid infinite loops
266
+ i = 0
267
+ max_iterations = 100 * len(self.edges)
268
+
269
+ # propagate types until we reach a fixed point
270
+ while work_list:
271
+ i += 1
272
+ if i > max_iterations:
273
+ err("Infinite Loop", "Infinite loop detected in the typer. Please, report this as a bug.")
274
+ break
275
+
276
+ node = work_list.pop(0)
277
+ next = None
278
+ if isinstance(node, mm.Field):
279
+ # this is a field loaded from a previous analysis, so all we need to do is
280
+ # propagate its type to its output edges
281
+ next = edges.get(node, [])
282
+
283
+ elif isinstance(node, (mm.Lookup, mm.Aggregate)):
284
+ # this is a lookup/aggregate that may be overloaded or a placeholder; try
285
+ # to resolve its reference, i.e. determine which specific relation or
286
+ # overload it refers to
287
+ found = self.resolve_reference(node)
288
+ if found is not None:
289
+ # if found is None it means that we need more info to resolve the
290
+ # reference; otherwise it was possible to resolve it (even if to no matches)
291
+
292
+ # keep the resolved args before propagation to see if they will change
293
+ resolved_args = [self.resolve(arg) for arg in node.args]
294
+ # propagate the reference resolution
295
+ self.propagate_reference(node, resolved_args, found)
296
+ if found:
297
+ # the next nodes to process are all the outgoing edges plus any arg that
298
+ # changed during propagation
299
+ next = OrderedSet()
300
+ next.update(edges.get(node, []))
301
+ next.update([arg for idx, arg in enumerate(node.args) if isinstance(arg, mm.Var) and not (self.resolve(arg) == resolved_args[idx])])
302
+ else:
303
+ # the reference is unresolved, so remove from the worklist the args
304
+ # because they depend on this being resolved
305
+ for arg in node.args:
306
+ if arg in work_list:
307
+ work_list.remove(arg)
308
+ else:
309
+ assert isinstance(node, (mm.Var, mm.Literal))
310
+ resolved = self.resolve(node)
311
+ # if we ended up with an invalid type, report it on a next edge (which is
312
+ # where the var or literal is going to be used)
313
+ if invalid_type(resolved):
314
+ self.invalid_type(edges.get(node, [node])[0], resolved)
315
+
316
+ # if the var is still abstract, add it to the work list to try again later
317
+ if isinstance(node, mm.Var) and bt.is_abstract(resolved):
318
+ # but if everything that is left are vars, we cannot make progress
319
+ if not all(isinstance(x, mm.Var) for x in work_list):
320
+ next = [node]
321
+ else:
322
+ # otherwise, check all outgoing edges
323
+ next = []
324
+ for out in edges.get(node, []):
325
+ if isinstance(out, mm.Update):
326
+ # this is an update to a field. We have to check that the type is
327
+ # valid for the field and if the field is abstract we can refine the
328
+ # type. But if the field is concrete we have to check that the type
329
+ # matche or can be converted.
330
+ is_population_lookup = isinstance(out.relation, mm.TypeNode)
331
+ for field in get_update_fields(node, out):
332
+ if not type_matches(resolved, field.type, accept_expected_super_types=is_population_lookup) and not conversion_allowed(resolved, field.type):
333
+ self.type_mismatch(out, field.type, resolved)
334
+ elif bt.is_abstract(field.type):
335
+ # if the field type changed, propagate further
336
+ if resolved != self.resolve(field):
337
+ next.append(field)
338
+ self.add_resolved_type(field, resolved)
339
+ else:
340
+ # this is an arg flowing into a task, so resolve the task next
341
+ next.append(out)
342
+
343
+ # add the new nodes to the work list
344
+ if next is not None:
345
+ for n in next:
346
+ if n not in work_list:
347
+ work_list.append(n)
348
+
349
+
350
+ def propagate_reference(self, task:mm.Lookup|mm.Aggregate, resolved_args:list[mm.Type], references:mm.Overload|list[mm.Relation]):
351
+ # TODO: distinguish between overloads and placeholders better when raising errors
352
+ if not references:
353
+ return self.unresolved_overload(task)
354
+
355
+ relation = get_relation(task)
356
+
357
+ if relation == b.core.cast:
358
+ # cast is special cased: we just propagate the type to the target
359
+ cast_type, target = task.args[0], task.args[2]
360
+ assert isinstance(cast_type, mm.Type)
361
+ assert isinstance(target, mm.Var)
362
+ self.add_resolved_type(target, cast_type)
363
+ return
364
+
365
+ if bt.is_placeholder(relation):
366
+ assert(references and isinstance(references, list))
367
+ # we've resolved the placeholder, so store that
368
+ self.resolved_placeholder[task.id] = references
369
+
370
+ # we need to determine the final types of our args by taking all the references
371
+ # and adding the type of their fields back to the args.
372
+ for reference in references:
373
+ resolved_fields = [self.resolve(f) for f in get_relation_fields(reference, relation.name)]
374
+ for field_type, arg_type, arg in zip(resolved_fields, resolved_args, task.args):
375
+ if bt.is_abstract(arg_type) and isinstance(arg, mm.Var):
376
+ self.add_resolved_type(arg, field_type)
377
+ else:
378
+ if isinstance(references, mm.Overload):
379
+ # we resolved an overload, use its types
380
+ types = list(references.types)
381
+ else:
382
+ # we resolved to a single concrete relation that contains type vars or needs
383
+ # number specialization, so use that relation's field types
384
+ types = list([self.resolve(f) for f in relation.fields])
385
+
386
+ # if our overload preserves types, we check to see if there's a preserved output
387
+ # type given the inputs and if so, shadow the field type with the preserved type.
388
+ # this is only attempted if all input types match the field types, i.e. no
389
+ # conversions are needed
390
+ resolved_fields = types
391
+ if bt.is_function(relation) and len(set(resolved_fields)) == 1 and not relation in self.NON_TYPE_PRESERVERS and\
392
+ all(type_matches(arg_type, field_type) for arg_type, field_type, field in zip(resolved_args, types, relation.fields) if field.input):
393
+
394
+ input_types = set([arg_type for field, arg_type
395
+ in zip(relation.fields, resolved_args) if field.input])
396
+ if out_type := self.try_preserve_type(input_types):
397
+ resolved_fields = [field_type if field.input else out_type
398
+ for field, field_type in zip(relation.fields, types)]
399
+
400
+
401
+ # eq is special cased because we don't want to specialize a number for it, as it
402
+ # can be just comparing numbers of different types.
403
+ if relation != b.core.eq and (b.core.Number in types or (b.core.TypeVar in types and any(bt.is_number(t) for t in resolved_args))):
404
+ # this relation contains generic numbers or typevars bound to numbers, so
405
+ # find which specific type of number to use given the arguments being passed
406
+ number, resolved_fields = self.specialize_number(relation, resolved_fields, resolved_args)
407
+ self.resolved_number[task.id] = number
408
+ # push the specialized number to the outputs
409
+ for field, field_type, arg in zip(relation.fields, resolved_fields, task.args):
410
+ if not field.input and isinstance(arg, mm.Var):
411
+ self.add_resolved_type(arg, field_type)
412
+
413
+ elif b.core.TypeVar in types:
414
+ # this relation contains type vars, so we have to make sure that all args
415
+ # bound to the same type var are consistent
416
+
417
+ # find which arg is bound to the type var and check that they are all consistent
418
+ typevar_type = None
419
+ for arg_type, field_type in zip(resolved_args, types):
420
+ if field_type == b.core.TypeVar:
421
+ if typevar_type is None:
422
+ typevar_type = arg_type
423
+ else:
424
+ typevar_type = merge_types(typevar_type, arg_type)
425
+ assert typevar_type is not None
426
+
427
+ # compute the final arg types by replacing type vars with the typevar_type
428
+ computed_arg_types = []
429
+ for arg_type, field_type in zip(resolved_args, types):
430
+ # check that the arg type matches the type var type
431
+ if not type_matches(typevar_type, arg_type) and not conversion_allowed(arg_type, typevar_type):
432
+ self.type_mismatch(task, typevar_type, arg_type)
433
+ if field_type == b.core.TypeVar:
434
+ computed_arg_types.append(typevar_type)
435
+ else:
436
+ computed_arg_types.append(arg_type)
437
+
438
+ # if no mismatches were found, propagate the computed arg types back to the args
439
+ if len(computed_arg_types) == len(task.args):
440
+ for computed_type, arg, arg_type in zip(computed_arg_types, task.args, resolved_args):
441
+ # TODO: we could allow for non-numeric/string literals because this means
442
+ # the literal is being used as a value type, but the backend emitters
443
+ # would have to deal with that.
444
+ if isinstance(arg, mm.Var) or (isinstance(arg, mm.Literal) and (bt.is_numeric(computed_type) or computed_type == b.core.String)):
445
+ self.add_resolved_type(arg, computed_type)
446
+
447
+ else:
448
+ # no typevar or number specialization shenanigans, just propagate field types to args
449
+ for field, field_type, arg, arg_type in zip(relation.fields, resolved_fields, task.args, resolved_args):
450
+ # add a resolved type if we learned more about the arg's type
451
+ if isinstance(arg, mm.Var) and (bt.is_abstract(arg_type) or bt.extends(field_type, arg_type)):
452
+ self.add_resolved_type(arg, field_type)
453
+ elif not type_matches(arg_type, field_type) and not conversion_allowed(arg_type, field_type):
454
+ self.type_mismatch(task, field_type, arg_type)
455
+
456
+
457
+ # we try to preserve types for relations that are functions (i.e. potentially multiple
458
+ # input but a single output) and where all types are the same. However, there are some
459
+ # exceptions to this rule, e.g. range() always returns int regardless of whether the
460
+ # input type is an int or a value type that extends int.
461
+ NON_TYPE_PRESERVERS = [
462
+ b.common.range
463
+ ]
464
+
465
+ def try_preserve_type(self, types:set[mm.Type]) -> Optional[mm.Type]:
466
+ # we keep the input type as the output type if either all inputs are the exact same
467
+ # type or there's one nominal and its base primitive type, e.g. USD + Decimal
468
+ if len(types) == 1:
469
+ return next(iter(types))
470
+ if len(types) == 2:
471
+ t1, t2 = types
472
+ t1_base = bt.get_primitive_supertype(t1)
473
+ t2_base = bt.get_primitive_supertype(t2)
474
+ if t1_base is None or t2_base is None:
475
+ base_equivalent = type_matches(t1, t2, accept_expected_super_types=True)
476
+ else:
477
+ base_equivalent = type_matches(t1_base, t2_base)
478
+ if base_equivalent:
479
+ # as long as one of the types is a base primitive, we can use the
480
+ # other type as final preserved type
481
+ if bt.is_primitive(t1):
482
+ return t2
483
+ elif bt.is_primitive(t2):
484
+ return t1
485
+ return None
486
+
487
+ def specialize_number(self, op, field_types:list[mm.Type], arg_types:list[mm.Type]) -> Tuple[mm.NumberType, list[mm.Type]]:
488
+ """
489
+ Find the number type to use for an overload that has Number in its field_types, and
490
+ which is being referred to with these arg_types.
491
+
492
+ Return a tuple where the first element is the specialized number type, and the second
493
+ element is a new list that contains the same types as field_types but with
494
+ Number replaced by this specialized number.
495
+ """
496
+ # special case a few operators according to Snowflake's rules in
497
+ # https://docs.snowflake.com/en/sql-reference/operators-arithmetic#scale-and-precision-in-arithmetic-operations
498
+ if op == b.core.div:
499
+ # see https://docs.snowflake.com/en/sql-reference/operators-arithmetic#division
500
+ numerator, denominator = get_number_type(arg_types[0]), get_number_type(arg_types[1])
501
+ s = max(numerator.scale, min(numerator.scale + 6, 12))
502
+ number = mm.NumberType(name=f"Number(38,{s})", precision=38, scale=s, source=op.source, super_types=(b.core.Numeric,))
503
+ return number, [numerator, denominator, number]
504
+ elif op == b.core.mul:
505
+ # see https://docs.snowflake.com/en/sql-reference/operators-arithmetic#multiplication
506
+ t1, t2 = get_number_type(arg_types[0]), get_number_type(arg_types[1])
507
+ S1 = t1.scale
508
+ S2 = t2.scale
509
+ s = min(S1 + S2, max(S1, S2, 12))
510
+ number = mm.NumberType(name=f"Number(38,{s})", precision=38, scale=s, source=op.source, super_types=(b.core.Numeric,))
511
+ return number, [t1, t2, number]
512
+ elif op == b.aggregates.avg:
513
+ # TODO!! - implement proper avg specialization
514
+ pass
515
+
516
+ # fall back to the the current specialization policy, which is to select the number
517
+ # with largest scale and, if there multiple with the largest scale, the one with the
518
+ # largest precision. This is safe because when converting a number to the
519
+ # specialized number, we never truncate fractional digits (because we selected the
520
+ # largest scale) and, if the non-fractional digits are too large to fit the
521
+ # specialized number, we will have a runtime overflow, which should alert the user
522
+ # of the problem.
523
+ number = None
524
+ for arg_type in arg_types:
525
+ x = bt.get_number_supertype(arg_type)
526
+ if isinstance(x, mm.NumberType):
527
+ if number is None or x.scale > number.scale or (x.scale == number.scale and x.precision > number.precision):
528
+ number = x
529
+ assert(number is not None)
530
+ return number, [number if bt.is_number(t) else t for t in field_types]
531
+
532
+
533
+ #--------------------------------------------------
534
+ # Display
535
+ #--------------------------------------------------
536
+
537
+ # draw the network as a mermaid graph for the debugger
538
+ def to_mermaid(self, max_edges=500) -> str:
539
+ # add links for edges while collecting nodes
540
+ nodes = OrderedSet()
541
+ link_strs = []
542
+ # edges
543
+ for src, dsts in self.edges.items():
544
+ nodes.add(src)
545
+ for dst in dsts:
546
+ if len(link_strs) > max_edges:
547
+ break
548
+ nodes.add(dst)
549
+ link_strs.append(f"n{src.id} --> n{dst.id}")
550
+ if len(link_strs) > max_edges:
551
+ break
552
+
553
+ def type_span(t:mm.Type) -> str:
554
+ type_str = t.name if isinstance(t, mm.ScalarType) else str(t)
555
+ return f"<span style='color:cyan;'>{type_str.strip()}</span>"
556
+
557
+ def reference_span(rel:mm.Relation, arg_types:list[mm.Type]) -> str:
558
+ args = []
559
+ for field, arg_type in zip(rel.fields, arg_types):
560
+ field_type = self.resolve(field)
561
+ if not type_matches(arg_type, field_type) and not conversion_allowed(arg_type, field_type) and not bt.is_abstract(field_type):
562
+ args.append(f"<span style='color:yellow;'>{str(arg_type).strip()} -> {str(field_type).strip()}</span>")
563
+ elif isinstance(arg_type, mm.UnionType):
564
+ args.append(type_span(field_type))
565
+ else:
566
+ args.append(type_span(arg_type))
567
+ return f'{rel.name}({", ".join(args)})'
568
+
569
+ resolved = self.resolved_types
570
+ node_strs = []
571
+ for node in nodes:
572
+ klass = ""
573
+ if isinstance(node, mm.Var):
574
+ ir_type = resolved.get(node) or self.resolve(node)
575
+ type_str = type_span(ir_type)
576
+ label = f'(["{node.name}:{type_str}"])'
577
+ elif isinstance(node, mm.Literal):
578
+ ir_type = resolved.get(node) or self.resolve(node)
579
+ type_str = type_span(ir_type)
580
+ klass = ":::literal"
581
+ label = f'(["{node.value}: {type_str}"])'
582
+ elif isinstance(node, mm.Field):
583
+ ir_type = resolved.get(node) or self.resolve(node)
584
+ type_str = type_span(ir_type)
585
+ klass = ":::field"
586
+ rel = node._relation
587
+ if rel is not None:
588
+ rel = str(node._relation)
589
+ label = f'[/"{node.name}:{type_str}\nfrom {rel}"\\]'
590
+ else:
591
+ label = f'[/"{node.name}:\n{type_str}"\\]'
592
+ elif isinstance(node, (mm.Lookup, mm.Aggregate, mm.Update)):
593
+ arg_types = [self.resolve(arg) for arg in node.args]
594
+ if node.id in self.resolved_placeholder:
595
+ overloads = self.resolved_placeholder[node.id]
596
+ content = "<br/>".join([reference_span(o, arg_types) for o in overloads])
597
+ else:
598
+ content = reference_span(get_relation(node), arg_types)
599
+ if isinstance(node, mm.Update):
600
+ klass = ":::update"
601
+ label = f'{{{{"{content}"}}}}'
602
+ else:
603
+ klass = ":::reference"
604
+ label = f'[/"{content}"/]'
605
+ else:
606
+ raise NotImplementedError(f"Unknown node type: {type(node)}")
607
+ if self.has_errors(node):
608
+ klass = ":::error"
609
+ node_strs.append(f'n{node.id}{label}{klass}')
610
+
611
+ node_str = "\n ".join(node_strs)
612
+ link_str = "\n ".join(link_strs)
613
+ template = f"""
614
+ %%{{init: {{'theme':'dark', 'flowchart':{{'useMaxWidth':false, 'htmlLabels': true}}}}}}%%
615
+ flowchart TD
616
+ linkStyle default stroke:#666
617
+ classDef field fill:#245,stroke:#478
618
+ classDef update fill:#245,stroke:#478
619
+ classDef literal fill:#452,stroke:#784
620
+ classDef error fill:#624,stroke:#945,color:#f9a
621
+ classDef default stroke:#444,stroke-width:2px, font-size:12px
622
+
623
+ %% nodes
624
+ {node_str}
625
+
626
+ %% edges
627
+ {link_str}
628
+ """
629
+ return template
630
+
631
+ #--------------------------------------------------
632
+ # Analyzer
633
+ #--------------------------------------------------
634
+
635
+ class Analyzer(Walker):
636
+ """ Walks the metamodel and builds the propagation network. """
637
+
638
+ def __init__(self, net:PropagationNetwork):
639
+ super().__init__()
640
+ self.net = net
641
+
642
+ def analyze(self, node: mm.Node):
643
+ self(node)
644
+
645
+ # TODO - ignoring requires for now because the typing of constraints seems incorrect
646
+ def enter_require(self, require: mm.Require):
647
+ return NO_WALK
648
+
649
+ def compute_potential_targets(self, relation: mm.Relation):
650
+ # register potential targets for placeholders
651
+ if bt.is_placeholder(relation):
652
+ self.net.potential_targets[relation] = get_potential_targets(self.net.model, relation)
653
+
654
+ #--------------------------------------------------
655
+ # Walk Update
656
+ #--------------------------------------------------
657
+
658
+ def update(self, node: mm.Update):
659
+ rel = node.relation
660
+ self.compute_potential_targets(rel)
661
+
662
+ # arg is flowing into a field
663
+ for arg, field in zip(node.args, rel.fields):
664
+ if isinstance(arg, (mm.Var, mm.Literal)):
665
+ self.net.add_edge(arg, node)
666
+
667
+ #--------------------------------------------------
668
+ # Walk Lookups + Aggregates
669
+ #--------------------------------------------------
670
+
671
+ def lookup(self, task: mm.Lookup):
672
+ self.compute_potential_targets(task.relation)
673
+ self.visit_rel_op(task)
674
+
675
+ def aggregate(self, task: mm.Aggregate):
676
+ self.visit_rel_op(task)
677
+
678
+ def visit_rel_op(self, task: mm.Lookup|mm.Aggregate):
679
+ relation = get_relation(task)
680
+
681
+ is_placeholder = bt.is_placeholder(relation)
682
+ for field, arg in zip(relation.fields, task.args):
683
+ if isinstance(arg, (mm.Var, mm.Literal)):
684
+ if field.input:
685
+ # we need to resolve all inputs before resolving the relation
686
+ self.net.add_edge(arg, task)
687
+ else:
688
+ # placeholders also need the output to be resolved
689
+ if is_placeholder:
690
+ self.net.add_edge(arg, task)
691
+ else:
692
+ # args bound to outputs can be resolved after
693
+ self.net.add_edge(task, arg)
694
+ # if the field is abstract, it needs to be resolved before we can
695
+ # resolve the task.
696
+ if bt.is_abstract(self.net.resolve(field)):
697
+ self.net.add_edge(field, task)
698
+
699
+
700
+ #--------------------------------------------------
701
+ # Replacer
702
+ #--------------------------------------------------
703
+
704
+ # Once we've pushed all the types through the network, we need to replace the types of
705
+ # fields and vars that we may have discovered. We also need to replace placeholder lookups
706
+ # with the chosen relations and do any conversions that are needed.
707
+ class Replacer(Rewriter):
708
+ def __init__(self, net:PropagationNetwork):
709
+ super().__init__()
710
+ self.net = net
711
+
712
+ def rewrite(self, model: T) -> T:
713
+ return self(model) # type: ignore
714
+
715
+ def logical(self, logical: mm.Logical):
716
+ if len(logical.body) == 0:
717
+ return logical
718
+ # inline logicals that are just there to group other nodes during rewrite
719
+ body = []
720
+ for child in logical.body:
721
+ if isinstance(child, mm.Logical) and not child.optional and not child.scope:
722
+ body.extend(child.body)
723
+ else:
724
+ body.append(child)
725
+ return logical.mut(body = tuple(body))
726
+
727
+ #--------------------------------------------------
728
+ # Rewriter handlers
729
+ #--------------------------------------------------
730
+
731
+ def field(self, node: mm.Field):
732
+ # TODO - this is only modifying the relation in the model, but then we have a new
733
+ # relation there, which is different than the object referenced by tasks.
734
+ if node in self.net.resolved_types:
735
+ return mm.Field(node.name, self.net.resolved_types[node], node.input, _relation = node._relation)
736
+ return node
737
+
738
+ def var(self, node: mm.Var):
739
+ if node in self.net.resolved_types:
740
+ return mm.Var(self.net.resolved_types[node], node.name)
741
+ return node
742
+
743
+ def literal(self, node: mm.Literal):
744
+ if node in self.net.resolved_types:
745
+ return mm.Literal(self.net.resolved_types[node], node.value)
746
+ return node
747
+
748
+ def update(self, node: mm.Update):
749
+ return self.convert_arguments(node, node.relation)
750
+
751
+ def lookup(self, node: mm.Lookup):
752
+ # We need to handle eq specially because its arguments can be converted symmetrically
753
+ if node.relation == b.core.eq:
754
+ return self.visit_eq_lookup(node)
755
+
756
+ args = types = None
757
+ if node.id in self.net.resolved_placeholder:
758
+ # placeholder resolved to multiple relations
759
+ resolved_relations = self.net.resolved_placeholder[node.id]
760
+ args = get_lookup_args(node, resolved_relations[0])
761
+ types = [f.type for f in resolved_relations[0].fields]
762
+ elif node.id in self.net.resolved_overload:
763
+ # overload resolved to a specific relation
764
+ resolved_relations = [node.relation]
765
+ types = self.net.resolved_overload[node.id].types
766
+ else:
767
+ # single relation
768
+ resolved_relations = [node.relation]
769
+
770
+ if len(resolved_relations) == 1:
771
+ # single relation, just convert arguments
772
+ x = self.convert_arguments(node, resolved_relations[0], args, types)
773
+ if isinstance(x, mm.Logical) and len(x.body) == 1:
774
+ return x.body[0]
775
+ else:
776
+ return x
777
+
778
+ # multiple relations, create a union
779
+ branches:list = []
780
+ for target in resolved_relations:
781
+ args = get_lookup_args(node, target)
782
+ types = [f.type for f in get_relation_fields(resolved_relations[0], node.relation.name)]
783
+ # adding this logical to avoid issues in the old backend
784
+ branches.append(mm.Logical((self.convert_arguments(node, target, args, types=types, force_copy=True),)))
785
+ return mm.Union(tuple(branches))
786
+
787
+ def convert_arguments(self, node: mm.Lookup|mm.Update, relation: mm.Relation, args: Iterable[mm.Value]|None=None, types: Iterable[mm.Type]|None=None, force_copy=False) -> mm.Logical|mm.Lookup|mm.Update:
788
+ """ This node was resolved to target this relation using these args, which should
789
+ have these types. Convert any arguments as needed and return a new node with the
790
+ proper relation and converted args. If multiple conversions are needed, return a
791
+ logical that contains all the conversion tasks plus the final node. """
792
+ args = args or node.args
793
+ types = types or [self.net.resolve(f) for f in relation.fields]
794
+ number_type = self.net.resolved_number.get(node.id)
795
+ is_function = bt.is_function(relation)
796
+ tasks = []
797
+ final_args = []
798
+ for arg, field, field_type in zip(args, relation.fields, types):
799
+ if isinstance(arg, (mm.Var, mm.Literal)) and (not is_function or field.input):
800
+ arg_type = to_type(arg)
801
+ if number_type and bt.is_number(arg_type) and not arg_type == b.core.ScaledNumber:
802
+ field_type = number_type
803
+ # the typer previously made sure that this should be valid so a type mismatch
804
+ # means we need to convert
805
+ if not type_matches(arg_type, field_type):
806
+ final_args.append(convert(arg, field_type, tasks))
807
+ else:
808
+ final_args.append(arg)
809
+ else:
810
+ final_args.append(arg)
811
+ # add the original node with the proper target relation and converted args;
812
+ # here we want to use mut because we keep information about nodes based on their ids
813
+ # (e.g. we store resolved types based on ids), so we want to keep the same id. But
814
+ # when a lookup is being converted as part of a union (i.e. multiple targets for a
815
+ # placeholder), we need to create nodes with new ids to avoid conflicts.
816
+ if force_copy:
817
+ tasks.append(node.replace(relation = relation, args = tuple(final_args)))
818
+ else:
819
+ tasks.append(node.mut(relation = relation, args = tuple(final_args)))
820
+ # if we need conversion tasks, wrap in a logical
821
+ if len(tasks) == 1:
822
+ return tasks[0]
823
+ return mm.Logical(tuple(tasks))
824
+
825
+ def visit_eq_lookup(self, node: mm.Lookup):
826
+ (left, right) = node.args
827
+ left_type = to_type(left)
828
+ right_type = to_type(right)
829
+
830
+ if type_matches(left_type, right_type):
831
+ return node
832
+
833
+ assert isinstance(left, (mm.Var, mm.Literal)) and isinstance(right, (mm.Var, mm.Literal))
834
+ final_args = []
835
+ tasks = []
836
+ if conversion_allowed(left_type, right_type):
837
+ final_args = [convert(left, right_type, tasks), right]
838
+ elif conversion_allowed(right_type, left_type):
839
+ final_args = [left, convert(right, left_type, tasks)]
840
+ else:
841
+ # this type mismatch was reported during propagation, so just return the node
842
+ return node
843
+
844
+ tasks.append(mm.Lookup(b.core.eq, tuple(final_args)))
845
+ return mm.Logical(tuple(tasks))
846
+
847
+ #--------------------------------------------------
848
+ # Helpers
849
+ #--------------------------------------------------
850
+
851
+ def get_relation(node: mm.Lookup|mm.Update|mm.Aggregate) -> mm.Relation:
852
+ if isinstance(node, mm.Aggregate):
853
+ return node.aggregation
854
+ return node.relation
855
+
856
+ def get_name(type: mm.Type) -> str:
857
+ if isinstance(type, mm.ScalarType):
858
+ return type.name
859
+ elif isinstance(type, mm.UnionType):
860
+ return '|'.join([get_name(t) for t in type.types])
861
+ elif isinstance(type, mm.ListType):
862
+ return f'List[{get_name(type.element_type)}]'
863
+ elif isinstance(type, mm.TupleType):
864
+ return f'Tuple[{", ".join([get_name(t) for t in type.element_types])}]'
865
+ else:
866
+ raise TypeError(f"Unknown type: {type}")
867
+
868
+ #--------------------------------------------------
869
+ # Type and Relation helpers
870
+ #--------------------------------------------------
871
+
872
+ def get_update_fields(arg, update: mm.Update) -> Iterable[mm.Field]:
873
+ """ Get the fields of the relation being updated by this arg. Note that an arg can be
874
+ bound to multiple fields at the same time. """
875
+ if arg in update.args:
876
+ for x, field in zip(update.args, update.relation.fields):
877
+ if arg == x:
878
+ yield field
879
+ return []
880
+
881
+ def get_relation_fields(relation: mm.Relation, name: str) -> Iterable[mm.Field]:
882
+ """ Get the fields of this relation, potentially reordered to match the reading with the given name."""
883
+ if name == relation.name:
884
+ return relation.fields
885
+ for reading in relation.readings:
886
+ if reading.name == name:
887
+ # reorder the fields to match the correct ordering
888
+ fields = []
889
+ for idx in reading.field_order:
890
+ fields.append(relation.fields[idx])
891
+ return fields
892
+ return []
893
+
894
+ def get_lookup_args(node: mm.Lookup, target: mm.Relation):
895
+ """ Get the args of this lookup, potentially reordered to match the reading with the given name."""
896
+ for reading in target.readings:
897
+ if reading.name == node.relation.name:
898
+ # reorder the args to match the correct ordering
899
+ args = []
900
+ for idx in reading.field_order:
901
+ args.append(node.args[idx])
902
+ return args
903
+ return node.args
904
+
905
+ def get_number_type(t: mm.Type) -> mm.NumberType:
906
+ # Get a number type from the given type, if it is Number return the default number
907
+ x = bt.get_number_supertype(t)
908
+ if isinstance(x, mm.NumberType):
909
+ return x
910
+ return b.core.DefaultNumber
911
+
912
+ def is_potential_target(placeholder: mm.Relation, target: mm.Relation) -> bool:
913
+ """ Whether this target is matches the placeholder signature and, thus, can be a potential target. """
914
+ if placeholder != target and len(placeholder.fields) == len(target.fields) and not bt.is_placeholder(target):
915
+ return placeholder.name == target.name or any(placeholder.name == reading.name for reading in target.readings)
916
+ return False
917
+
918
+ def get_potential_targets(model: mm.Model, placeholder: mm.Relation) -> list[mm.Relation]:
919
+ """ Get all potential target relations in the model that match the placeholder signature. """
920
+ return list(filter(lambda r: is_potential_target(placeholder, r), model.relations))
921
+
922
+ def to_type(value: mm.Value|mm.Field|mm.Literal) -> mm.Type:
923
+ if isinstance(value, (mm.Var, mm.Literal)):
924
+ return value.type
925
+
926
+ if isinstance(value, mm.Type):
927
+ return b.core.Type
928
+
929
+ if isinstance(value, mm.Field):
930
+ return b.core.Field
931
+
932
+ if isinstance(value, tuple):
933
+ return mm.TupleType(element_types=tuple(to_type(v) for v in value))
934
+
935
+ raise TypeError(f"Cannot determine IR type for value: {value} of type {type(value).__name__}")
936
+
937
+ def convert(value: mm.Var|mm.Literal, to_type: mm.Type, tasks: list[mm.Task]) -> mm.Value:
938
+ # if the arg is a literal, we can just change its type
939
+ # TODO - we may want to check that the value is actually convertible
940
+ if isinstance(value, mm.Literal):
941
+ return mm.Literal(to_type, value.value)
942
+
943
+ # otherise we need to add a cast
944
+ name = sanitize(value.name + "_" + get_name(to_type))
945
+ to_type_base = bt.get_primitive_supertype(to_type) or to_type
946
+ new_value = mm.Var(to_type_base, name)
947
+ tasks.append(mm.Lookup(b.core.cast, (to_type_base, value, new_value)))
948
+ return new_value
949
+
950
+ def conversion_allowed(from_type: mm.Type, to_type: mm.Type) -> bool:
951
+ # value type conversion is allowed only if the value types are related by inheritance
952
+ if bt.is_value_type(from_type) and bt.is_value_type(to_type) and not bt.extends(from_type, to_type):
953
+ return False
954
+ # value type conversion is allowed
955
+ x = bt.get_primitive_supertype(from_type)
956
+ y = bt.get_primitive_supertype(to_type)
957
+ if x and y and (x != from_type or y != to_type) and conversion_allowed(x, y):
958
+ return True
959
+
960
+ # numbers can be converted to floats
961
+ if bt.is_numeric(from_type) and to_type == b.core.Float:
962
+ return True
963
+
964
+ # a number can be converted to another number of larger scale
965
+ if isinstance(from_type, mm.NumberType) and isinstance(to_type, mm.NumberType):
966
+ if to_type.scale >= from_type.scale:
967
+ return True
968
+
969
+ if from_type == b.core.Number and isinstance(to_type, mm.NumberType):
970
+ return True
971
+
972
+ return False
973
+
974
+ def type_matches(actual: mm.Type, expected: mm.Type, accept_expected_super_types=False) -> bool:
975
+ """
976
+ True iff we can use a value of the actual type when expecting the expected type, without
977
+ conversions.
978
+
979
+ Any super-type of `actual` can match `expected`. For example if we expect a `Person`, we
980
+ can use an `Employee` if `Employee < Person`.
981
+
982
+ In general, the other way around is not true: if we expect an `Employee` we cannot use a
983
+ `Person` instead.
984
+
985
+ However, when the relation is a Type (previsously known as "population relations"), it
986
+ is valid to provide sub-types of the expected type. For example, `Employee(Person)` is
987
+ a valid way to check that `Person` is an `Employee` on a `Lookup`, or to assert that a
988
+ particular `Person` is an `Employee` on an `Update`.
989
+ """
990
+ # exact match
991
+ if actual == expected:
992
+ return True
993
+
994
+ # any matches anything
995
+ if actual == b.core.Any or expected == b.core.Any:
996
+ return True
997
+
998
+ # type vars match anything
999
+ if expected == b.core.TypeVar:
1000
+ return True
1001
+
1002
+ # if an entity type var or any entity is expected, it matches any actual entity type
1003
+ if (expected == b.core.EntityTypeVar or bt.extends(expected, b.core.AnyEntity)) and not bt.is_primitive(actual):
1004
+ return True
1005
+
1006
+ # the abstract Number type and the number type variable match any number type
1007
+ if (expected == b.core.Number) and bt.is_number(actual):
1008
+ return True
1009
+
1010
+ if (expected == b.core.Numeric) and bt.is_numeric(actual):
1011
+ return True
1012
+
1013
+ # different value types never match
1014
+ if bt.is_value_type(actual) and bt.is_value_type(expected) and not bt.extends(actual, expected):
1015
+ return False
1016
+
1017
+ # if actual is scalar, any of its parents may match the expected type
1018
+ if isinstance(actual, mm.ScalarType) and any([type_matches(parent, expected) for parent in actual.super_types]):
1019
+ return True
1020
+
1021
+ # 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
1022
+ 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]):
1023
+ return True
1024
+
1025
+ # if we expect a union, the actual can match any of its types
1026
+ if isinstance(expected, mm.UnionType):
1027
+ for t in expected.types:
1028
+ if type_matches(t, actual, accept_expected_super_types):
1029
+ return True
1030
+
1031
+ # if actual is a union, every one of its types must match the expected type
1032
+ # if isinstance(actual, mm.UnionType):
1033
+ # for t in actual.types:
1034
+ # if not type_matches(t, expected, accept_expected_super_types):
1035
+ # return False
1036
+ # return True
1037
+ # TODO - we have to distinguish between when we are checking that a specific arg matches
1038
+ # a relation vs when we are selecting relations for the placeholders; then we have to
1039
+ # decide between the above and this.
1040
+ if isinstance(actual, mm.UnionType):
1041
+ for t in actual.types:
1042
+ if type_matches(t, expected, accept_expected_super_types):
1043
+ return True
1044
+
1045
+ # a list type matches if their element types match
1046
+ if isinstance(actual, (mm.ListType, mm.TupleType)) and isinstance(expected, mm.ListType):
1047
+ if isinstance(actual, mm.TupleType):
1048
+ for et in actual.element_types:
1049
+ if not type_matches(et, expected.element_type):
1050
+ return False
1051
+ return True
1052
+ return type_matches(actual.element_type, expected.element_type)
1053
+
1054
+ # a tuple types match if any of all their types match
1055
+ if isinstance(actual, mm.TupleType) and isinstance(expected, mm.TupleType):
1056
+ return all([type_matches(ae, ee) for ae, ee in zip(actual.element_types, expected.element_types)])
1057
+
1058
+ # accept tuples with a single element type to match a list with that type
1059
+ if isinstance(actual, mm.TupleType) and isinstance(expected, mm.ListType):
1060
+ return type_matches(actual.element_types[0], expected.element_type)
1061
+
1062
+ # otherwise no match
1063
+ return False
1064
+
1065
+ def merge_numeric_types(type1: mm.Type, type2: mm.Type) -> Optional[mm.Type]:
1066
+ # if one of them is the abstract Number type, pick the other
1067
+ if type1 == b.core.Number and isinstance(type2, mm.NumberType):
1068
+ return type2
1069
+ if type2 == b.core.Number and isinstance(type1, mm.NumberType):
1070
+ return type1
1071
+
1072
+ # if both are number types, pick the one with larger scale/precision
1073
+ if isinstance(type1, mm.NumberType) and isinstance(type2, mm.NumberType):
1074
+ if type1.scale > type2.scale or (type1.scale == type2.scale and type1.precision > type2.precision):
1075
+ return type1
1076
+ else:
1077
+ return type2
1078
+
1079
+ # if we are overriding a number with a float, pick float
1080
+ if isinstance(type1, mm.NumberType) and type2 == b.core.Float:
1081
+ return type2
1082
+ if isinstance(type2, mm.NumberType) and type1 == b.core.Float:
1083
+ return type1
1084
+
1085
+ return None
1086
+
1087
+ def merge_types(type1: mm.Type, type2: mm.Type) -> mm.Type:
1088
+ if type1 == type2:
1089
+ return type1
1090
+ if bt.is_type_var(type1):
1091
+ return type2
1092
+ if bt.is_type_var(type2):
1093
+ return type1
1094
+
1095
+ types_to_process = [type1, type2]
1096
+
1097
+ numeric_merge = merge_numeric_types(type1, type2)
1098
+ if numeric_merge is not None:
1099
+ return numeric_merge
1100
+
1101
+ # if one extends the other, pick the most specific one
1102
+ if bt.extends(type1, type2):
1103
+ return type1
1104
+ if bt.extends(type2, type1):
1105
+ return type2
1106
+
1107
+ # give precedence to nominal types (e.g. merging USD(decimal) with decimal gives USD(decimal))
1108
+ base_primitive_type1 = bt.get_primitive_supertype(type1)
1109
+ base_primitive_type2 = bt.get_primitive_supertype(type2)
1110
+ if base_primitive_type1 == base_primitive_type2:
1111
+ if bt.is_primitive(type1):
1112
+ return type2
1113
+ elif bt.is_primitive(type2):
1114
+ return type1
1115
+
1116
+ if base_primitive_type1 and base_primitive_type2:
1117
+ numeric_merge = merge_numeric_types(base_primitive_type1, base_primitive_type2)
1118
+ if numeric_merge == base_primitive_type1:
1119
+ return type1
1120
+ elif numeric_merge == base_primitive_type2:
1121
+ return type2
1122
+
1123
+ combined = OrderedSet()
1124
+ # Iterative flattening of union types
1125
+ while types_to_process:
1126
+ t = types_to_process.pop()
1127
+ if isinstance(t, mm.UnionType):
1128
+ types_to_process.extend(t.types)
1129
+ else:
1130
+ combined.add(t)
1131
+
1132
+ # If we have multiple types and Any or AnyEntity is one of them, remove Any
1133
+ if len(combined) > 1:
1134
+ if b.core.Any in combined:
1135
+ combined.remove(b.core.Any)
1136
+ if b.core.AnyEntity in combined:
1137
+ combined.remove(b.core.AnyEntity)
1138
+
1139
+ # If we still have multiple types, make sure supertypes are removed to keep only the
1140
+ # most specific types
1141
+ if len(combined) > 1:
1142
+ to_remove = set()
1143
+ for t1 in combined:
1144
+ for t2 in combined:
1145
+ if t1 != t2 and bt.extends(t1, t2):
1146
+ to_remove.add(t2)
1147
+ for r in to_remove:
1148
+ combined.remove(r)
1149
+
1150
+ # Return single type or create a union
1151
+ return next(iter(combined)) if len(combined) == 1 else mm.UnionType(types=tuple(combined))
1152
+
1153
+ def invalid_type(type:mm.Type) -> bool:
1154
+ if isinstance(type, mm.UnionType):
1155
+ # if there are multiple primitives, or a primitive and a non-primitive
1156
+ # then we have an invalid type
1157
+ if len(type.types) > 1:
1158
+ return any([bt.is_primitive(t) for t in type.types])
1159
+ return False
1160
+
1161
+
1162
+ #--------------------------------------------------
1163
+ # Type Errors
1164
+ #--------------------------------------------------
1165
+
1166
+ @dataclass
1167
+ class TyperError():
1168
+ node: mm.Node
1169
+
1170
+ def report(self):
1171
+ err(self.name(), self.message(), self.parts())
1172
+
1173
+ def name(self) -> str:
1174
+ return type(self).__name__
1175
+
1176
+ def message(self) -> str:
1177
+ raise NotImplementedError()
1178
+
1179
+ def parts(self) -> list[Part]:
1180
+ if self.node.source is None:
1181
+ return [str(self.node)]
1182
+ return [str(self.node), Source(self.node.source)]
1183
+
1184
+ @dataclass
1185
+ class TypeMismatch(TyperError):
1186
+ expected: mm.Type
1187
+ actual: mm.Type
1188
+
1189
+ def message(self) -> str:
1190
+ return f"Expected {get_name(self.expected)}, got {get_name(self.actual)}"
1191
+
1192
+ @dataclass
1193
+ class InvalidType(TyperError):
1194
+ type: mm.Type
1195
+
1196
+ def message(self) -> str:
1197
+ return f"Incompatible types infered: {get_name(self.type)}"
1198
+
1199
+ @dataclass
1200
+ class UnresolvedOverload(TyperError):
1201
+ arg_types: list[mm.Type]
1202
+
1203
+ def message(self) -> str:
1204
+ assert isinstance(self.node, (mm.Lookup, mm.Update, mm.Aggregate))
1205
+ rel = get_relation(self.node)
1206
+ types = ', '.join([get_name(t) for t in self.arg_types])
1207
+ return f"Unresolved overload: {rel.name}({types})"
1208
+
1209
+ @dataclass
1210
+ class UnresolvedType(TyperError):
1211
+
1212
+ def message(self) -> str:
1213
+ return "Unable to determine concrete type."