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,1719 @@
1
+ """Base frontend classes for RelationalAI semantics API.
2
+ """
3
+ from __future__ import annotations
4
+
5
+ import decimal
6
+ import math
7
+ import re
8
+ import datetime as dt
9
+ from typing import TYPE_CHECKING, Any, Generic, Iterable, Iterator, NoReturn, Optional, Sequence, Tuple, Type, TypeGuard, TypeVar, cast
10
+ import itertools
11
+ from more_itertools import peekable
12
+ from pandas import DataFrame
13
+ import pandas as pd
14
+ from enum import Enum, EnumMeta
15
+
16
+ from ...util import schema as schema_util
17
+ from ...util.structures import KeyedSet
18
+ from ...util.naming import Namer, sanitize
19
+ from ...util.python import pytype_to_concept_name
20
+ from ...util.source import SourcePos
21
+ from ...util.tracing import get_tracer
22
+ from ...util.error import err, exc, warn, source
23
+ from ...util.docutils import include_in_docs
24
+ from ..metamodel.builtins import builtins
25
+ from ..metamodel.metamodel import Model as mModel
26
+
27
+ tracer = get_tracer()
28
+ tracer.start_program()
29
+
30
+ #------------------------------------------------------
31
+ # Global ID Generator
32
+ #------------------------------------------------------
33
+
34
+ _global_id = peekable(itertools.count(0))
35
+
36
+ #------------------------------------------------------
37
+ # Helpers
38
+ #------------------------------------------------------
39
+
40
+ def _find_field(fields: list[Field], field: str|int|Concept) -> tuple[Field, int]|None:
41
+ resolved = None
42
+ if isinstance(field, int):
43
+ if not (0 <= field < len(fields)):
44
+ raise IndexError(f"Field index {field} out of range for relationship with {len(fields)} fields")
45
+ resolved = fields[field]
46
+ elif isinstance(field, str):
47
+ resolved = next((f for f in fields if f.name == field), None)
48
+ elif isinstance(field, Concept):
49
+ resolved = next((f for f in fields if f.type is field), None)
50
+ if not resolved:
51
+ return None
52
+ return resolved, fields.index(resolved)
53
+
54
+ def _find_concept(model: Model, passed_type: Any, extra_types: Sequence[Concept] = [], default:Concept|None = None) -> Concept | None:
55
+ if isinstance(passed_type, Concept):
56
+ return passed_type
57
+ if isinstance(passed_type, str):
58
+ for ext_type in extra_types:
59
+ if ext_type._name == passed_type:
60
+ return ext_type
61
+ name = pytype_to_concept_name.get(passed_type, passed_type)
62
+ if name.startswith("Number(") or name.startswith("Decimal("):
63
+ return cast(Any, CoreConcepts["Number"]).parse(name)
64
+ return model._find_concept(name)
65
+ #TODO: try to find a variable in the frame that is a concept with this name
66
+ return default
67
+
68
+ #------------------------------------------------------
69
+ # Primitive
70
+ #------------------------------------------------------
71
+
72
+ Primitive = str|int|float|bool
73
+
74
+ def is_primitive(value: Any) -> TypeGuard[str | int | float | bool]:
75
+ return isinstance(value, (str, int, float, bool))
76
+
77
+ #------------------------------------------------------
78
+ # DSLBase
79
+ #------------------------------------------------------
80
+
81
+ class DSLBase:
82
+ def __init__(self, model:Model):
83
+ self._model = model
84
+ self._id = next(_global_id)
85
+ self._source = SourcePos.new()
86
+
87
+ #------------------------------------------------------
88
+ # Hashing
89
+ #------------------------------------------------------
90
+
91
+ def to_dict_key(self) -> int|tuple[int, int]:
92
+ return self._id
93
+
94
+ def __hash__(self) -> NoReturn:
95
+ exc("Unhashable type", f"{self.__class__.__name__} objects are unhashable. Use `to_dict_key()` instead.", [source(self)])
96
+
97
+ #------------------------------------------------------
98
+ # DSLDict
99
+ #------------------------------------------------------
100
+
101
+ def dsl_key(obj:Any) -> Any:
102
+ if isinstance(obj, DSLBase):
103
+ return obj.to_dict_key()
104
+ elif isinstance(obj, tuple):
105
+ return tuple(dsl_key(o) for o in obj)
106
+ return obj
107
+
108
+ #------------------------------------------------------
109
+ # Variable
110
+ #------------------------------------------------------
111
+
112
+ class Variable(DSLBase):
113
+ #--------------------------------------------------
114
+ # Infix operator overloads
115
+ #--------------------------------------------------
116
+
117
+ def _bin_op(self, op, left, right) -> Expression:
118
+ res = CoreConcepts["Numeric"].ref()
119
+ return Expression(CoreRelationships[op], [left, right, res])
120
+
121
+ def __add__(self, other):
122
+ return self._bin_op("+", self, other)
123
+ def __radd__(self, other):
124
+ return self._bin_op("+", other, self)
125
+
126
+ def __mul__(self, other):
127
+ return self._bin_op("*", self, other)
128
+ def __rmul__(self, other):
129
+ return self._bin_op("*", other, self)
130
+
131
+ def __sub__(self, other):
132
+ return self._bin_op("-", self, other)
133
+ def __rsub__(self, other):
134
+ return self._bin_op("-", other, self)
135
+
136
+ def __truediv__(self, other):
137
+ return self._bin_op("/", self, other)
138
+ def __rtruediv__(self, other):
139
+ return self._bin_op("/", other, self)
140
+
141
+ def __floordiv__(self, other):
142
+ return self._bin_op("//", self, other)
143
+ def __rfloordiv__(self, other):
144
+ return self._bin_op("//", other, self)
145
+
146
+ def __pow__(self, other):
147
+ return self._bin_op("^", self, other)
148
+ def __rpow__(self, other):
149
+ return self._bin_op("^", other, self)
150
+
151
+ def __mod__(self, other):
152
+ return self._bin_op("%", self, other)
153
+ def __rmod__(self, other):
154
+ return self._bin_op("%", other, self)
155
+
156
+ def __neg__(self):
157
+ return self._bin_op("*", self, -1)
158
+
159
+ #--------------------------------------------------
160
+ # Filter overloads
161
+ #--------------------------------------------------
162
+
163
+ def _filter(self, op, left, right) -> Expression:
164
+ return Expression(CoreRelationships[op], [left, right])
165
+
166
+ def __gt__(self, other):
167
+ return self._filter(">", self, other)
168
+ def __ge__(self, other):
169
+ return self._filter(">=", self, other)
170
+ def __lt__(self, other):
171
+ return self._filter("<", self, other)
172
+ def __le__(self, other):
173
+ return self._filter("<=", self, other)
174
+ def __eq__(self, other) -> Any:
175
+ return self._filter("=", self, other)
176
+ def __ne__(self, other) -> Any:
177
+ return self._filter("!=", self, other)
178
+
179
+ #--------------------------------------------------
180
+ # And/Or
181
+ #--------------------------------------------------
182
+
183
+ def __or__(self, other) -> Match:
184
+ return Match(self._model, self, other)
185
+
186
+ def __and__(self, other) -> Fragment:
187
+ if isinstance(other, Fragment):
188
+ new = other.where()
189
+ new._where.insert(0, self)
190
+ return new
191
+ return self._model.where(self, other)
192
+
193
+ #------------------------------------------------------
194
+ # AsBool
195
+ #------------------------------------------------------
196
+
197
+ def as_bool(self) -> AsBool:
198
+ return AsBool(self)
199
+
200
+ #------------------------------------------------------
201
+ # Alias
202
+ #------------------------------------------------------
203
+
204
+ def alias(self, alias: str) -> Alias:
205
+ return Alias(self, alias)
206
+
207
+ #--------------------------------------------------
208
+ # in_
209
+ #--------------------------------------------------
210
+
211
+ def in_(self, values:Sequence[Value]|Variable):
212
+ if isinstance(values, Variable):
213
+ return self == values
214
+ if all(isinstance(v, (str, int, float, bool)) for v in values):
215
+ data_table = self._model.data([(v,) for v in values])
216
+ return self == data_table[0]
217
+ else:
218
+ return self == self._model.union(*values)
219
+
220
+ #--------------------------------------------------
221
+ # Check value
222
+ #--------------------------------------------------
223
+
224
+ def _check_value(self) -> bool:
225
+ return True
226
+
227
+ def _to_concept(self) -> Concept|None:
228
+ return None
229
+
230
+ #------------------------------------------------------
231
+ # Error handling
232
+ #------------------------------------------------------
233
+
234
+ def __bool__(self) -> NoReturn:
235
+ cur_source = self._source.block.source or ""
236
+ invalid = next((bool_check for bool_check in ["if ", "while ", " and ", " or "] if bool_check in cur_source), "bool check").strip()
237
+ mapped = {"and": "`&` or `,`", "or": "`|`", "if": "`where`"}
238
+ if m := mapped.get(invalid):
239
+ exc(f"Invalid operator", f"Cannot use python's `{invalid}` in model expressions. Use {m} instead.", [source(self)])
240
+ else:
241
+ exc(f"Invalid operator", f"Cannot use python's `{invalid}` in model expressions.", [source(self)])
242
+
243
+ if not TYPE_CHECKING:
244
+ def __iter__(self):
245
+ common_incorrect = ["sum", "min", "max"]
246
+ for agg in common_incorrect:
247
+ if agg in self._source.block.source:
248
+ exc(f"Invalid built-in", f"The Python built-in `{agg}()` was used instead of the RAI equivalent.", [
249
+ source(self),
250
+ f"Use [cyan]`relationalai.semantics.std.aggregates.{agg}`[/cyan] instead.",
251
+ ])
252
+ exc("Invalid iteration", f"Cannot iterate over {self.__class__.__name__} objects.", [
253
+ source(self)
254
+ ])
255
+
256
+ def __len__(self) -> NoReturn:
257
+ exc("Invalid operator", "Cannot use len() on model variables.", [source(self)])
258
+
259
+ #------------------------------------------------------
260
+ # Concept
261
+ #------------------------------------------------------
262
+
263
+ class Concept(Variable):
264
+ lookup_by_id: dict[int, Concept] = {}
265
+
266
+ def __init__(self, name: str, extends: list[Concept], identify_by: dict[str, Concept], model:Model):
267
+ super().__init__(model)
268
+ Concept.lookup_by_id[self._id] = self
269
+ self._name = name
270
+ clean_extends = []
271
+ for extend in extends:
272
+ if not isinstance(extend, Concept):
273
+ exc("Invalid extend", f"Cannot extend from non-Concept type: {extend}", [source(self)])
274
+ if "Number" in CoreConcepts and extend is CoreConcepts["Number"]:
275
+ clean_extends.append(extend.size(38, 14))
276
+ else:
277
+ clean_extends.append(extend)
278
+ self._extends = clean_extends
279
+ self._annotations: list[Expression|Relationship] = []
280
+ self._relationships = {}
281
+ self._identify_by:list[Relationship] = []
282
+ for id_name, id_val in identify_by.items():
283
+ if not isinstance(id_val, (Concept, Property)):
284
+ exc("Invalid identify_by", f"identify_by values must be Concepts, got: {type(id_val)}", [source(self)])
285
+ id_val = self._dot(id_name, with_type=id_val)
286
+ self._identify_by.append(id_val)
287
+
288
+ def __setattr__(self, name: str, value: Any) -> None:
289
+ if isinstance(value, Relationship):
290
+ key = name.lower()
291
+ if self._relationships.get(key) is not None:
292
+ exc("Duplicate relationship", f"Relationship '{name}' is already defined on concept '{self._name}'", [
293
+ source(self._relationships[key])
294
+ ])
295
+ if not value._short_name:
296
+ value._short_name = name
297
+ self._relationships[key] = value
298
+ else:
299
+ super().__setattr__(name, value)
300
+
301
+ def _dot_recur(self, name: str, with_type: Any = None) -> Relationship|None:
302
+ if name.lower() in self._relationships:
303
+ return self._relationships[name.lower()]
304
+ for ext in self._extends:
305
+ rel = ext._dot_recur(name, with_type)
306
+ if rel is not None:
307
+ return rel
308
+ # We should create the relationship on the root type if it's not
309
+ # found anywhere
310
+ if self._model is not CoreLibrary:
311
+ field_type = _find_concept(self._model, with_type, default=CoreConcepts["Any"])
312
+ assert field_type is not None
313
+ rel = self._relationships[name.lower()] = Property(fields=[
314
+ Field(self._name.lower(), self),
315
+ Field(name, field_type)
316
+ ], short_name=name, model=self._model)
317
+ return rel
318
+ return None
319
+
320
+ def _dot(self, name: str, with_type: Any = None) -> Relationship:
321
+ if self._model is CoreLibrary:
322
+ exc("Invalid relationship", f"Cannot access relationships on core concept '{self._name}'.", [source(self)])
323
+ if with_type is None:
324
+ with_type = CoreConcepts["Any"]
325
+ rel = self._dot_recur(name, with_type)
326
+ assert rel is not None
327
+ return rel
328
+
329
+ def __getattr__(self, item) -> Chain:
330
+ if item.startswith("_"):
331
+ return object.__getattribute__(self, item)
332
+ return Chain(self, self._dot(item))
333
+
334
+ def __call__(self, *args: Any, **kwargs: Any) -> Expression:
335
+ return Expression(self, list(args), kwargs)
336
+
337
+ def new(self, *args: StatementAndSchema, **kwargs: Any) -> New:
338
+ return New(self, args, kwargs)
339
+
340
+ def to_identity(self, *args: Any, **kwargs: Any) -> New:
341
+ return New(self, args, kwargs, identity_only=True)
342
+
343
+ def identify_by(self, *properties: Property|Chain) -> Concept:
344
+ for prop in properties:
345
+ if isinstance(prop, Chain):
346
+ prop = prop._next
347
+ if not isinstance(prop, Property) and not (isinstance(prop, Reading) and isinstance(prop._relationship, Property)):
348
+ exc("Invalid identify_by", f"identify_by expects Properties, got: {type(prop).__name__}", [source(self)])
349
+ if prop._fields[0].type is not self:
350
+ exc("Invalid identify_by", f"Property {prop} have the first field be {self._name}", [source(self)])
351
+ self._identify_by.append(prop)
352
+ return self
353
+
354
+ def filter_by(self, **kwargs: Any) -> Expression:
355
+ return FilterBy(self, kwargs)
356
+
357
+ def require(self, *items: Variable|Fragment) -> Fragment:
358
+ return self._model.where(self).require(*items)
359
+
360
+ def ref(self, name="") -> Ref:
361
+ return Ref(self, name)
362
+
363
+ def annotate(self, *annos:Expression|Relationship) -> Concept:
364
+ self._annotations.extend(annos)
365
+ return self
366
+
367
+ def _to_concept(self) -> Concept:
368
+ return self
369
+
370
+ def __dir__(self):
371
+ default = set(super().__dir__())
372
+ return sorted(default.union(self._relationships.keys()))
373
+
374
+ def __format__(self, format_spec: str) -> str:
375
+ if not format_spec:
376
+ return f"{{{self._name}#{self._id}}}"
377
+ return f"{{{self._name}#{self._id}:{format_spec.strip()}}}"
378
+
379
+ #------------------------------------------------------
380
+ # NumberConcept
381
+ #------------------------------------------------------
382
+
383
+ class NumberConcept(Concept):
384
+ def __init__(self, name: str, precision: int, scale: int, model: Model):
385
+ super().__init__(name, extends=[CoreConcepts["Numeric"]], identify_by={}, model=model)
386
+ self._precision = precision
387
+ self._scale = scale
388
+
389
+ #------------------------------------------------------
390
+ # Ref
391
+ #------------------------------------------------------
392
+
393
+ class Ref(Variable):
394
+ def __init__(self, concept: Concept, name: str|None = None):
395
+ super().__init__(concept._model)
396
+ self._concept = concept
397
+ self._name = name or ("number" if (isinstance(concept, NumberConcept) or concept is CoreConcepts["Numeric"])
398
+ else concept._name)
399
+
400
+ def __getattr__(self, item):
401
+ if item.startswith("_"):
402
+ return object.__getattribute__(self, item)
403
+ return Chain(self, self._concept._dot(item))
404
+
405
+ def _to_concept(self) -> Concept:
406
+ return self._concept
407
+
408
+ def FilterBy(self, **kwargs: Any) -> Expression:
409
+ return FilterBy(self, kwargs)
410
+
411
+ #------------------------------------------------------
412
+ # Table
413
+ #------------------------------------------------------
414
+
415
+ class Table(Concept):
416
+ def __init__(self, name:str, schema: dict[str, Concept], model: Model):
417
+ super().__init__(name, [], {}, model)
418
+ self._known_columns = []
419
+ for col_name, col_type in schema.items():
420
+ if isinstance(col_type, Property):
421
+ col_rel = col_type
422
+ setattr(self, col_name, col_rel)
423
+ else:
424
+ col_rel = self._dot(col_name, with_type=col_type)
425
+ self._known_columns.append(col_rel)
426
+
427
+ @property
428
+ def _columns(self) -> list[Relationship]:
429
+ if self._known_columns:
430
+ return self._known_columns
431
+ schema = schema_util.fetch(self._name)
432
+ for col_name, col_type_name in schema.items():
433
+ col_type = _find_concept(self._model, col_type_name, default=CoreConcepts["Any"])
434
+ col_rel = self._dot(col_name, with_type=col_type)
435
+ col_rel._short_name = col_name
436
+ if col_type is not None:
437
+ col_rel._fields[1].type = col_type
438
+ self._known_columns.append(col_rel)
439
+ return self._known_columns
440
+
441
+ def __getitem__(self, index: int|str) -> Chain:
442
+ if isinstance(index, int):
443
+ if not (0 <= index < len(self._columns)):
444
+ raise IndexError(f"Column index {index} out of range, there are {len(self._columns)} columns")
445
+ return Chain(self, self._columns[index])
446
+ col = next((c for c in self._columns if c._short_name == index), None)
447
+ if col is None:
448
+ raise KeyError(f"Column name '{index}' not found, columns have names {[c._short_name for c in self._columns]}")
449
+ return Chain(self, col)
450
+
451
+ def __iter__(self) -> Iterator[Relationship]:
452
+ return iter(self._columns)
453
+
454
+ def new(self, *args:StatementAndSchema, **kwargs: Any) -> NoReturn:
455
+ exc("Invalid new call", "Cannot create new instances of Tables.", [source(self)])
456
+
457
+ def to_identity(self, *args: Any, **kwargs: Any) -> NoReturn:
458
+ exc("Invalid identity call", "Cannot create identity instances of Tables.", [source(self)])
459
+
460
+ def to_schema(self, *, exclude: list[str] = []) -> TableSchema:
461
+ return TableSchema(self, exclude=exclude)
462
+
463
+ #------------------------------------------------------
464
+ # TableSchema
465
+ #------------------------------------------------------
466
+
467
+ class TableSchema(DSLBase):
468
+ def __init__(self, table: Table, exclude: list[str] = []):
469
+ super().__init__(table._model)
470
+ self._table = table
471
+ self.exclude = set([e.lower() for e in exclude])
472
+
473
+ def get_columns(self) -> list[Relationship]:
474
+ return [col for col in self._table._columns if col._short_name.lower() not in self.exclude]
475
+
476
+ #--------------------------------------------------
477
+ # DerivedTable
478
+ #--------------------------------------------------
479
+
480
+ class DerivedTable(Variable):
481
+ def __init__(self, model: Model):
482
+ super().__init__(model)
483
+ self.__cols = []
484
+
485
+ @property
486
+ def _columns(self):
487
+ if not self.__cols:
488
+ self.__cols = self._get_cols()
489
+ if not self.__cols:
490
+ raise ValueError(f"Cannot use {self.__class__.__name__} as it has no columns")
491
+ return self.__cols
492
+
493
+ def _check_value(self):
494
+ return bool(self._columns)
495
+
496
+ def __iter__(self) -> Iterator[DerivedColumn]:
497
+ return iter(self._columns)
498
+
499
+ def __getitem__(self, index: int|str) -> DerivedColumn:
500
+ if isinstance(index, int):
501
+ if not (0 <= index < len(self._columns)):
502
+ raise IndexError(f"Column index {index} out of range, there are {len(self._columns)} columns")
503
+ return self._columns[index]
504
+ col = next((c for c in self._columns if c._name == index), None)
505
+ if not col:
506
+ raise KeyError(f"Column name '{index}' not found, columns have names {[c._name for c in self._columns]}")
507
+ return col
508
+
509
+ def _get_cols(self):
510
+ raise NotImplementedError()
511
+
512
+ #--------------------------------------------------
513
+ # DerivedColumn
514
+ #--------------------------------------------------
515
+
516
+ class DerivedColumn(Variable):
517
+ def __init__(self, table: DerivedTable, index: int, name: str|None = None, type_: Concept|None = None):
518
+ super().__init__(table._model)
519
+ self._table = table
520
+ self._index = index
521
+ self._name = name
522
+ self._type = type_
523
+ self._relationships = {}
524
+
525
+ def _dot(self, name: str) -> Relationship:
526
+ if self._type is not None:
527
+ return self._type._dot(name)
528
+ if name.lower() not in self._relationships:
529
+ self._relationships[name.lower()] = Property(fields=[
530
+ Field("entity", CoreConcepts["Any"]),
531
+ Field(name, CoreConcepts["Any"])
532
+ ], short_name=name, model=self._model)
533
+ return self._relationships[name.lower()]
534
+
535
+ def __getattr__(self, item):
536
+ if item.startswith("_"):
537
+ return object.__getattribute__(self, item)
538
+ return Chain(self, self._dot(item))
539
+
540
+ @staticmethod
541
+ def from_value(table: DerivedTable, index: int, var: Value) -> DerivedColumn:
542
+ name = None
543
+ if isinstance(var, Alias):
544
+ name = var._alias
545
+ if not isinstance(var, Variable):
546
+ var = Literal(var, table._model)
547
+ return DerivedColumn(table=table, index=index, name=name, type_=var._to_concept())
548
+
549
+ #------------------------------------------------------
550
+ # Field
551
+ #------------------------------------------------------
552
+
553
+ class Field:
554
+ def __init__(self, name: str, type_: Concept, is_input: bool = False, is_list: bool = False, source: SourcePos | None = None):
555
+ self._id = next(_global_id)
556
+ self.name = name
557
+ self.type = type_
558
+ self.is_input = is_input
559
+ self.is_list = is_list
560
+ self._source = source
561
+
562
+ @classmethod
563
+ def input(cls, name: str, concept: Concept, is_list: bool = False) -> Field:
564
+ return Field(name, concept, is_input=True, is_list=is_list)
565
+
566
+ def _match(self, other: Field) -> bool:
567
+ return self.name == other.name and self.type is other.type
568
+
569
+ #--------------------------------------------------
570
+ # Relationship
571
+ #--------------------------------------------------
572
+
573
+ class Relationship(Variable):
574
+ def __init__(self, model: Model, reading_str:str = "", fields: list[Field] = [], short_name: str = "", allow_no_fields: bool = False, overloads:list[list[Concept]]|None = None, is_unresolved: bool = False):
575
+ super().__init__(model)
576
+ if not reading_str and not fields and not allow_no_fields:
577
+ raise ValueError("Either reading_str or fields must be provided")
578
+ if not reading_str and fields:
579
+ reading_str = " and ".join([f"{{{f.type._name}#{f.type._id}:{f.name}}}" for f in fields[:-1]])
580
+ reading_str = f"{reading_str} has {{{fields[-1].type._name}#{fields[-1].type._id}:{fields[-1].name}}}"
581
+ parts = []
582
+ if not fields:
583
+ (fields, parts) = Reading.parse(model, reading_str, fields, _source=self)
584
+ self._fields = fields
585
+ if not fields and not allow_no_fields:
586
+ exc("Invalid Relationship", "A Relationship must have at least one field.", [source(self)])
587
+ self._readings = [Reading(model, self, reading_str, fields, parts)]
588
+ self._short_name = short_name
589
+ self._relationships = {}
590
+ self._annotations: list[Expression|Relationship] = []
591
+ self._overloads = overloads
592
+ self._is_unresolved = is_unresolved
593
+ for f in self._fields:
594
+ if f._source is None:
595
+ f._source = self._source
596
+
597
+ def _dot(self, name: str) -> Relationship:
598
+ field_type = self._fields[-1].type
599
+ if name.lower() in self._relationships:
600
+ return self._relationships[name.lower()]
601
+ if field_type is CoreConcepts["Any"]:
602
+ rel = Property(fields=[
603
+ Field(self._fields[-1].name, field_type),
604
+ Field(name, CoreConcepts["Any"])
605
+ ], short_name=name, model=self._model, is_unresolved=True)
606
+ self._relationships[name.lower()] = rel
607
+ return rel
608
+ return field_type._dot(name)
609
+
610
+ def __getattr__(self, item):
611
+ if item.startswith("_"):
612
+ return object.__getattribute__(self, item)
613
+ op = self._fields[-1].type._dot(item)
614
+ return Chain(self, op)
615
+
616
+ def __call__(self, *args: Any, **kwargs: Any) -> Expression:
617
+ return Expression(self, list(args), kwargs, root=self)
618
+
619
+ def __getitem__(self, field: str|int|Concept) -> FieldRef:
620
+ resolved = _find_field(self._fields, field)
621
+ if not resolved:
622
+ raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._fields]}")
623
+ return FieldRef(self, field, *resolved)
624
+
625
+ def alt(self, reading_str: str) -> Reading:
626
+ reading = Reading(self._model, self, reading_str)
627
+ self._readings.append(reading)
628
+ return reading
629
+
630
+ def annotate(self, *annos:Expression|Relationship) -> Relationship:
631
+ self._annotations.extend(annos)
632
+ return self
633
+
634
+ def _to_concept(self) -> Concept|None:
635
+ return self._fields[-1].type
636
+
637
+ def __dir__(self):
638
+ default = set(super().__dir__())
639
+ return sorted(default.union(self._fields[-1].type.__dir__()))
640
+
641
+ def to_df(self) -> DataFrame:
642
+ refs = [self[ix] for ix in range(len(self._fields))]
643
+ return self._model.select(*refs).where(self(*refs)).to_df()
644
+
645
+ def inspect(self):
646
+ print(self.to_df())
647
+
648
+ #------------------------------------------------------
649
+ # Reading
650
+ #------------------------------------------------------
651
+
652
+ class Reading(Relationship):
653
+ def __init__(self, model: Model, relationship: Relationship, reading_str: str, reading_fields: list[Field] = [], reading_parts: list[str|int] = []):
654
+ Variable.__init__(self, model)
655
+ self._relationship = relationship
656
+ if not reading_fields or not reading_parts:
657
+ parsed_fields, parsed_parts = Reading.parse(model, reading_str, reading_fields, _source=self)
658
+ reading_fields = reading_fields or parsed_fields
659
+ reading_parts = reading_parts or parsed_parts
660
+ # make sure we can find all these fields in the base relationship
661
+ index_map = {}
662
+ matched = []
663
+ for field_i, field in enumerate(reading_fields):
664
+ found = next(((i, f) for i, f in enumerate(relationship._fields) if f._match(field)), None)
665
+ if not found:
666
+ raise ValueError(f"Field {field.name}:{field.type._name} not found in relationship with fields {[f'{f.name}:{f.type._name}' for f in relationship._fields]}")
667
+ matched.append(found[1])
668
+ index_map[field_i] = found[0]
669
+ self._fields = matched
670
+ self._reading = reading_str
671
+ self._short_name = ""
672
+ self._parts:list[str|int] = [index_map.get(p, p) for p in reading_parts]
673
+ self._relationships = {}
674
+ self._annotations: list[Expression|Relationship] = []
675
+
676
+ #------------------------------------------------------
677
+ # Parse
678
+ #------------------------------------------------------
679
+
680
+ @classmethod
681
+ def parse(cls, model: Model, reading: str, known_fields: list[Field], _source=None) -> Tuple[list[Field], list[str|int]]:
682
+ # match <class 'foo'> which is the serialized form of a python type mistakenly passed instead of a concept
683
+ class_pattern = re.compile(r'<class \'(.*?)\'>')
684
+ match = class_pattern.search(reading)
685
+ if match:
686
+ extra: list = [source(_source)]
687
+ if match.group(1) in pytype_to_concept_name:
688
+ concept = pytype_to_concept_name[match.group(1)]
689
+ extra.append(f" Did you mean to use [cyan]relationalai.semantics.{concept}[/cyan]?")
690
+ exc("Invalid field type", f"The type '{match.group(1)}' is not a valid Concept.", extra)
691
+
692
+ # {Type} or {name:Type}, where Type can include Number(38,14)
693
+ pattern = re.compile(r'\{([a-zA-Z0-9_.#]+(?:(?:\([0-9]+,[0-9]+\))(?:[#0-9]+)?)?)(?::\s*([a-zA-Z0-9_.]+(?:\([0-9]+,[0-9]+\))?))?\}')
694
+
695
+ namer = Namer()
696
+ fields: list[Field] = []
697
+ parts: list[str|int] = []
698
+
699
+ last_end = 0
700
+ is_old_style = True
701
+ for m in pattern.finditer(reading):
702
+ # literal chunk before this match
703
+ parts.append(reading[last_end:m.start()])
704
+ field_name, field_type_name = m.group(1), m.group(2)
705
+ field_type_id = None
706
+ if "#" in field_name:
707
+ temp_name = field_type_name
708
+ field_type_name, field_type_id = field_name.split("#")
709
+ field_name = temp_name or sanitize(field_type_name.lower())
710
+ is_old_style = False
711
+
712
+ # if we don't have a type_name, then only a type was provided
713
+ if not field_type_name:
714
+ field_type_name = field_name
715
+ field_name = sanitize(field_name.lower())
716
+
717
+ field_name = namer.get_name(field_name)
718
+ field_type = Concept.lookup_by_id.get(int(field_type_id)) \
719
+ if field_type_id \
720
+ else _find_concept(model, field_type_name, extra_types=[f.type for f in known_fields])
721
+ if field_type is None:
722
+ exc("Unknown Concept", f"The Concept '{field_type_name}' couldn't be found in the model", [
723
+ source(_source),
724
+ ])
725
+
726
+ fields.append(Field(field_name, field_type))
727
+ parts.append(len(fields) - 1)
728
+ last_end = m.end()
729
+
730
+ # trailing literal after the final match
731
+ if(last_end < len(reading)):
732
+ parts.append(reading[last_end:])
733
+
734
+ if is_old_style and fields:
735
+ correct = []
736
+ for part in parts:
737
+ if isinstance(part, int):
738
+ type_name = fields[part].type._name
739
+ correct_type_name = type_name
740
+ if isinstance(fields[part].type, NumberConcept):
741
+ precision = fields[part].type._precision
742
+ scale = fields[part].type._scale
743
+ if scale == 0:
744
+ correct_type_name = f"Integer"
745
+ else:
746
+ correct_type_name = f"Number.size({precision},{scale})"
747
+ if type_name.lower() != fields[part].name and correct_type_name.lower() != fields[part].name:
748
+ correct.append(f"{{{correct_type_name}:{fields[part].name}}}")
749
+ else:
750
+ correct.append(f"{{{correct_type_name}}}")
751
+ else:
752
+ correct.append(part)
753
+ warn("Deprecated format", "Plain strings for Relationships/Properties is deprecated. Use an f-string instead.", [
754
+ source(_source),
755
+ f'For example: [cyan]f"{"".join(correct)}"',
756
+ ])
757
+
758
+ return fields, parts
759
+
760
+ #------------------------------------------------------
761
+ # Property
762
+ #------------------------------------------------------
763
+
764
+ class Property(Relationship):
765
+ pass
766
+
767
+ #------------------------------------------------------
768
+ # Literal
769
+ #------------------------------------------------------
770
+
771
+ class Literal(Variable):
772
+ def __init__(self, value: Any, model:Model, type: Concept|None = None):
773
+ super().__init__(model)
774
+ self._value = value
775
+ self._type = type if type is not None else self._get_type(value)
776
+
777
+ def _to_concept(self) -> Concept:
778
+ return self._type
779
+
780
+ @staticmethod
781
+ def _get_type(value: Any) -> Concept:
782
+ if type(value) is float and (math.isnan(value) or math.isinf(value)):
783
+ return CoreConcepts["Float"]
784
+ if type(value) is float:
785
+ fractional_digits = 0 if math.isnan(value) else min(len(str(value).split(".")[1]), 14)
786
+ return CoreConcepts["Decimal"].size(38, fractional_digits) # type: ignore
787
+ if type(value) is decimal.Decimal:
788
+ str_value = format(value, 'f')
789
+ if '.' in str_value:
790
+ _, fractional_part = str_value.split('.')
791
+ scale = min(len(fractional_part), 14)
792
+ else:
793
+ scale = 14
794
+ return CoreConcepts["Decimal"].size(38, scale) # type: ignore
795
+ if type(value) in pytype_to_concept_name:
796
+ return CoreConcepts[pytype_to_concept_name[type(value)]]
797
+ else:
798
+ raise NotImplementedError(f"Literal type not implemented for value type: {type(value)}")
799
+
800
+ #------------------------------------------------------
801
+ # Chain
802
+ #------------------------------------------------------
803
+
804
+ class Chain(Variable):
805
+ def __init__(self, start: Chain|Concept|Relationship|DerivedColumn|Table|Ref|FieldRef|Expression, next: Relationship, is_ref = False):
806
+ super().__init__(start._model)
807
+ self._start = start
808
+ self._next = next
809
+ self._is_ref = is_ref
810
+
811
+ def __getattr__(self, item):
812
+ if item.startswith("_"):
813
+ return object.__getattribute__(self._next, item)
814
+ next_rel = self._next._dot(item)
815
+ return Chain(self, next_rel)
816
+
817
+ def __call__(self, *args: Any, **kwargs: Any) -> Expression:
818
+ last = self._next
819
+ assert not isinstance(last, Chain)
820
+ if len(last._fields) > len(args):
821
+ return Expression(last, [self._start, *args], kwargs, root=self)
822
+ return Expression(last, [*args], kwargs, root=self)
823
+
824
+ def to_dict_key(self) -> int|tuple:
825
+ if self._is_ref:
826
+ return self._id
827
+ return (self._start.to_dict_key(), self._next.to_dict_key())
828
+
829
+ def __getitem__(self, field: str|int|Concept) -> FieldRef:
830
+ resolved = _find_field(self._next._fields, field)
831
+ if not resolved:
832
+ raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._next._fields]}")
833
+ return FieldRef(self, field, *resolved)
834
+
835
+ def ref(self) -> Chain:
836
+ return Chain(self._start, self._next, is_ref=True)
837
+
838
+ def alt(self, reading_str: str) -> Reading:
839
+ return self._next.alt(reading_str)
840
+
841
+ def annotate(self, *annos:Expression|Relationship) -> Relationship:
842
+ return self._next.annotate(*annos)
843
+
844
+ def _to_concept(self) -> Concept | None:
845
+ return self._next._to_concept()
846
+
847
+ #------------------------------------------------------
848
+ # Expression
849
+ #------------------------------------------------------
850
+
851
+ class Expression(Variable):
852
+ def __init__(self, op: Relationship|Concept, args: Sequence[Value], kwargs: dict|None = None, root: Chain|Relationship|Concept|None = None):
853
+ super().__init__(op._model)
854
+ self._op = op
855
+ self._has_output = isinstance(op, Concept) or (any(not f.is_input for f in op._fields))
856
+ self._auto_filled = False
857
+ self._root = root
858
+
859
+ # clean args
860
+ clean_args = []
861
+ for arg in args:
862
+ if isinstance(arg, (ModelEnum, Field, TupleVariable)):
863
+ pass
864
+ elif isinstance(arg, TableSchema):
865
+ exc("Invalid argument", "Cannot use a schema as an argument to an Expression.", [source(self)])
866
+ elif not isinstance(arg, Variable):
867
+ arg = Literal(arg, self._model)
868
+ elif isinstance(arg, (Match, Fragment)):
869
+ arg._columns
870
+ clean_args.append(arg)
871
+ self._args = clean_args
872
+
873
+ self._kwargs = kwargs or {}
874
+ # clean kwargs
875
+ for k, v in self._kwargs.items():
876
+ if not isinstance(v, Variable):
877
+ self._kwargs[k] = Literal(v, self._model)
878
+
879
+ if isinstance(op, Relationship):
880
+ op_len = len(op._fields)
881
+ arg_len = len(self._args)
882
+ if op_len - arg_len == 1:
883
+ self._args.append(op._fields[-1].type.ref(name=op._fields[-1].name))
884
+ self._auto_filled = True
885
+ elif op_len != arg_len:
886
+ dir = "Too few" if arg_len < op_len - 1 else "Too many"
887
+ exc(f"{dir} args", f"{op._short_name or 'Relationship'} requires {op_len - 1}-{op_len} arguments but got {arg_len}", [
888
+ source(self),
889
+ ])
890
+
891
+ def __getattr__(self, item):
892
+ if item.startswith("_"):
893
+ return object.__getattribute__(self, item)
894
+ rel = self._op._dot(item)
895
+ return Chain(self, rel)
896
+
897
+ def __getitem__(self, field: str|int|Concept) -> FieldRef:
898
+ if not isinstance(self._op, Relationship):
899
+ raise TypeError(f"Cannot index into Expression with non-Relationship: {self._op}")
900
+ resolved = _find_field(self._op._fields, field)
901
+ if not resolved:
902
+ raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._op._fields]}")
903
+ return FieldRef(self, field, *resolved)
904
+
905
+ def _to_concept(self) -> Concept | None:
906
+ return self._args[-1]._to_concept() if self._args else self._op._to_concept()
907
+
908
+ #------------------------------------------------------
909
+ # New
910
+ #------------------------------------------------------
911
+
912
+ class New(Expression):
913
+ def __init__(self, concept: Concept, args: Sequence[Any], kwargs: dict[str, Any], identity_only: bool = False):
914
+ clean_args = []
915
+ row_ids = []
916
+ for arg in args:
917
+ if isinstance(arg, TableSchema):
918
+ row_ids.append(arg._table)
919
+ lower_case_kwargs = {k.lower() for k, v in kwargs.items()}
920
+ # add any keyword args that aren't already in kwargs
921
+ for col in arg.get_columns():
922
+ if col._short_name and col._short_name.lower() not in lower_case_kwargs:
923
+ kwargs[col._short_name] = col(arg._table)
924
+ else:
925
+ clean_args.append(arg)
926
+ for k in list(kwargs.keys()):
927
+ concept._dot(k)
928
+ super().__init__(concept, clean_args, kwargs)
929
+ self._identity_only = identity_only
930
+ self._row_ids = row_ids
931
+
932
+ #------------------------------------------------------
933
+ # FilterBy
934
+ #------------------------------------------------------
935
+
936
+ class FilterBy(Expression):
937
+ def __init__(self, item: Concept|Ref, kwargs: dict[str, Any]):
938
+ concept = item._concept if isinstance(item, Ref) else item
939
+ super().__init__(concept, [item], kwargs)
940
+
941
+ #------------------------------------------------------
942
+ # FieldRef
943
+ #------------------------------------------------------
944
+
945
+ class FieldRef(Variable):
946
+ def __init__(self, root: Chain|Relationship|Expression, field: str|int|Concept, resolved: Field, resolved_ix:int):
947
+ super().__init__(root._model)
948
+ self._root = root
949
+ self._field = field
950
+ self._resolved = resolved
951
+ self._resolved_ix = resolved_ix
952
+
953
+ def _to_concept(self) -> Concept:
954
+ return self._resolved.type
955
+
956
+ def __getattr__(self, item):
957
+ if item.startswith("_"):
958
+ return object.__getattribute__(self, item)
959
+ return Chain(self, self._resolved.type._dot(item))
960
+
961
+ def to_dict_key(self) -> tuple:
962
+ return (self._root.to_dict_key(), self._resolved._id)
963
+
964
+
965
+ #------------------------------------------------------
966
+ # MetaRef
967
+ #------------------------------------------------------
968
+
969
+ class MetaRef(Variable):
970
+ def __init__(self, target: Concept|Relationship|Field):
971
+ model = target._model if isinstance(target, DSLBase) else target.type._model
972
+ super().__init__(model)
973
+ self._target = target
974
+
975
+ #------------------------------------------------------
976
+ # TupleVariable
977
+ #------------------------------------------------------
978
+
979
+ class TupleVariable:
980
+ def __init__(self, items: Sequence[Value|Distinct]):
981
+ self._items = list(items)
982
+ self._source = SourcePos.new()
983
+
984
+ #------------------------------------------------------
985
+ # AsBool
986
+ #------------------------------------------------------
987
+
988
+ class AsBool(Variable):
989
+ def __init__(self, item: Variable):
990
+ super().__init__(item._model)
991
+ self._item = item
992
+
993
+ def _to_concept(self) -> Concept:
994
+ return CoreConcepts["Boolean"]
995
+
996
+ #------------------------------------------------------
997
+ # Alias
998
+ #------------------------------------------------------
999
+
1000
+ class Alias(Variable):
1001
+ def __init__(self, source: Variable, alias: str):
1002
+ super().__init__(source._model)
1003
+ self._source = source
1004
+ self._alias = alias
1005
+
1006
+ def _to_concept(self) -> Concept|None:
1007
+ return self._source._to_concept()
1008
+
1009
+ def __format__(self, format_spec: str) -> str:
1010
+ if isinstance(self._source, Concept):
1011
+ if format_spec:
1012
+ exc("Invalid alias", f"Alias already specifies an alias for this concept, you can remove `:{format_spec}`")
1013
+ return self._source.__format__(self._alias)
1014
+ return super().__format__(format_spec)
1015
+
1016
+ #------------------------------------------------------
1017
+ # Match
1018
+ #------------------------------------------------------
1019
+
1020
+ class Match(DerivedTable):
1021
+ def __init__(self, model:Model, *items: Statement):
1022
+ super().__init__(model)
1023
+ t = type(self)
1024
+ self._items = [
1025
+ x
1026
+ for item in items
1027
+ for x in (item._items if (type(item) is t and isinstance(item, Match)) else (item,))
1028
+ ] # flatten nested Matches/Unions
1029
+
1030
+ def _get_cols(self) -> list[DerivedColumn]:
1031
+ return [DerivedColumn(self, i) for i in range(self._arg_count())]
1032
+
1033
+ def _arg_count(self) -> int:
1034
+ counts = []
1035
+ for item in self._items:
1036
+ if isinstance(item, DerivedTable):
1037
+ try:
1038
+ counts.append(len(item._columns))
1039
+ except ValueError:
1040
+ counts.append(0)
1041
+ else:
1042
+ # Expressions with no output and Not are filters, do not count as returning values
1043
+ is_filter = isinstance(item, Not) or (isinstance(item, Expression) and not item._has_output)
1044
+ counts.append(0 if is_filter else 1)
1045
+ if not counts:
1046
+ return 0
1047
+ first = counts[0]
1048
+ if any(c != first for c in counts[1:]):
1049
+ exc("Inconsistent branches",
1050
+ f"All branches in a {self.__class__.__name__} must have the same number of returned values",
1051
+ [source(self)])
1052
+ return first
1053
+
1054
+ def __getattr__(self, item) -> Chain:
1055
+ if item.startswith("_"):
1056
+ return object.__getattribute__(self, item)
1057
+ return getattr(self._columns[-1], item)
1058
+
1059
+ #------------------------------------------------------
1060
+ # Union
1061
+ #------------------------------------------------------
1062
+
1063
+ class Union(Match):
1064
+ def __init__(self, model:Model, *items: Value):
1065
+ super().__init__(model, *items)
1066
+
1067
+ #------------------------------------------------------
1068
+ # Not
1069
+ #------------------------------------------------------
1070
+
1071
+ class Not(DSLBase):
1072
+ def __init__(self, *items: Value, model:Model):
1073
+ super().__init__(model)
1074
+ self._items = items
1075
+
1076
+ def __or__(self, other) -> Match:
1077
+ return Match(self._model, self, other)
1078
+
1079
+ def __and__(self, other) -> Fragment:
1080
+ if isinstance(other, Fragment):
1081
+ new = other.where()
1082
+ new._where.insert(0, self)
1083
+ return new
1084
+ return self._model.where(self, other)
1085
+
1086
+ #------------------------------------------------------
1087
+ # Distinct
1088
+ #------------------------------------------------------
1089
+
1090
+ class Distinct(DSLBase):
1091
+ def __init__(self, *items: Value, model:Model):
1092
+ super().__init__(model)
1093
+ self._items = items
1094
+
1095
+ #------------------------------------------------------
1096
+ # Aggregate
1097
+ #------------------------------------------------------
1098
+
1099
+ class Group(DSLBase):
1100
+ def __init__(self, *args: Value):
1101
+ model = [arg._model for arg in args if isinstance(arg, Variable)][0] if args else None
1102
+ super().__init__(model) # type: ignore
1103
+ self._args = list(args)
1104
+
1105
+ def _extend(self, args: Sequence[Value]) -> Group:
1106
+ new = Group(*self._args)
1107
+ new._args.extend(args)
1108
+ return new
1109
+
1110
+ def _clone(self):
1111
+ return Group(*self._args)
1112
+
1113
+ class Aggregate(Variable):
1114
+ def __init__(self, op: Relationship, *args: Value|Distinct, check_args: bool = True, distinct: bool = False):
1115
+ model = self._find_model(args) or op._model
1116
+ super().__init__(model)
1117
+ self._op = op
1118
+ self._where = Fragment(model)
1119
+ self._group = Group()
1120
+ self._args: list[Value] = []
1121
+ self._projection_args: list[Value] = []
1122
+ self._distinct = distinct
1123
+ if check_args:
1124
+ # unwrap distinct if present
1125
+ if any(isinstance(arg, Distinct) for arg in args):
1126
+ if len(args) != 1:
1127
+ exc("Invalid distinct", "Distinct must be applied to all arguments", [source(self)])
1128
+ assert isinstance(args[0], Distinct)
1129
+ args = args[0]._items
1130
+ self._distinct = True
1131
+
1132
+ args = cast(tuple[Value], args)
1133
+
1134
+ num_inputs = sum(f.is_input for f in op._fields)
1135
+ if len(args) < num_inputs:
1136
+ need = [f.name for f in op._fields if f.is_input][len(args):]
1137
+ exc("Missing argument",
1138
+ f"`{op._short_name or 'Relationship'}(..)` is missing: {', '.join(need)}",
1139
+ [source(self)])
1140
+
1141
+ self._projection_args = list(args[:-num_inputs] if num_inputs else args)
1142
+ supplied = iter(args[-num_inputs:] if num_inputs else [])
1143
+
1144
+ self._args = [
1145
+ (next(supplied) if f.is_input else f.type.ref(f.name))
1146
+ for f in op._fields
1147
+ ]
1148
+
1149
+ def _find_model(self, args: Sequence[Value|Distinct|TupleVariable]) -> Model|None:
1150
+ for arg in args:
1151
+ if isinstance(arg, (Variable, Distinct)):
1152
+ return arg._model
1153
+ elif isinstance(arg, TupleVariable):
1154
+ for item in arg._items:
1155
+ if isinstance(item, (Variable, Distinct)):
1156
+ return item._model
1157
+ return None
1158
+
1159
+
1160
+ def where(self, *args: Value) -> Aggregate:
1161
+ new = self._clone()
1162
+ new._where = new._where.where(*args)
1163
+ return new
1164
+
1165
+ def per(self, *args: Value) -> Aggregate:
1166
+ new = self._clone()
1167
+ new._group = new._group._extend(args)
1168
+ return new
1169
+
1170
+ def _clone(self):
1171
+ agg = Aggregate(self._op, check_args=False)
1172
+ agg._args = self._args
1173
+ agg._projection_args = self._projection_args
1174
+ agg._where = self._where
1175
+ agg._group = self._group
1176
+ agg._distinct = self._distinct
1177
+ return agg
1178
+
1179
+ #------------------------------------------------------
1180
+ # Data
1181
+ #------------------------------------------------------
1182
+
1183
+ class Data(Table):
1184
+ def __init__(self, df:DataFrame, model:Model):
1185
+ schema = {}
1186
+ for col in df.columns:
1187
+ _type = df[col].dtype
1188
+ if pd.api.types.is_datetime64_any_dtype(_type):
1189
+ col_type = "DateTime"
1190
+ elif pd.api.types.is_object_dtype(_type) and self._is_date_column(df[col]):
1191
+ col_type = "Date"
1192
+ else:
1193
+ col_type = pytype_to_concept_name.get(_type, "Any")
1194
+ if isinstance(col, int):
1195
+ col = f"col{col}"
1196
+ schema[col] = CoreConcepts[col_type]
1197
+ super().__init__("Data", schema, model)
1198
+ self._data = df
1199
+
1200
+ def _is_date_column(self, col) -> bool:
1201
+ sample = col.dropna()
1202
+ if sample.empty:
1203
+ return False
1204
+ sample_value = sample.iloc[0]
1205
+ return isinstance(sample_value, dt.date) and not isinstance(sample_value, dt.datetime)
1206
+
1207
+ @staticmethod
1208
+ def raw_to_df(data: DataFrame | list[tuple] | list[dict], columns:list[str]|None) -> DataFrame:
1209
+ if isinstance(data, DataFrame):
1210
+ return data
1211
+ if not data:
1212
+ return DataFrame()
1213
+ if isinstance(data, list):
1214
+ if isinstance(data[0], tuple):
1215
+ # Named tuple check
1216
+ if hasattr(data[0], '_fields'):
1217
+ return DataFrame([t._asdict() for t in data]) #type: ignore
1218
+ return DataFrame(data, columns=columns)
1219
+ elif isinstance(data[0], dict):
1220
+ return DataFrame(data)
1221
+ raise TypeError(f"Cannot convert {type(data)} to DataFrame. Use DataFrame, list of tuples, or list of dicts.")
1222
+
1223
+ #------------------------------------------------------
1224
+ # Enum
1225
+ #------------------------------------------------------
1226
+
1227
+ class ModelEnumMeta(EnumMeta):
1228
+ _concept: Concept
1229
+ def __setattr__(self, name: str, value: Any) -> None:
1230
+ if name.startswith("_") or isinstance(value, self):
1231
+ super().__setattr__(name, value)
1232
+ elif isinstance(value, (Relationship, Reading)):
1233
+ setattr(self._concept, name, value)
1234
+ else:
1235
+ raise AttributeError(f"Cannot set attribute {name} on {type(self).__name__}")
1236
+
1237
+ def __format__(self, format_spec: str) -> str:
1238
+ return format(self._concept, format_spec)
1239
+
1240
+ class ModelEnum(Enum, metaclass=ModelEnumMeta):
1241
+ _model:Model
1242
+
1243
+ def __init__(self, *args) -> None:
1244
+ super().__init__(*args)
1245
+ self._source = SourcePos.new()
1246
+
1247
+ def _compile_lookup(self):
1248
+ concept = getattr(self.__class__, "_concept")
1249
+ return concept.to_identity(name=self.name)
1250
+
1251
+ @classmethod
1252
+ def lookup(cls, value:Variable|str):
1253
+ concept = cls._concept
1254
+ return concept.to_identity(name=value)
1255
+
1256
+ # Python 3.10 doesn't correctly populate __members__ by the time it calls
1257
+ # __init_subclass__, so we need to initialize the members lazily when we
1258
+ # encounter the enum for the first time.
1259
+ @classmethod
1260
+ def _init_members(cls):
1261
+ if cls._has_inited_members:
1262
+ return
1263
+ c = cls._concept
1264
+ # Add the name and value attributes to the hashes we create for the enum
1265
+ members = [
1266
+ c.new(name=name, value=value.value)
1267
+ for name, value in cls.__members__.items()
1268
+ ]
1269
+ cls._model.define(*members)
1270
+ cls._has_inited_members = True
1271
+
1272
+ def __format__(self, format_spec: str) -> str:
1273
+ return format(self._concept, format_spec)
1274
+
1275
+ def create_enum_class(model: Model):
1276
+ class AttachedModelEnum(ModelEnum):
1277
+ def __init_subclass__(cls, **kwargs):
1278
+ super().__init_subclass__(**kwargs)
1279
+ # this is voodoo black magic that is doing meta meta programming where
1280
+ # we are plugging into anytime a new subtype of this class is created
1281
+ # and then creating a concept to represent the enum. This happens both
1282
+ # when you do `class Foo(Enum)` and when you do `Enum("Foo", [a, b, c])`
1283
+ cls._model = model
1284
+ c = model.Concept(
1285
+ cls.__name__,
1286
+ extends=[CoreConcepts["Enum"]],
1287
+ identify_by={"name": CoreConcepts["String"]}
1288
+ )
1289
+ model.enums.append(cls)
1290
+ model.enums_index[cls.__name__] = cls
1291
+ cls._has_inited_members = False
1292
+ cls._concept = c
1293
+
1294
+ return AttachedModelEnum
1295
+
1296
+ #------------------------------------------------------
1297
+ # Value
1298
+ #------------------------------------------------------
1299
+
1300
+ Value = Variable|Primitive|Field|TupleVariable
1301
+
1302
+ #------------------------------------------------------
1303
+ # Statement
1304
+ #------------------------------------------------------
1305
+
1306
+ Statement = Value | Group | Not | Distinct | Aggregate
1307
+ """Union of statement types."""
1308
+
1309
+ StatementAndSchema = Statement | TableSchema
1310
+
1311
+ #------------------------------------------------------
1312
+ # Fragment
1313
+ #------------------------------------------------------
1314
+
1315
+ @include_in_docs
1316
+ class Fragment(DerivedTable):
1317
+ """Composable chunk of a query with select/where/define state.
1318
+
1319
+ Parameters
1320
+ ----------
1321
+ model : Model
1322
+ The semantic model that provides type information and stores any
1323
+ definitions produced by this fragment.
1324
+ parent : Fragment, optional
1325
+ An existing fragment to inherit selection and filter state from when
1326
+ building chained queries.
1327
+ """
1328
+
1329
+ def __init__(self, model:Model, parent:Fragment|None=None):
1330
+ super().__init__(model)
1331
+ self._id = next(_global_id)
1332
+ self._select = []
1333
+ self._where = []
1334
+ self._require = []
1335
+ self._define = []
1336
+ self._order_by = []
1337
+ self._limit = 0
1338
+ self._model = model
1339
+ self._into:Optional[Table] = None
1340
+ self._is_into_update = False
1341
+ self._has_executed = False
1342
+ assert self._model, "Fragment must have a model"
1343
+
1344
+ self._parent = parent
1345
+ # self._source = runtime_env.get_source_pos()
1346
+ self._meta = {}
1347
+ self._annotations = []
1348
+ if parent is not None:
1349
+ self._select.extend(parent._select)
1350
+ self._where.extend(parent._where)
1351
+ self._require.extend(parent._require)
1352
+ self._define.extend(parent._define)
1353
+ self._order_by.extend(parent._order_by)
1354
+ self._limit = parent._limit
1355
+
1356
+
1357
+ def _add_items(self, items:Sequence[Statement], to_attr:list[Statement]):
1358
+ # TODO: ensure that you are _either_ a select, require, or then
1359
+ # not a mix of them
1360
+ model = self._model
1361
+
1362
+ # remove any existing rules that this consumes
1363
+ for item in itertools.chain(items, [self._parent]):
1364
+ if isinstance(item, Fragment) and item._is_effect():
1365
+ model._remove_rule(item)
1366
+
1367
+ to_attr.extend(items)
1368
+ if self._is_effect():
1369
+ model._add_rule(self)
1370
+
1371
+ return self
1372
+
1373
+ #------------------------------------------------------
1374
+ # Select arg handling
1375
+ #------------------------------------------------------
1376
+
1377
+ def _check_select_args(self, args:Sequence[StatementAndSchema]) -> Sequence[Statement]:
1378
+ clean_args = []
1379
+ for arg in args:
1380
+ # If you select x > y, treat that as AsBool(x > y)
1381
+ if isinstance(arg, Expression) and not arg._has_output:
1382
+ clean_args.append(AsBool(arg))
1383
+ elif isinstance(arg, TableSchema):
1384
+ for col in arg.get_columns():
1385
+ clean_args.append(col(arg._table))
1386
+ else:
1387
+ clean_args.append(arg)
1388
+ return clean_args
1389
+
1390
+ #------------------------------------------------------
1391
+ # Core API
1392
+ #------------------------------------------------------
1393
+
1394
+ def where(self, *args: Statement) -> Fragment:
1395
+ f = Fragment(self._model, parent=self)
1396
+ return f._add_items(args, f._where)
1397
+
1398
+ def select(self, *args: StatementAndSchema) -> Fragment:
1399
+ f = Fragment(self._model, parent=self)
1400
+ return f._add_items(self._check_select_args(args), f._select)
1401
+
1402
+ def require(self, *args: Statement) -> Fragment:
1403
+ f = Fragment(self._model, parent=self)
1404
+ return f._add_items(args, f._require)
1405
+
1406
+ def define(self, *args: Statement) -> Fragment:
1407
+ f = Fragment(self._model, parent=self)
1408
+ return f._add_items(args, f._define)
1409
+
1410
+ if not TYPE_CHECKING:
1411
+ def order_by(self, *args: Any) -> Fragment:
1412
+ exc("Feature unavailable", "The 'order_by' method is not yet available when querying RAI directly.", [
1413
+ source(self),
1414
+ "You can use [cyan]`relationalai.semantics.std.aggregates.rank`[/cyan] and select it as the first column as a temporary substitute.",
1415
+ ])
1416
+ f = Fragment(self._model, parent=self)
1417
+ return f._add_items(args, f._order_by)
1418
+
1419
+ def limit(self, n:int) -> Fragment:
1420
+ exc("Feature unavailable", "The 'order_by' method is not yet available when querying RAI directly.", [
1421
+ source(self),
1422
+ "You can use [cyan]`relationalai.semantics.std.aggregates.limit`[/cyan] in a where clause as a temporary substitute.",
1423
+ ])
1424
+ f = Fragment(self._model, parent=self)
1425
+ f._limit = n
1426
+ return f
1427
+
1428
+ # def meta(self, **kwargs: Any) -> Fragment:
1429
+ # self._meta.update(kwargs)
1430
+ # return self
1431
+
1432
+ def annotate(self, *annos:Expression|Relationship) -> Fragment:
1433
+ self._annotations.extend(annos)
1434
+ return self
1435
+
1436
+ #------------------------------------------------------
1437
+ # into
1438
+ #------------------------------------------------------
1439
+
1440
+ def into(self, table: Table, update=False) -> Fragment:
1441
+ f = Fragment(self._model, parent=self)
1442
+ f._into = table
1443
+ f._is_into_update = update
1444
+ self._model.exports.add(f)
1445
+ return f
1446
+
1447
+ #------------------------------------------------------
1448
+ # Execution
1449
+ #------------------------------------------------------
1450
+
1451
+ def exec(self):
1452
+ if self._has_executed:
1453
+ return
1454
+ if self._into is None:
1455
+ exc("Cannot execute", "Query must have an 'into' table specified to execute.", [source(self)])
1456
+ from relationalai.shims.executor import execute
1457
+ self._has_executed = True
1458
+ return execute(self, self._model, export_to=self._into._name, update=self._is_into_update)
1459
+
1460
+ def to_df(self):
1461
+ from relationalai.shims.executor import execute
1462
+ return execute(self, self._model)
1463
+
1464
+ def inspect(self):
1465
+ print(self.to_df())
1466
+
1467
+ #------------------------------------------------------
1468
+ # helpers
1469
+ #------------------------------------------------------
1470
+
1471
+ def _is_effect(self) -> bool:
1472
+ return bool(self._define or self._require)
1473
+
1474
+ def _is_where_only(self) -> bool:
1475
+ return not self._select and not self._define and not self._require and not self._order_by
1476
+
1477
+ #------------------------------------------------------
1478
+ # And/Or
1479
+ #------------------------------------------------------
1480
+
1481
+ def __or__(self, other) -> Match:
1482
+ return Match(self._model, self, other)
1483
+
1484
+ def __and__(self, other) -> Fragment:
1485
+ if not isinstance(other, Fragment):
1486
+ other = Fragment(self._model).where(other)
1487
+ if self._is_where_only() and other._is_where_only():
1488
+ return self.where(*other._where)
1489
+ elif self._is_where_only():
1490
+ return other.where(*self._where)
1491
+ elif other._is_where_only():
1492
+ return self.where(*other._where)
1493
+ else:
1494
+ raise Exception("Cannot AND two non-where-only fragments")
1495
+
1496
+ #------------------------------------------------------
1497
+ # DerivedTable cols
1498
+ #------------------------------------------------------
1499
+
1500
+ def _get_cols(self):
1501
+ return [DerivedColumn.from_value(self, i, col) for i, col in enumerate(self._select)]
1502
+
1503
+ #------------------------------------------------------
1504
+ # Marterialize
1505
+ #------------------------------------------------------
1506
+
1507
+ def to_metamodel(self):
1508
+ m = self._model
1509
+ return m._compiler.compile(self)
1510
+
1511
+ #------------------------------------------------------
1512
+ # Model
1513
+ #------------------------------------------------------
1514
+
1515
+ @include_in_docs
1516
+ class Model:
1517
+ """Class representing a semantic model.
1518
+
1519
+ Parameters
1520
+ ----------
1521
+ name : str, optional
1522
+ Name of the model.
1523
+ exclude_core : bool, optional
1524
+ If True, the core library will not be included by default.
1525
+ is_library : bool, optional
1526
+ If True, this model is a library and will not be added to the global
1527
+ list of models.
1528
+
1529
+ Examples
1530
+ --------
1531
+ Create a model object:
1532
+
1533
+ >>> from relationalai.semantics import Model
1534
+ >>> model = Model(name="MyModel")
1535
+ """
1536
+ all_models:list[Model] = []
1537
+
1538
+ def __init__(self, name: str = "", exclude_core: bool = False, is_library: bool = False):
1539
+ self.name = name
1540
+ self.defines:KeyedSet[Fragment] = KeyedSet(dsl_key)
1541
+ self.requires:KeyedSet[Fragment] = KeyedSet(dsl_key)
1542
+ self.exports:KeyedSet[Fragment] = KeyedSet(dsl_key)
1543
+ self.libraries = []
1544
+ self.concepts: list[Concept] = []
1545
+ self.tables: list[Table] = []
1546
+ self.relationships: list[Relationship] = []
1547
+ self.enums: list[Type[ModelEnum]] = []
1548
+ self.concept_index: dict[str, Concept] = {}
1549
+ self.table_index: dict[str, Table] = {}
1550
+ self.relationship_index: dict[str, Relationship] = {}
1551
+ self.enums_index: dict[str, Type[ModelEnum]] = {}
1552
+ self._source = SourcePos.new()
1553
+
1554
+ self.Enum = create_enum_class(self)
1555
+
1556
+ if not is_library:
1557
+ Model.all_models.append(self)
1558
+
1559
+ self.__compiler = None
1560
+ if not exclude_core:
1561
+ self.libraries.append(CoreLibrary)
1562
+
1563
+ #------------------------------------------------------
1564
+ # Internal
1565
+ #------------------------------------------------------
1566
+
1567
+ @property
1568
+ def _compiler(self):
1569
+ if not self.__compiler:
1570
+ from .front_compiler import FrontCompiler
1571
+ self.__compiler = FrontCompiler(self)
1572
+ return self.__compiler
1573
+
1574
+ def _find_concept(self, name: str) -> Concept|None:
1575
+ if name in self.concept_index:
1576
+ return self.concept_index[name]
1577
+ if name in self.table_index:
1578
+ return self.table_index[name]
1579
+ for lib in self.libraries:
1580
+ found = lib._find_concept(name)
1581
+ if found is not None:
1582
+ return found
1583
+
1584
+ def _remove_rule(self, fragment: Fragment) -> None:
1585
+ if fragment._define and fragment in self.defines:
1586
+ self.defines.remove(fragment)
1587
+ elif fragment._require and fragment in self.requires:
1588
+ self.requires.remove(fragment)
1589
+ else:
1590
+ raise ValueError("Fragment must have either define or require clauses to be removed as a rule")
1591
+
1592
+ def _add_rule(self, fragment: Fragment) -> None:
1593
+ if fragment._define and fragment not in self.defines:
1594
+ self.defines.add(fragment)
1595
+ elif fragment._require and fragment not in self.requires:
1596
+ self.requires.add(fragment)
1597
+ else:
1598
+ raise ValueError("Fragment must have either define or require clauses to be added as a rule")
1599
+
1600
+ #------------------------------------------------------
1601
+ # Primary API
1602
+ #------------------------------------------------------
1603
+
1604
+ def Concept(self, name: str, extends: list[Concept] = [], identify_by: dict[str, Property|Concept] = {}) -> Concept:
1605
+ c = Concept(name, extends=extends, identify_by=identify_by, model=self)
1606
+ self.concepts.append(c)
1607
+ self.concept_index[name] = c
1608
+ return c
1609
+
1610
+ def Table(self, path: str, schema: dict[str, Concept] = {}) -> Table:
1611
+ t = Table(path, schema=schema, model=self)
1612
+ self.tables.append(t)
1613
+ self.table_index[path] = t
1614
+ return t
1615
+
1616
+ def Relationship(self, reading: str = "", fields: list[Field] = [], short_name: str = "") -> Relationship:
1617
+ r = Relationship(self, reading, fields, short_name)
1618
+ self.relationships.append(r)
1619
+ self.relationship_index[short_name] = r
1620
+ return r
1621
+
1622
+ def Property(self, reading: str = "", fields: list[Field] = [], short_name: str = "") -> Property:
1623
+ p = Property(self, reading, fields, short_name)
1624
+ self.relationships.append(p)
1625
+ self.relationship_index[short_name] = p
1626
+ return p
1627
+
1628
+ def select(self, *args: StatementAndSchema) -> Fragment:
1629
+ f = Fragment(self)
1630
+ return f.select(*args)
1631
+
1632
+ def where(self, *args: Statement) -> Fragment:
1633
+ f = Fragment(self)
1634
+ return f.where(*args)
1635
+
1636
+ def require(self, *args: Statement) -> Fragment:
1637
+ f = Fragment(self)
1638
+ return f.require(*args)
1639
+
1640
+ def define(self, *args: Statement) -> Fragment:
1641
+ f = Fragment(self)
1642
+ return f.define(*args)
1643
+
1644
+ def union(self, *items: Value) -> Union:
1645
+ return Union(self, *items)
1646
+
1647
+ def data(self, data: DataFrame | list[tuple] | list[dict], columns: list[str]|None = None) -> Data:
1648
+ df = Data.raw_to_df(data, columns)
1649
+ return Data(df, self)
1650
+
1651
+ def not_(self, *items: Value) -> Not:
1652
+ return Not(*items, model=self)
1653
+
1654
+ def distinct(self, *items: Value) -> Distinct:
1655
+ return Distinct(*items, model=self)
1656
+
1657
+ #------------------------------------------------------
1658
+ # Meta
1659
+ #------------------------------------------------------
1660
+
1661
+ def to_metamodel(self) -> mModel:
1662
+ return self._compiler.compile_model(self)
1663
+
1664
+ #------------------------------------------------------
1665
+ # Library
1666
+ #------------------------------------------------------
1667
+
1668
+ class Library(Model):
1669
+ def __init__(self, name: str, exclude_core: bool = False):
1670
+ super().__init__(name=name, exclude_core=exclude_core, is_library=True)
1671
+
1672
+ def Type(self, name: str, super_types: list[Concept] = []) -> Concept:
1673
+ c = self.Concept(name, extends=super_types)
1674
+ self._register_builtin(c)
1675
+ return c
1676
+
1677
+ def Relation(self, name: str, fields: list[Field], overloads: list[list[Concept]]|None = None, annotations: list[Any]|None = None) -> Relationship:
1678
+ r = Relationship(self, "", fields, name, allow_no_fields=True, overloads=overloads)
1679
+ self.relationships.append(r)
1680
+ self.relationship_index[name] = r
1681
+ self._register_builtin(r)
1682
+ # TODO - deal with annotations
1683
+ return r
1684
+
1685
+ def _register_builtin(self, builtin: Concept|Relationship) -> None:
1686
+ """ Register a builtin concept or relationship in the global builtins registry. """
1687
+ builtins.register(self.name, self._compiler.to_relation(builtin))
1688
+
1689
+
1690
+ #------------------------------------------------------
1691
+ # Core Library
1692
+ #------------------------------------------------------
1693
+
1694
+ # Library is populated when relationalai.semantics.frontend.core is imported
1695
+ CoreLibrary: Library = Library("core", exclude_core=True)
1696
+ CoreRelationships = CoreLibrary.relationship_index
1697
+ CoreConcepts = CoreLibrary.concept_index
1698
+
1699
+
1700
+ #------------------------------------------------------
1701
+ # todo
1702
+ #------------------------------------------------------
1703
+
1704
+ """
1705
+ x new/to_identity/filter_by
1706
+ x capture all defines/requires
1707
+ x identify_by
1708
+ x figure out Table/DerivedTable
1709
+ x data node
1710
+ x in_
1711
+ x error on invalid iteration/sum/min/max
1712
+ x Aggregates
1713
+ x fill in std.agg functions when Thiago's lib stuff lands
1714
+ - Error?
1715
+ x rank stuff
1716
+ - validation checks for identify_by
1717
+ - unique
1718
+ - Enum?
1719
+ """