pytrilogy 0.0.3.46__tar.gz → 0.0.3.48__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pytrilogy might be problematic. Click here for more details.

Files changed (142) hide show
  1. {pytrilogy-0.0.3.46/pytrilogy.egg-info → pytrilogy-0.0.3.48}/PKG-INFO +12 -15
  2. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/README.md +11 -14
  3. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48/pytrilogy.egg-info}/PKG-INFO +12 -15
  4. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/pytrilogy.egg-info/SOURCES.txt +0 -1
  5. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_functions.py +3 -0
  6. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_parsing_failures.py +2 -2
  7. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/__init__.py +1 -1
  8. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/environment_helpers.py +2 -27
  9. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/author.py +4 -1
  10. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/build.py +60 -34
  11. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/execute.py +2 -1
  12. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/optimization.py +0 -3
  13. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/optimizations/__init__.py +0 -2
  14. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/group_node.py +4 -1
  15. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/__init__.py +2 -0
  16. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/query_processor.py +28 -14
  17. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/statements/author.py +21 -9
  18. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/base.py +22 -4
  19. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/common.py +25 -14
  20. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/executor.py +3 -0
  21. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/common.py +73 -16
  22. pytrilogy-0.0.3.48/trilogy/parsing/exceptions.py +8 -0
  23. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/parse_engine.py +34 -29
  24. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/trilogy.lark +1 -4
  25. pytrilogy-0.0.3.46/trilogy/core/optimizations/inline_constant.py +0 -35
  26. pytrilogy-0.0.3.46/trilogy/parsing/exceptions.py +0 -2
  27. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/LICENSE.md +0 -0
  28. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/pyproject.toml +0 -0
  29. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/pytrilogy.egg-info/dependency_links.txt +0 -0
  30. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/pytrilogy.egg-info/entry_points.txt +0 -0
  31. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/pytrilogy.egg-info/requires.txt +0 -0
  32. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/pytrilogy.egg-info/top_level.txt +0 -0
  33. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/setup.cfg +0 -0
  34. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/setup.py +0 -0
  35. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_datatypes.py +0 -0
  36. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_declarations.py +0 -0
  37. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_derived_concepts.py +0 -0
  38. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_discovery_nodes.py +0 -0
  39. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_enums.py +0 -0
  40. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_environment.py +0 -0
  41. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_executor.py +0 -0
  42. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_failure.py +0 -0
  43. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_imports.py +0 -0
  44. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_metadata.py +0 -0
  45. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_models.py +0 -0
  46. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_multi_join_assignments.py +0 -0
  47. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_parse_engine.py +0 -0
  48. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_parsing.py +0 -0
  49. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_partial_handling.py +0 -0
  50. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_query_processing.py +0 -0
  51. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_query_render.py +0 -0
  52. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_select.py +0 -0
  53. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_show.py +0 -0
  54. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_statements.py +0 -0
  55. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_typing.py +0 -0
  56. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_undefined_concept.py +0 -0
  57. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_user_functions.py +0 -0
  58. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/tests/test_where_clause.py +0 -0
  59. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/authoring/__init__.py +0 -0
  60. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/compiler.py +0 -0
  61. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/constants.py +0 -0
  62. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/__init__.py +0 -0
  63. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/constants.py +0 -0
  64. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/enums.py +0 -0
  65. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/env_processor.py +0 -0
  66. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/ergonomics.py +0 -0
  67. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/exceptions.py +0 -0
  68. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/functions.py +0 -0
  69. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/graph_models.py +0 -0
  70. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/internal.py +0 -0
  71. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/__init__.py +0 -0
  72. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/build_environment.py +0 -0
  73. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/core.py +0 -0
  74. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/datasource.py +0 -0
  75. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/models/environment.py +0 -0
  76. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/optimizations/base_optimization.py +0 -0
  77. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/optimizations/inline_datasource.py +0 -0
  78. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  79. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/__init__.py +0 -0
  80. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  81. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/graph_utils.py +0 -0
  82. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/__init__.py +0 -0
  83. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  84. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/common.py +0 -0
  85. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  86. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  87. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  88. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  89. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  90. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  91. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  92. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  93. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/select_node.py +0 -0
  94. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  95. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/union_node.py +0 -0
  96. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  97. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/node_generators/window_node.py +0 -0
  98. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/base_node.py +0 -0
  99. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/filter_node.py +0 -0
  100. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/group_node.py +0 -0
  101. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/merge_node.py +0 -0
  102. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  103. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/union_node.py +0 -0
  104. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  105. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/nodes/window_node.py +0 -0
  106. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/processing/utility.py +0 -0
  107. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/statements/__init__.py +0 -0
  108. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/statements/build.py +0 -0
  109. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/statements/common.py +0 -0
  110. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/core/statements/execute.py +0 -0
  111. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/__init__.py +0 -0
  112. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/bigquery.py +0 -0
  113. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/config.py +0 -0
  114. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/dataframe.py +0 -0
  115. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/duckdb.py +0 -0
  116. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/enums.py +0 -0
  117. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/postgres.py +0 -0
  118. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/presto.py +0 -0
  119. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/snowflake.py +0 -0
  120. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/dialect/sql_server.py +0 -0
  121. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/engine.py +0 -0
  122. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/hooks/__init__.py +0 -0
  123. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/hooks/base_hook.py +0 -0
  124. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/hooks/graph_hook.py +0 -0
  125. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/hooks/query_debugger.py +0 -0
  126. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/metadata/__init__.py +0 -0
  127. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parser.py +0 -0
  128. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/__init__.py +0 -0
  129. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/config.py +0 -0
  130. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/helpers.py +0 -0
  131. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/parsing/render.py +0 -0
  132. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/py.typed +0 -0
  133. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/render.py +0 -0
  134. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/scripts/__init__.py +0 -0
  135. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/scripts/trilogy.py +0 -0
  136. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/std/__init__.py +0 -0
  137. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/std/date.preql +0 -0
  138. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/std/display.preql +0 -0
  139. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/std/geography.preql +0 -0
  140. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/std/money.preql +0 -0
  141. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/std/report.preql +0 -0
  142. {pytrilogy-0.0.3.46 → pytrilogy-0.0.3.48}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.46
3
+ Version: 0.0.3.48
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -53,7 +53,7 @@ Installation: `pip install pytrilogy`
53
53
 
54
54
  You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
55
55
 
56
- Trilogy:
56
+ Trilogy looks like SQL:
57
57
  ```sql
58
58
  WHERE
59
59
  name like '%lvis%'
@@ -65,7 +65,7 @@ ORDER BY
65
65
  LIMIT 10;
66
66
  ```
67
67
  ## Goals
68
- vs SQL, the goals are:
68
+ And aims to:
69
69
 
70
70
  Preserve:
71
71
  - Correctness
@@ -85,6 +85,7 @@ Maintain:
85
85
  Save the following code in a file named `hello.preql`
86
86
 
87
87
  ```python
88
+ # semantic model is abstract from data
88
89
  key sentence_id int;
89
90
  property sentence_id.word_one string; # comments after a definition
90
91
  property sentence_id.word_two string; # are syntactic sugar for adding
@@ -92,7 +93,8 @@ property sentence_id.word_three string; # a description to it
92
93
 
93
94
  # comments in other places are just comments
94
95
 
95
- # define our datasources as queries in duckdb
96
+ # define our datasource to bind the model to data
97
+ # testing using query fixtures is a common pattern
96
98
  datasource word_one(
97
99
  sentence: sentence_id,
98
100
  word:word_one
@@ -126,25 +128,20 @@ union all
126
128
  select 2 as sentence, '!'
127
129
  ''';
128
130
 
131
+ def concat_with_space(x,y) -> x || ' ' || y;
132
+
129
133
  # an actual select statement
130
134
  # joins are automatically resolved between the 3 sources
131
135
  with sentences as
132
- select sentence_id, word_one || ' ' || word_two || word_three as text;
136
+ select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
133
137
 
134
- SELECT
135
- --sentences.sentence_id,
136
- sentences.text
137
138
  WHERE
138
- sentences.sentence_id = 1
139
- ;
140
-
139
+ sentences.sentence_id in (1,2)
141
140
  SELECT
142
- --sentences.sentence_id,
143
141
  sentences.text
144
- WHERE
145
- sentences.sentence_id = 2
146
142
  ;
147
- # semicolon termination for all statements
143
+
144
+
148
145
 
149
146
  ```
150
147
 
@@ -15,7 +15,7 @@ Installation: `pip install pytrilogy`
15
15
 
16
16
  You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
17
17
 
18
- Trilogy:
18
+ Trilogy looks like SQL:
19
19
  ```sql
20
20
  WHERE
21
21
  name like '%lvis%'
@@ -27,7 +27,7 @@ ORDER BY
27
27
  LIMIT 10;
28
28
  ```
29
29
  ## Goals
30
- vs SQL, the goals are:
30
+ And aims to:
31
31
 
32
32
  Preserve:
33
33
  - Correctness
@@ -47,6 +47,7 @@ Maintain:
47
47
  Save the following code in a file named `hello.preql`
48
48
 
49
49
  ```python
50
+ # semantic model is abstract from data
50
51
  key sentence_id int;
51
52
  property sentence_id.word_one string; # comments after a definition
52
53
  property sentence_id.word_two string; # are syntactic sugar for adding
@@ -54,7 +55,8 @@ property sentence_id.word_three string; # a description to it
54
55
 
55
56
  # comments in other places are just comments
56
57
 
57
- # define our datasources as queries in duckdb
58
+ # define our datasource to bind the model to data
59
+ # testing using query fixtures is a common pattern
58
60
  datasource word_one(
59
61
  sentence: sentence_id,
60
62
  word:word_one
@@ -88,25 +90,20 @@ union all
88
90
  select 2 as sentence, '!'
89
91
  ''';
90
92
 
93
+ def concat_with_space(x,y) -> x || ' ' || y;
94
+
91
95
  # an actual select statement
92
96
  # joins are automatically resolved between the 3 sources
93
97
  with sentences as
94
- select sentence_id, word_one || ' ' || word_two || word_three as text;
98
+ select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
95
99
 
96
- SELECT
97
- --sentences.sentence_id,
98
- sentences.text
99
100
  WHERE
100
- sentences.sentence_id = 1
101
- ;
102
-
101
+ sentences.sentence_id in (1,2)
103
102
  SELECT
104
- --sentences.sentence_id,
105
103
  sentences.text
106
- WHERE
107
- sentences.sentence_id = 2
108
104
  ;
109
- # semicolon termination for all statements
105
+
106
+
110
107
 
111
108
  ```
112
109
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.46
3
+ Version: 0.0.3.48
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -53,7 +53,7 @@ Installation: `pip install pytrilogy`
53
53
 
54
54
  You can read more about the project [here](https://trilogydata.dev/) and try out an interactive demo [here](https://trilogydata.dev/demo/).
55
55
 
56
- Trilogy:
56
+ Trilogy looks like SQL:
57
57
  ```sql
58
58
  WHERE
59
59
  name like '%lvis%'
@@ -65,7 +65,7 @@ ORDER BY
65
65
  LIMIT 10;
66
66
  ```
67
67
  ## Goals
68
- vs SQL, the goals are:
68
+ And aims to:
69
69
 
70
70
  Preserve:
71
71
  - Correctness
@@ -85,6 +85,7 @@ Maintain:
85
85
  Save the following code in a file named `hello.preql`
86
86
 
87
87
  ```python
88
+ # semantic model is abstract from data
88
89
  key sentence_id int;
89
90
  property sentence_id.word_one string; # comments after a definition
90
91
  property sentence_id.word_two string; # are syntactic sugar for adding
@@ -92,7 +93,8 @@ property sentence_id.word_three string; # a description to it
92
93
 
93
94
  # comments in other places are just comments
94
95
 
95
- # define our datasources as queries in duckdb
96
+ # define our datasource to bind the model to data
97
+ # testing using query fixtures is a common pattern
96
98
  datasource word_one(
97
99
  sentence: sentence_id,
98
100
  word:word_one
@@ -126,25 +128,20 @@ union all
126
128
  select 2 as sentence, '!'
127
129
  ''';
128
130
 
131
+ def concat_with_space(x,y) -> x || ' ' || y;
132
+
129
133
  # an actual select statement
130
134
  # joins are automatically resolved between the 3 sources
131
135
  with sentences as
132
- select sentence_id, word_one || ' ' || word_two || word_three as text;
136
+ select sentence_id, @concat_with_space(word_one, word_two) || word_three as text;
133
137
 
134
- SELECT
135
- --sentences.sentence_id,
136
- sentences.text
137
138
  WHERE
138
- sentences.sentence_id = 1
139
- ;
140
-
139
+ sentences.sentence_id in (1,2)
141
140
  SELECT
142
- --sentences.sentence_id,
143
141
  sentences.text
144
- WHERE
145
- sentences.sentence_id = 2
146
142
  ;
147
- # semicolon termination for all statements
143
+
144
+
148
145
 
149
146
  ```
150
147
 
@@ -66,7 +66,6 @@ trilogy/core/models/environment.py
66
66
  trilogy/core/models/execute.py
67
67
  trilogy/core/optimizations/__init__.py
68
68
  trilogy/core/optimizations/base_optimization.py
69
- trilogy/core/optimizations/inline_constant.py
70
69
  trilogy/core/optimizations/inline_datasource.py
71
70
  trilogy/core/optimizations/predicate_pushdown.py
72
71
  trilogy/core/processing/__init__.py
@@ -123,6 +123,7 @@ def test_date_functions(test_environment):
123
123
  year(order_timestamp) -> order_year,
124
124
  date_trunc(order_timestamp, month) -> order_month_trunc,
125
125
  date_add(order_timestamp, month, 1) -> one_month_post_order,
126
+ date_sub(order_timestamp, month, 1) -> one_month_pre_order,
126
127
  date_trunc(order_timestamp, day) -> order_day_trunc,
127
128
  date_trunc(order_timestamp, year) -> order_year_trunc,
128
129
  date_trunc(order_timestamp, hour) -> order_hour_trunc,
@@ -212,6 +213,7 @@ def test_math_functions(test_environment):
212
213
  property order_sub <- revenue - 2;
213
214
  property order_id.order_nested <- revenue * 2/2;
214
215
  property order_id.rounded <- round(revenue + 2.01,2);
216
+ property order_id.rounded_default <- round(revenue + 2.01);
215
217
  constant random <- random(1);
216
218
  select
217
219
  order_id,
@@ -221,6 +223,7 @@ def test_math_functions(test_environment):
221
223
  order_sub,
222
224
  order_add,
223
225
  rounded,
226
+ rounded_default,
224
227
  random,
225
228
  ;"""
226
229
  env, parsed = parse(declarations, environment=test_environment)
@@ -8,7 +8,7 @@ from trilogy.core.models.environment import (
8
8
  EnvironmentOptions,
9
9
  )
10
10
  from trilogy.parsing.parse_engine import (
11
- ParseError,
11
+ NameShadowError,
12
12
  parse_text,
13
13
  )
14
14
 
@@ -77,7 +77,7 @@ SELECT
77
77
  1+2->scalar
78
78
  ;
79
79
  """
80
- with raises(ParseError) as e:
80
+ with raises(NameShadowError) as e:
81
81
  env, parsed = parse_text(
82
82
  x, parse_config=Parsing(strict_name_shadow_enforcement=True)
83
83
  )
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.46"
7
+ __version__ = "0.0.3.48"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -6,23 +6,8 @@ from trilogy.core.models.core import DataType, StructType, arg_to_datatype
6
6
  from trilogy.core.models.environment import Environment
7
7
  from trilogy.parsing.common import Meta
8
8
 
9
- FUNCTION_DESCRIPTION_MAPS = {
10
- FunctionType.DATE: "The date part of a timestamp/date. Integer, 0-31 depending on month.",
11
- FunctionType.MONTH: "The month part of a timestamp/date. Integer, 1-12.",
12
- FunctionType.YEAR: "The year part of a timestamp/date. Integer.",
13
- FunctionType.QUARTER: "The quarter part of a timestamp/date. Integer, 1-4.",
14
- FunctionType.DAY_OF_WEEK: "The day of the week part of a timestamp/date. Integer, 0-6.",
15
- FunctionType.HOUR: "The hour part of a timestamp. Integer, 0-23.",
16
- FunctionType.MINUTE: "The minute part of a timestamp. Integer, 0-59.",
17
- FunctionType.SECOND: "The second part of a timestamp. Integer, 0-59.",
18
- }
19
-
20
9
 
21
10
  def generate_date_concepts(concept: Concept, environment: Environment):
22
- if concept.metadata and concept.metadata.description:
23
- base_description = concept.metadata.description
24
- else:
25
- base_description = f"a {concept.address}"
26
11
  if concept.metadata and concept.metadata.line_number:
27
12
  base_line_number = concept.metadata.line_number
28
13
  else:
@@ -67,7 +52,6 @@ def generate_date_concepts(concept: Concept, environment: Environment):
67
52
  [concept.address],
68
53
  ),
69
54
  metadata=Metadata(
70
- description=f"Auto-derived from {base_description}. {FUNCTION_DESCRIPTION_MAPS.get(ftype, ftype.value)}",
71
55
  line_number=base_line_number,
72
56
  concept_source=ConceptSource.AUTO_DERIVED,
73
57
  ),
@@ -95,7 +79,7 @@ def generate_date_concepts(concept: Concept, environment: Environment):
95
79
  [concept.address],
96
80
  ),
97
81
  metadata=Metadata(
98
- description=f"Auto-derived from {base_description}. The date truncated to the {grain.value}.",
82
+ # description=f"Auto-derived from {base_description}. The date truncated to the {grain.value}.",
99
83
  line_number=base_line_number,
100
84
  concept_source=ConceptSource.AUTO_DERIVED,
101
85
  ),
@@ -105,10 +89,6 @@ def generate_date_concepts(concept: Concept, environment: Environment):
105
89
 
106
90
 
107
91
  def generate_datetime_concepts(concept: Concept, environment: Environment):
108
- if concept.metadata and concept.metadata.description:
109
- base_description = concept.metadata.description
110
- else:
111
- base_description = concept.address
112
92
  if concept.metadata and concept.metadata.line_number:
113
93
  base_line_number = concept.metadata.line_number
114
94
  else:
@@ -146,7 +126,6 @@ def generate_datetime_concepts(concept: Concept, environment: Environment):
146
126
  [concept.address],
147
127
  ),
148
128
  metadata=Metadata(
149
- description=f"Auto-derived from {base_description}. {FUNCTION_DESCRIPTION_MAPS.get(ftype, ftype.value)}",
150
129
  line_number=base_line_number,
151
130
  concept_source=ConceptSource.AUTO_DERIVED,
152
131
  ),
@@ -157,10 +136,6 @@ def generate_datetime_concepts(concept: Concept, environment: Environment):
157
136
 
158
137
 
159
138
  def generate_key_concepts(concept: Concept, environment: Environment):
160
- if concept.metadata and concept.metadata.description:
161
- base_description = concept.metadata.description
162
- else:
163
- base_description = f"a {concept.datatype.value}"
164
139
  if concept.metadata and concept.metadata.line_number:
165
140
  base_line_number = concept.metadata.line_number
166
141
  else:
@@ -186,7 +161,7 @@ def generate_key_concepts(concept: Concept, environment: Environment):
186
161
  namespace=concept.namespace,
187
162
  keys=set(),
188
163
  metadata=Metadata(
189
- description=f"Auto-derived integer. The {ftype.value} of {concept.address}, {base_description}",
164
+ # description=f"Auto-derived integer. The {ftype.value} of {concept.address}, {base_description}",
190
165
  line_number=base_line_number,
191
166
  concept_source=ConceptSource.AUTO_DERIVED,
192
167
  ),
@@ -445,13 +445,16 @@ class Grain(Namespaced, BaseModel):
445
445
  concepts: Iterable[Concept | ConceptRef | str],
446
446
  environment: Environment | None = None,
447
447
  where_clause: WhereClause | None = None,
448
+ local_concepts: dict[str, Concept] | None = None,
448
449
  ) -> Grain:
449
450
  from trilogy.parsing.common import concepts_to_grain_concepts
450
451
 
451
452
  x = Grain.model_construct(
452
453
  components={
453
454
  c.address
454
- for c in concepts_to_grain_concepts(concepts, environment=environment)
455
+ for c in concepts_to_grain_concepts(
456
+ concepts, environment=environment, local_concepts=local_concepts
457
+ )
455
458
  },
456
459
  where_clause=where_clause,
457
460
  )
@@ -126,9 +126,14 @@ def concept_is_relevant(
126
126
 
127
127
  return False
128
128
  if concept.purpose in (Purpose.PROPERTY, Purpose.METRIC) and concept.keys:
129
- if any([c in others for c in concept.keys]):
130
-
129
+ if all([c in others for c in concept.keys]):
131
130
  return False
131
+ if (
132
+ concept.purpose == Purpose.KEY
133
+ and concept.keys
134
+ and all([c in others for c in concept.keys])
135
+ ):
136
+ return False
132
137
  if concept.purpose in (Purpose.METRIC,):
133
138
  if all([c in others for c in concept.grain.components]):
134
139
  return False
@@ -242,6 +247,17 @@ def get_concept_arguments(expr) -> List["BuildConcept"]:
242
247
  return output
243
248
 
244
249
 
250
+ class BuildParamaterizedConceptReference(BaseModel):
251
+ concept: BuildConcept
252
+
253
+ def __str__(self):
254
+ return f":{self.concept.address}"
255
+
256
+ @property
257
+ def safe_address(self) -> str:
258
+ return self.concept.safe_address
259
+
260
+
245
261
  class BuildGrain(BaseModel):
246
262
  components: set[str] = Field(default_factory=set)
247
263
  where_clause: Optional[BuildWhereClause] = None
@@ -1458,11 +1474,35 @@ class Factory:
1458
1474
  {} if local_concepts is None else local_concepts
1459
1475
  )
1460
1476
 
1477
+ def instantiate_concept(
1478
+ self,
1479
+ arg: (
1480
+ AggregateWrapper
1481
+ | FunctionCallWrapper
1482
+ | WindowItem
1483
+ | FilterItem
1484
+ | Function
1485
+ | ListWrapper[Any]
1486
+ | MapWrapper[Any, Any]
1487
+ | int
1488
+ | float
1489
+ | str
1490
+ ),
1491
+ ) -> tuple[Concept, BuildConcept]:
1492
+ from trilogy.parsing.common import arbitrary_to_concept
1493
+
1494
+ new = arbitrary_to_concept(
1495
+ arg,
1496
+ environment=self.environment,
1497
+ )
1498
+ built = self.build(new)
1499
+ self.local_concepts[new.address] = built
1500
+ return new, built
1501
+
1461
1502
  @singledispatchmethod
1462
1503
  def build(self, base):
1463
1504
  raise NotImplementedError("Cannot build {}".format(type(base)))
1464
1505
 
1465
- @build.register
1466
1506
  @build.register
1467
1507
  def _(
1468
1508
  self,
@@ -1496,31 +1536,6 @@ class Factory:
1496
1536
  ):
1497
1537
  return base
1498
1538
 
1499
- def instantiate_concept(
1500
- self,
1501
- arg: (
1502
- AggregateWrapper
1503
- | FunctionCallWrapper
1504
- | WindowItem
1505
- | FilterItem
1506
- | Function
1507
- | ListWrapper[Any]
1508
- | MapWrapper[Any, Any]
1509
- | int
1510
- | float
1511
- | str
1512
- ),
1513
- ) -> tuple[Concept, BuildConcept]:
1514
- from trilogy.parsing.common import arbitrary_to_concept
1515
-
1516
- new = arbitrary_to_concept(
1517
- arg,
1518
- environment=self.environment,
1519
- )
1520
- built = self.build(new)
1521
- self.local_concepts[new.address] = built
1522
- return new, built
1523
-
1524
1539
  @build.register
1525
1540
  def _(self, base: None) -> None:
1526
1541
  return base
@@ -1574,7 +1589,7 @@ class Factory:
1574
1589
 
1575
1590
  new = BuildFunction.model_construct(
1576
1591
  operator=base.operator,
1577
- arguments=[self.build(c) for c in raw_args],
1592
+ arguments=[self.handle_constant(self.build(c)) for c in raw_args],
1578
1593
  output_datatype=base.output_datatype,
1579
1594
  output_purpose=base.output_purpose,
1580
1595
  valid_inputs=base.valid_inputs,
@@ -1611,6 +1626,8 @@ class Factory:
1611
1626
 
1612
1627
  @build.register
1613
1628
  def _(self, base: Concept) -> BuildConcept:
1629
+
1630
+ # TODO: if we are using parameters, wrap it in a new model and use that in rendering
1614
1631
  if base.address in self.local_concepts:
1615
1632
  return self.local_concepts[base.address]
1616
1633
  new_lineage, final_grain, _ = base.get_select_grain_and_keys(
@@ -1626,6 +1643,7 @@ class Factory:
1626
1643
  derivation, final_grain, build_lineage
1627
1644
  )
1628
1645
  is_aggregate = Concept.calculate_is_aggregate(build_lineage)
1646
+
1629
1647
  rval = BuildConcept.model_construct(
1630
1648
  name=base.name,
1631
1649
  datatype=base.datatype,
@@ -1646,7 +1664,6 @@ class Factory:
1646
1664
 
1647
1665
  @build.register
1648
1666
  def _(self, base: AggregateWrapper) -> BuildAggregateWrapper:
1649
-
1650
1667
  if not base.by:
1651
1668
  by = [
1652
1669
  self.build(self.environment.concepts[c]) for c in self.grain.components
@@ -1734,8 +1751,8 @@ class Factory:
1734
1751
  @build.register
1735
1752
  def _(self, base: Conditional) -> BuildConditional:
1736
1753
  return BuildConditional.model_construct(
1737
- left=(self.build(base.left)),
1738
- right=(self.build(base.right)),
1754
+ left=self.handle_constant(self.build(base.left)),
1755
+ right=self.handle_constant(self.build(base.right)),
1739
1756
  operator=base.operator,
1740
1757
  )
1741
1758
 
@@ -1763,8 +1780,8 @@ class Factory:
1763
1780
  right_c, _ = self.instantiate_concept(right)
1764
1781
  right = right_c # type: ignore
1765
1782
  return BuildComparison.model_construct(
1766
- left=(self.build(left)),
1767
- right=(self.build(right)),
1783
+ left=self.handle_constant(self.build(left)),
1784
+ right=self.handle_constant(self.build(right)),
1768
1785
  operator=base.operator,
1769
1786
  )
1770
1787
 
@@ -2011,3 +2028,12 @@ class Factory:
2011
2028
  factory.build(base.non_partial_for) if base.non_partial_for else None
2012
2029
  ),
2013
2030
  )
2031
+
2032
+ def handle_constant(self, base):
2033
+ if (
2034
+ isinstance(base, BuildConcept)
2035
+ and isinstance(base.lineage, BuildFunction)
2036
+ and base.lineage.operator == FunctionType.CONSTANT
2037
+ ):
2038
+ return BuildParamaterizedConceptReference(concept=base)
2039
+ return base
@@ -24,6 +24,7 @@ from trilogy.core.models.build import (
24
24
  BuildFunction,
25
25
  BuildGrain,
26
26
  BuildOrderBy,
27
+ BuildParamaterizedConceptReference,
27
28
  BuildParenthetical,
28
29
  BuildRowsetItem,
29
30
  LooseBuildConceptList,
@@ -447,7 +448,7 @@ class CTEConceptPair(ConceptPair):
447
448
 
448
449
 
449
450
  class InstantiatedUnnestJoin(BaseModel):
450
- concept_to_unnest: BuildConcept
451
+ object_to_unnest: BuildConcept | BuildParamaterizedConceptReference | BuildFunction
451
452
  alias: str = "unnest"
452
453
 
453
454
 
@@ -5,7 +5,6 @@ from trilogy.core.models.build import (
5
5
  )
6
6
  from trilogy.core.models.execute import CTE, UnionCTE
7
7
  from trilogy.core.optimizations import (
8
- InlineConstant,
9
8
  InlineDatasource,
10
9
  OptimizationRule,
11
10
  PredicatePushdown,
@@ -206,8 +205,6 @@ def optimize_ctes(
206
205
  REGISTERED_RULES.append(PredicatePushdown())
207
206
  if CONFIG.optimizations.predicate_pushdown:
208
207
  REGISTERED_RULES.append(PredicatePushdownRemove())
209
- if CONFIG.optimizations.constant_inlining:
210
- REGISTERED_RULES.append(InlineConstant())
211
208
  for rule in REGISTERED_RULES:
212
209
  loops = 0
213
210
  complete = False
@@ -1,11 +1,9 @@
1
1
  from .base_optimization import OptimizationRule
2
- from .inline_constant import InlineConstant
3
2
  from .inline_datasource import InlineDatasource
4
3
  from .predicate_pushdown import PredicatePushdown, PredicatePushdownRemove
5
4
 
6
5
  __all__ = [
7
6
  "OptimizationRule",
8
- "InlineConstant",
9
7
  "InlineDatasource",
10
8
  "PredicatePushdown",
11
9
  "PredicatePushdownRemove",
@@ -1,6 +1,7 @@
1
1
  from typing import List
2
2
 
3
3
  from trilogy.constants import logger
4
+ from trilogy.core.internal import ALL_ROWS_CONCEPT
4
5
  from trilogy.core.models.build import (
5
6
  BuildAggregateWrapper,
6
7
  BuildConcept,
@@ -92,7 +93,9 @@ def gen_group_node(
92
93
  logger.info(
93
94
  f"{padding(depth)}{LOGGER_PREFIX} fetching group node parents {LooseBuildConceptList(concepts=parent_concepts)}"
94
95
  )
95
- parent_concepts = unique(parent_concepts, "address")
96
+ parent_concepts = unique(
97
+ [x for x in parent_concepts if not x.name == ALL_ROWS_CONCEPT], "address"
98
+ )
96
99
  parent = source_concepts(
97
100
  mandatory_list=parent_concepts,
98
101
  environment=environment,
@@ -1,6 +1,7 @@
1
1
  from pydantic import BaseModel, ConfigDict, Field
2
2
 
3
3
  from trilogy.core.exceptions import UnresolvableQueryException
4
+ from trilogy.core.models.author import Concept
4
5
  from trilogy.core.models.build import BuildConcept, BuildWhereClause
5
6
  from trilogy.core.models.build_environment import BuildEnvironment
6
7
  from trilogy.core.models.environment import Environment
@@ -17,6 +18,7 @@ from .window_node import WindowNode
17
18
 
18
19
  class History(BaseModel):
19
20
  base_environment: Environment
21
+ local_base_concepts: dict[str, Concept] = Field(default_factory=dict)
20
22
  history: dict[str, StrategyNode | None] = Field(default_factory=dict)
21
23
  select_history: dict[str, StrategyNode | None] = Field(default_factory=dict)
22
24
  started: dict[str, int] = Field(default_factory=dict)