pytrilogy 0.3.138__cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.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 (182) hide show
  1. LICENSE.md +19 -0
  2. _preql_import_resolver/__init__.py +5 -0
  3. _preql_import_resolver/_preql_import_resolver.cpython-311-x86_64-linux-gnu.so +0 -0
  4. pytrilogy-0.3.138.dist-info/METADATA +525 -0
  5. pytrilogy-0.3.138.dist-info/RECORD +182 -0
  6. pytrilogy-0.3.138.dist-info/WHEEL +5 -0
  7. pytrilogy-0.3.138.dist-info/entry_points.txt +2 -0
  8. pytrilogy-0.3.138.dist-info/licenses/LICENSE.md +19 -0
  9. trilogy/__init__.py +9 -0
  10. trilogy/ai/README.md +10 -0
  11. trilogy/ai/__init__.py +19 -0
  12. trilogy/ai/constants.py +92 -0
  13. trilogy/ai/conversation.py +107 -0
  14. trilogy/ai/enums.py +7 -0
  15. trilogy/ai/execute.py +50 -0
  16. trilogy/ai/models.py +34 -0
  17. trilogy/ai/prompts.py +87 -0
  18. trilogy/ai/providers/__init__.py +0 -0
  19. trilogy/ai/providers/anthropic.py +106 -0
  20. trilogy/ai/providers/base.py +24 -0
  21. trilogy/ai/providers/google.py +146 -0
  22. trilogy/ai/providers/openai.py +89 -0
  23. trilogy/ai/providers/utils.py +68 -0
  24. trilogy/authoring/README.md +3 -0
  25. trilogy/authoring/__init__.py +143 -0
  26. trilogy/constants.py +113 -0
  27. trilogy/core/README.md +52 -0
  28. trilogy/core/__init__.py +0 -0
  29. trilogy/core/constants.py +6 -0
  30. trilogy/core/enums.py +443 -0
  31. trilogy/core/env_processor.py +120 -0
  32. trilogy/core/environment_helpers.py +320 -0
  33. trilogy/core/ergonomics.py +193 -0
  34. trilogy/core/exceptions.py +123 -0
  35. trilogy/core/functions.py +1227 -0
  36. trilogy/core/graph_models.py +139 -0
  37. trilogy/core/internal.py +85 -0
  38. trilogy/core/models/__init__.py +0 -0
  39. trilogy/core/models/author.py +2672 -0
  40. trilogy/core/models/build.py +2521 -0
  41. trilogy/core/models/build_environment.py +180 -0
  42. trilogy/core/models/core.py +494 -0
  43. trilogy/core/models/datasource.py +322 -0
  44. trilogy/core/models/environment.py +748 -0
  45. trilogy/core/models/execute.py +1177 -0
  46. trilogy/core/optimization.py +251 -0
  47. trilogy/core/optimizations/__init__.py +12 -0
  48. trilogy/core/optimizations/base_optimization.py +17 -0
  49. trilogy/core/optimizations/hide_unused_concept.py +47 -0
  50. trilogy/core/optimizations/inline_datasource.py +102 -0
  51. trilogy/core/optimizations/predicate_pushdown.py +245 -0
  52. trilogy/core/processing/README.md +94 -0
  53. trilogy/core/processing/READMEv2.md +121 -0
  54. trilogy/core/processing/VIRTUAL_UNNEST.md +30 -0
  55. trilogy/core/processing/__init__.py +0 -0
  56. trilogy/core/processing/concept_strategies_v3.py +508 -0
  57. trilogy/core/processing/constants.py +15 -0
  58. trilogy/core/processing/discovery_node_factory.py +451 -0
  59. trilogy/core/processing/discovery_utility.py +517 -0
  60. trilogy/core/processing/discovery_validation.py +167 -0
  61. trilogy/core/processing/graph_utils.py +43 -0
  62. trilogy/core/processing/node_generators/README.md +9 -0
  63. trilogy/core/processing/node_generators/__init__.py +31 -0
  64. trilogy/core/processing/node_generators/basic_node.py +160 -0
  65. trilogy/core/processing/node_generators/common.py +268 -0
  66. trilogy/core/processing/node_generators/constant_node.py +38 -0
  67. trilogy/core/processing/node_generators/filter_node.py +315 -0
  68. trilogy/core/processing/node_generators/group_node.py +213 -0
  69. trilogy/core/processing/node_generators/group_to_node.py +117 -0
  70. trilogy/core/processing/node_generators/multiselect_node.py +205 -0
  71. trilogy/core/processing/node_generators/node_merge_node.py +653 -0
  72. trilogy/core/processing/node_generators/recursive_node.py +88 -0
  73. trilogy/core/processing/node_generators/rowset_node.py +165 -0
  74. trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  75. trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +261 -0
  76. trilogy/core/processing/node_generators/select_merge_node.py +748 -0
  77. trilogy/core/processing/node_generators/select_node.py +95 -0
  78. trilogy/core/processing/node_generators/synonym_node.py +98 -0
  79. trilogy/core/processing/node_generators/union_node.py +91 -0
  80. trilogy/core/processing/node_generators/unnest_node.py +182 -0
  81. trilogy/core/processing/node_generators/window_node.py +201 -0
  82. trilogy/core/processing/nodes/README.md +28 -0
  83. trilogy/core/processing/nodes/__init__.py +179 -0
  84. trilogy/core/processing/nodes/base_node.py +519 -0
  85. trilogy/core/processing/nodes/filter_node.py +75 -0
  86. trilogy/core/processing/nodes/group_node.py +194 -0
  87. trilogy/core/processing/nodes/merge_node.py +420 -0
  88. trilogy/core/processing/nodes/recursive_node.py +46 -0
  89. trilogy/core/processing/nodes/select_node_v2.py +242 -0
  90. trilogy/core/processing/nodes/union_node.py +53 -0
  91. trilogy/core/processing/nodes/unnest_node.py +62 -0
  92. trilogy/core/processing/nodes/window_node.py +56 -0
  93. trilogy/core/processing/utility.py +823 -0
  94. trilogy/core/query_processor.py +596 -0
  95. trilogy/core/statements/README.md +35 -0
  96. trilogy/core/statements/__init__.py +0 -0
  97. trilogy/core/statements/author.py +536 -0
  98. trilogy/core/statements/build.py +0 -0
  99. trilogy/core/statements/common.py +20 -0
  100. trilogy/core/statements/execute.py +155 -0
  101. trilogy/core/table_processor.py +66 -0
  102. trilogy/core/utility.py +8 -0
  103. trilogy/core/validation/README.md +46 -0
  104. trilogy/core/validation/__init__.py +0 -0
  105. trilogy/core/validation/common.py +161 -0
  106. trilogy/core/validation/concept.py +146 -0
  107. trilogy/core/validation/datasource.py +227 -0
  108. trilogy/core/validation/environment.py +73 -0
  109. trilogy/core/validation/fix.py +106 -0
  110. trilogy/dialect/__init__.py +32 -0
  111. trilogy/dialect/base.py +1359 -0
  112. trilogy/dialect/bigquery.py +256 -0
  113. trilogy/dialect/common.py +147 -0
  114. trilogy/dialect/config.py +144 -0
  115. trilogy/dialect/dataframe.py +50 -0
  116. trilogy/dialect/duckdb.py +177 -0
  117. trilogy/dialect/enums.py +147 -0
  118. trilogy/dialect/metadata.py +173 -0
  119. trilogy/dialect/mock.py +190 -0
  120. trilogy/dialect/postgres.py +91 -0
  121. trilogy/dialect/presto.py +104 -0
  122. trilogy/dialect/results.py +89 -0
  123. trilogy/dialect/snowflake.py +90 -0
  124. trilogy/dialect/sql_server.py +92 -0
  125. trilogy/engine.py +48 -0
  126. trilogy/execution/config.py +75 -0
  127. trilogy/executor.py +568 -0
  128. trilogy/hooks/__init__.py +4 -0
  129. trilogy/hooks/base_hook.py +40 -0
  130. trilogy/hooks/graph_hook.py +139 -0
  131. trilogy/hooks/query_debugger.py +166 -0
  132. trilogy/metadata/__init__.py +0 -0
  133. trilogy/parser.py +10 -0
  134. trilogy/parsing/README.md +21 -0
  135. trilogy/parsing/__init__.py +0 -0
  136. trilogy/parsing/common.py +1069 -0
  137. trilogy/parsing/config.py +5 -0
  138. trilogy/parsing/exceptions.py +8 -0
  139. trilogy/parsing/helpers.py +1 -0
  140. trilogy/parsing/parse_engine.py +2813 -0
  141. trilogy/parsing/render.py +750 -0
  142. trilogy/parsing/trilogy.lark +540 -0
  143. trilogy/py.typed +0 -0
  144. trilogy/render.py +42 -0
  145. trilogy/scripts/README.md +7 -0
  146. trilogy/scripts/__init__.py +0 -0
  147. trilogy/scripts/dependency/Cargo.lock +617 -0
  148. trilogy/scripts/dependency/Cargo.toml +39 -0
  149. trilogy/scripts/dependency/README.md +131 -0
  150. trilogy/scripts/dependency/build.sh +25 -0
  151. trilogy/scripts/dependency/src/directory_resolver.rs +162 -0
  152. trilogy/scripts/dependency/src/lib.rs +16 -0
  153. trilogy/scripts/dependency/src/main.rs +770 -0
  154. trilogy/scripts/dependency/src/parser.rs +435 -0
  155. trilogy/scripts/dependency/src/preql.pest +208 -0
  156. trilogy/scripts/dependency/src/python_bindings.rs +289 -0
  157. trilogy/scripts/dependency/src/resolver.rs +716 -0
  158. trilogy/scripts/dependency/tests/base.preql +3 -0
  159. trilogy/scripts/dependency/tests/cli_integration.rs +377 -0
  160. trilogy/scripts/dependency/tests/customer.preql +6 -0
  161. trilogy/scripts/dependency/tests/main.preql +9 -0
  162. trilogy/scripts/dependency/tests/orders.preql +7 -0
  163. trilogy/scripts/dependency/tests/test_data/base.preql +9 -0
  164. trilogy/scripts/dependency/tests/test_data/consumer.preql +1 -0
  165. trilogy/scripts/dependency.py +323 -0
  166. trilogy/scripts/display.py +460 -0
  167. trilogy/scripts/environment.py +46 -0
  168. trilogy/scripts/parallel_execution.py +483 -0
  169. trilogy/scripts/single_execution.py +131 -0
  170. trilogy/scripts/trilogy.py +772 -0
  171. trilogy/std/__init__.py +0 -0
  172. trilogy/std/color.preql +3 -0
  173. trilogy/std/date.preql +13 -0
  174. trilogy/std/display.preql +18 -0
  175. trilogy/std/geography.preql +22 -0
  176. trilogy/std/metric.preql +15 -0
  177. trilogy/std/money.preql +67 -0
  178. trilogy/std/net.preql +14 -0
  179. trilogy/std/ranking.preql +7 -0
  180. trilogy/std/report.preql +5 -0
  181. trilogy/std/semantic.preql +6 -0
  182. trilogy/utility.py +34 -0
@@ -0,0 +1,3 @@
1
+ // Shared base definitions - no imports
2
+ const MAX_RECORDS <- 1000;
3
+ const DEFAULT_LIMIT <- 100;
@@ -0,0 +1,377 @@
1
+ use std::fs;
2
+ use std::path::{Path, PathBuf};
3
+ use std::process::Command;
4
+ use tempfile::TempDir;
5
+
6
+ fn get_binary_path() -> PathBuf {
7
+ // Find the compiled binary in either debug or release
8
+ let manifest_dir = env!("CARGO_MANIFEST_DIR");
9
+ let debug_path = Path::new(manifest_dir).join("target/debug/preql-import-resolver");
10
+ let release_path = Path::new(manifest_dir).join("target/release/preql-import-resolver");
11
+
12
+ #[cfg(target_os = "windows")]
13
+ {
14
+ let debug_exe = debug_path.with_extension("exe");
15
+ let release_exe = release_path.with_extension("exe");
16
+ if release_exe.exists() {
17
+ return release_exe;
18
+ }
19
+ debug_exe
20
+ }
21
+
22
+ #[cfg(not(target_os = "windows"))]
23
+ {
24
+ if release_path.exists() {
25
+ return release_path;
26
+ }
27
+ debug_path
28
+ }
29
+ }
30
+
31
+ fn create_test_file(dir: &Path, name: &str, content: &str) -> PathBuf {
32
+ let path = dir.join(name);
33
+ if let Some(parent) = path.parent() {
34
+ fs::create_dir_all(parent).unwrap();
35
+ }
36
+ fs::write(&path, content).unwrap();
37
+ path
38
+ }
39
+
40
+ #[test]
41
+ fn test_parse_simple_file() {
42
+ let temp = TempDir::new().unwrap();
43
+ let root = temp.path();
44
+
45
+ let test_file = create_test_file(
46
+ root,
47
+ "test.preql",
48
+ "import models.customer;\nimport models.orders as ord;",
49
+ );
50
+
51
+ let output = Command::new(get_binary_path())
52
+ .arg("parse")
53
+ .arg(&test_file)
54
+ .arg("--format")
55
+ .arg("json")
56
+ .output()
57
+ .expect("Failed to execute command");
58
+
59
+ assert!(output.status.success(), "Command failed: {:?}", output);
60
+
61
+ let stdout = String::from_utf8_lossy(&output.stdout);
62
+ assert!(stdout.contains("models.customer"));
63
+ assert!(stdout.contains("models.orders"));
64
+ assert!(stdout.contains("\"alias\":\"ord\""));
65
+ }
66
+
67
+ #[test]
68
+ fn test_parse_with_datasource() {
69
+ let temp = TempDir::new().unwrap();
70
+ let root = temp.path();
71
+
72
+ let test_file = create_test_file(
73
+ root,
74
+ "test.preql",
75
+ r#"
76
+ datasource orders (
77
+ id: key,
78
+ amount: metric
79
+ )
80
+ address db.orders;
81
+ "#,
82
+ );
83
+
84
+ let output = Command::new(get_binary_path())
85
+ .arg("parse")
86
+ .arg(&test_file)
87
+ .arg("--format")
88
+ .arg("json")
89
+ .output()
90
+ .expect("Failed to execute command");
91
+
92
+ assert!(output.status.success(), "Command failed: {:?}", output);
93
+
94
+ let stdout = String::from_utf8_lossy(&output.stdout);
95
+ assert!(stdout.contains("\"datasources\""));
96
+ assert!(stdout.contains("\"name\":\"orders\""));
97
+ }
98
+
99
+ #[test]
100
+ fn test_parse_with_persist() {
101
+ let temp = TempDir::new().unwrap();
102
+ let root = temp.path();
103
+
104
+ let test_file = create_test_file(
105
+ root,
106
+ "test.preql",
107
+ "persist orders;\nappend customers where status = 'active';",
108
+ );
109
+
110
+ let output = Command::new(get_binary_path())
111
+ .arg("parse")
112
+ .arg(&test_file)
113
+ .arg("--format")
114
+ .arg("json")
115
+ .output()
116
+ .expect("Failed to execute command");
117
+
118
+ assert!(output.status.success(), "Command failed: {:?}", output);
119
+
120
+ let stdout = String::from_utf8_lossy(&output.stdout);
121
+ assert!(stdout.contains("\"persists\""));
122
+ assert!(stdout.contains("\"target_datasource\":\"orders\""));
123
+ assert!(stdout.contains("\"target_datasource\":\"customers\""));
124
+ assert!(stdout.contains("\"mode\":\"persist\""));
125
+ assert!(stdout.contains("\"mode\":\"append\""));
126
+ }
127
+
128
+ #[test]
129
+ fn test_parse_directory() {
130
+ let temp = TempDir::new().unwrap();
131
+ let root = temp.path();
132
+
133
+ create_test_file(root, "a.preql", "import b;");
134
+ create_test_file(root, "b.preql", "// no imports");
135
+ create_test_file(root, "c.preql", "import a;");
136
+
137
+ let output = Command::new(get_binary_path())
138
+ .arg("parse")
139
+ .arg(root)
140
+ .arg("--format")
141
+ .arg("json")
142
+ .output()
143
+ .expect("Failed to execute command");
144
+
145
+ assert!(output.status.success(), "Command failed: {:?}", output);
146
+
147
+ let stdout = String::from_utf8_lossy(&output.stdout);
148
+ assert!(stdout.contains("a.preql"));
149
+ assert!(stdout.contains("b.preql"));
150
+ assert!(stdout.contains("c.preql"));
151
+ }
152
+
153
+ #[test]
154
+ fn test_resolve_simple_dependency() {
155
+ let temp = TempDir::new().unwrap();
156
+ let root = temp.path();
157
+
158
+ create_test_file(root, "a.preql", "import b;");
159
+ create_test_file(root, "b.preql", "// no imports");
160
+
161
+ let a_path = root.join("a.preql");
162
+
163
+ let output = Command::new(get_binary_path())
164
+ .arg("resolve")
165
+ .arg(&a_path)
166
+ .arg("--format")
167
+ .arg("json")
168
+ .output()
169
+ .expect("Failed to execute command");
170
+
171
+ assert!(output.status.success(), "Command failed: {:?}", output);
172
+
173
+ let stdout = String::from_utf8_lossy(&output.stdout);
174
+ assert!(stdout.contains("\"order\""));
175
+ assert!(stdout.contains("b.preql"));
176
+ assert!(stdout.contains("a.preql"));
177
+ }
178
+
179
+ #[test]
180
+ fn test_resolve_with_datasources() {
181
+ let temp = TempDir::new().unwrap();
182
+ let root = temp.path();
183
+
184
+ create_test_file(
185
+ root,
186
+ "base.preql",
187
+ r#"
188
+ datasource orders (
189
+ id,
190
+ amount
191
+ )
192
+ address db.orders;
193
+ "#,
194
+ );
195
+ create_test_file(root, "updater.preql", "persist orders;");
196
+ create_test_file(root, "consumer.preql", "import base;");
197
+ create_test_file(root, "main.preql", "import updater;\nimport consumer;");
198
+
199
+ let main_path = root.join("main.preql");
200
+
201
+ let output = Command::new(get_binary_path())
202
+ .arg("resolve")
203
+ .arg(&main_path)
204
+ .arg("--format")
205
+ .arg("json")
206
+ .output()
207
+ .expect("Failed to execute command");
208
+
209
+ assert!(output.status.success(), "Command failed: {:?}", output);
210
+
211
+ let stdout = String::from_utf8_lossy(&output.stdout);
212
+ assert!(stdout.contains("\"datasource_declarations\""));
213
+ assert!(stdout.contains("\"datasource_updaters\""));
214
+ }
215
+
216
+ #[test]
217
+ fn test_datasources_command() {
218
+ let temp = TempDir::new().unwrap();
219
+ let root = temp.path();
220
+
221
+ create_test_file(
222
+ root,
223
+ "models.preql",
224
+ r#"
225
+ datasource customers (
226
+ id: key
227
+ )
228
+ address db.customers;
229
+
230
+ datasource orders (
231
+ id: key,
232
+ customer_id
233
+ )
234
+ address db.orders;
235
+ "#,
236
+ );
237
+ create_test_file(root, "updater.preql", "persist orders;");
238
+
239
+ let output = Command::new(get_binary_path())
240
+ .arg("datasources")
241
+ .arg(root)
242
+ .arg("--format")
243
+ .arg("json")
244
+ .output()
245
+ .expect("Failed to execute command");
246
+
247
+ assert!(output.status.success(), "Command failed: {:?}", output);
248
+
249
+ let stdout = String::from_utf8_lossy(&output.stdout);
250
+ assert!(stdout.contains("\"declarations\""));
251
+ assert!(stdout.contains("\"updaters\""));
252
+ assert!(stdout.contains("customers"));
253
+ assert!(stdout.contains("orders"));
254
+ }
255
+
256
+ #[test]
257
+ fn test_pretty_format() {
258
+ let temp = TempDir::new().unwrap();
259
+ let root = temp.path();
260
+
261
+ let test_file = create_test_file(root, "test.preql", "import models.customer;");
262
+
263
+ let output = Command::new(get_binary_path())
264
+ .arg("parse")
265
+ .arg(&test_file)
266
+ .arg("--format")
267
+ .arg("pretty")
268
+ .output()
269
+ .expect("Failed to execute command");
270
+
271
+ assert!(output.status.success(), "Command failed: {:?}", output);
272
+
273
+ let stdout = String::from_utf8_lossy(&output.stdout);
274
+ // Pretty format should have indentation
275
+ assert!(stdout.contains(" ") || stdout.contains("\n"));
276
+ }
277
+
278
+ #[test]
279
+ fn test_direct_only_flag() {
280
+ let temp = TempDir::new().unwrap();
281
+ let root = temp.path();
282
+
283
+ create_test_file(root, "a.preql", "import b;");
284
+ create_test_file(root, "b.preql", "import c;");
285
+ create_test_file(root, "c.preql", "// no imports");
286
+
287
+ let a_path = root.join("a.preql");
288
+
289
+ let output = Command::new(get_binary_path())
290
+ .arg("parse")
291
+ .arg(&a_path)
292
+ .arg("--direct-only")
293
+ .arg("--format")
294
+ .arg("json")
295
+ .output()
296
+ .expect("Failed to execute command");
297
+
298
+ assert!(output.status.success(), "Command failed: {:?}", output);
299
+
300
+ let stdout = String::from_utf8_lossy(&output.stdout);
301
+ // With --direct-only, should not have resolved_dependencies
302
+ assert!(!stdout.contains("\"resolved_dependencies\"") || stdout.contains("\"resolved_dependencies\":null"));
303
+ }
304
+
305
+ #[test]
306
+ fn test_invalid_file() {
307
+ let temp = TempDir::new().unwrap();
308
+ let root = temp.path();
309
+ let nonexistent = root.join("nonexistent.preql");
310
+
311
+ let output = Command::new(get_binary_path())
312
+ .arg("parse")
313
+ .arg(&nonexistent)
314
+ .arg("--format")
315
+ .arg("json")
316
+ .output()
317
+ .expect("Failed to execute command");
318
+
319
+ assert!(!output.status.success(), "Command should fail for nonexistent file");
320
+
321
+ let stderr = String::from_utf8_lossy(&output.stderr);
322
+ assert!(stderr.contains("error") || stderr.contains("Error"));
323
+ }
324
+
325
+ #[test]
326
+ fn test_recursive_directory_parse() {
327
+ let temp = TempDir::new().unwrap();
328
+ let root = temp.path();
329
+
330
+ // Create nested structure
331
+ create_test_file(root, "top.preql", "import nested.a;");
332
+ create_test_file(root, "nested/a.preql", "import b;");
333
+ create_test_file(root, "nested/b.preql", "// no imports");
334
+
335
+ let output = Command::new(get_binary_path())
336
+ .arg("parse")
337
+ .arg(root)
338
+ .arg("--recursive")
339
+ .arg("--format")
340
+ .arg("json")
341
+ .output()
342
+ .expect("Failed to execute command");
343
+
344
+ assert!(output.status.success(), "Command failed: {:?}", output);
345
+
346
+ let stdout = String::from_utf8_lossy(&output.stdout);
347
+ assert!(stdout.contains("top.preql"));
348
+ assert!(stdout.contains("a.preql"));
349
+ assert!(stdout.contains("b.preql"));
350
+ }
351
+
352
+ #[test]
353
+ fn test_order_only_flag() {
354
+ let temp = TempDir::new().unwrap();
355
+ let root = temp.path();
356
+
357
+ create_test_file(root, "a.preql", "import b;");
358
+ create_test_file(root, "b.preql", "// no imports");
359
+
360
+ let a_path = root.join("a.preql");
361
+
362
+ let output = Command::new(get_binary_path())
363
+ .arg("resolve")
364
+ .arg(&a_path)
365
+ .arg("--order-only")
366
+ .arg("--format")
367
+ .arg("json")
368
+ .output()
369
+ .expect("Failed to execute command");
370
+
371
+ assert!(output.status.success(), "Command failed: {:?}", output);
372
+
373
+ let stdout = String::from_utf8_lossy(&output.stdout);
374
+ // With --order-only, should just be an array of paths
375
+ assert!(stdout.starts_with("["));
376
+ assert!(stdout.ends_with("]\n") || stdout.ends_with("]"));
377
+ }
@@ -0,0 +1,6 @@
1
+ // Customer model
2
+ import ..shared.base;
3
+ import std.display; // stdlib import - will be skipped
4
+
5
+ key customer_id int;
6
+ property customer_name string;
@@ -0,0 +1,9 @@
1
+ // Main entry point
2
+ import models.customer as cust;
3
+ import models.orders;
4
+
5
+ select
6
+ customer_id,
7
+ customer_name,
8
+ order_count
9
+ ;
@@ -0,0 +1,7 @@
1
+ // Orders model
2
+ import ..shared.base;
3
+ import customer; // relative import in same directory
4
+
5
+ key order_id int;
6
+ property order_date date;
7
+ metric order_count <- count(order_id);
@@ -0,0 +1,9 @@
1
+ key order_id int;
2
+ property order_id.amount float;
3
+
4
+ datasource orders (
5
+ order_id,
6
+ amount
7
+ )
8
+ grain (order_id)
9
+ address db.orders;
@@ -0,0 +1 @@
1
+ import base;