@malloydata/malloy 0.0.22-dev230207144036 → 0.0.22-dev230207180844

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 (359) hide show
  1. package/dist/connection_utils.d.ts +1 -0
  2. package/dist/connection_utils.js +66 -0
  3. package/dist/dialect/dialect.d.ts +66 -0
  4. package/dist/dialect/dialect.js +85 -0
  5. package/dist/dialect/dialect_map.d.ts +3 -0
  6. package/dist/dialect/dialect_map.js +45 -0
  7. package/dist/dialect/duckdb.d.ts +52 -0
  8. package/dist/dialect/duckdb.js +364 -0
  9. package/dist/dialect/index.d.ts +6 -0
  10. package/dist/dialect/index.js +37 -0
  11. package/dist/dialect/postgres.d.ts +52 -0
  12. package/dist/dialect/postgres.js +330 -0
  13. package/dist/dialect/standardsql.d.ts +49 -0
  14. package/dist/dialect/standardsql.js +381 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.js +59 -0
  17. package/dist/lang/ast/ast-utils.d.ts +10 -0
  18. package/dist/lang/ast/ast-utils.js +42 -0
  19. package/dist/lang/ast/elements/define-query.d.ts +14 -0
  20. package/dist/lang/ast/elements/define-query.js +56 -0
  21. package/dist/lang/ast/elements/define-source.d.ts +17 -0
  22. package/dist/lang/ast/elements/define-source.js +79 -0
  23. package/dist/lang/ast/elements/import-statement.d.ts +9 -0
  24. package/dist/lang/ast/elements/import-statement.js +78 -0
  25. package/dist/lang/ast/elements/pipeline-desc.d.ts +30 -0
  26. package/dist/lang/ast/elements/pipeline-desc.js +102 -0
  27. package/dist/lang/ast/elements/refined-source.d.ts +15 -0
  28. package/dist/lang/ast/elements/refined-source.js +121 -0
  29. package/dist/lang/ast/elements/source.d.ts +12 -0
  30. package/dist/lang/ast/elements/source.js +53 -0
  31. package/dist/lang/ast/error-factory.d.ts +9 -0
  32. package/dist/lang/ast/error-factory.js +61 -0
  33. package/dist/lang/ast/executors/index-executor.d.ts +18 -0
  34. package/dist/lang/ast/executors/index-executor.js +96 -0
  35. package/dist/lang/ast/executors/project-executor.d.ts +10 -0
  36. package/dist/lang/ast/executors/project-executor.js +68 -0
  37. package/dist/lang/ast/executors/reduce-executor.d.ts +21 -0
  38. package/dist/lang/ast/executors/reduce-executor.js +149 -0
  39. package/dist/lang/ast/expressions/apply.d.ts +8 -0
  40. package/dist/lang/ast/expressions/apply.js +37 -0
  41. package/dist/lang/ast/expressions/binary-boolean.d.ts +12 -0
  42. package/dist/lang/ast/expressions/binary-boolean.js +54 -0
  43. package/dist/lang/ast/expressions/binary-numeric.d.ts +11 -0
  44. package/dist/lang/ast/expressions/binary-numeric.js +42 -0
  45. package/dist/lang/ast/expressions/boolean.d.ts +8 -0
  46. package/dist/lang/ast/expressions/boolean.js +39 -0
  47. package/dist/lang/ast/expressions/constant-sub-expression.d.ts +16 -0
  48. package/dist/lang/ast/expressions/constant-sub-expression.js +98 -0
  49. package/dist/lang/ast/expressions/expr-add-sub.d.ts +4 -0
  50. package/dist/lang/ast/expressions/expr-add-sub.js +34 -0
  51. package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +15 -0
  52. package/dist/lang/ast/expressions/expr-aggregate-function.js +99 -0
  53. package/dist/lang/ast/expressions/expr-alternation-tree.d.ts +11 -0
  54. package/dist/lang/ast/expressions/expr-alternation-tree.js +54 -0
  55. package/dist/lang/ast/expressions/expr-asymmetric.d.ts +10 -0
  56. package/dist/lang/ast/expressions/expr-asymmetric.js +27 -0
  57. package/dist/lang/ast/expressions/expr-avg.d.ts +6 -0
  58. package/dist/lang/ast/expressions/expr-avg.js +34 -0
  59. package/dist/lang/ast/expressions/expr-cast.d.ts +13 -0
  60. package/dist/lang/ast/expressions/expr-cast.js +29 -0
  61. package/dist/lang/ast/expressions/expr-compare.d.ts +10 -0
  62. package/dist/lang/ast/expressions/expr-compare.js +58 -0
  63. package/dist/lang/ast/expressions/expr-count-distinct.d.ts +6 -0
  64. package/dist/lang/ast/expressions/expr-count-distinct.js +35 -0
  65. package/dist/lang/ast/expressions/expr-count.d.ts +11 -0
  66. package/dist/lang/ast/expressions/expr-count.js +57 -0
  67. package/dist/lang/ast/expressions/expr-filter.d.ts +12 -0
  68. package/dist/lang/ast/expressions/expr-filter.js +69 -0
  69. package/dist/lang/ast/expressions/expr-func.d.ts +10 -0
  70. package/dist/lang/ast/expressions/expr-func.js +63 -0
  71. package/dist/lang/ast/expressions/expr-granular-time.d.ts +24 -0
  72. package/dist/lang/ast/expressions/expr-granular-time.js +117 -0
  73. package/dist/lang/ast/expressions/expr-id-reference.d.ts +12 -0
  74. package/dist/lang/ast/expressions/expr-id-reference.js +75 -0
  75. package/dist/lang/ast/expressions/expr-logical-op.d.ts +9 -0
  76. package/dist/lang/ast/expressions/expr-logical-op.js +36 -0
  77. package/dist/lang/ast/expressions/expr-max.d.ts +9 -0
  78. package/dist/lang/ast/expressions/expr-max.js +16 -0
  79. package/dist/lang/ast/expressions/expr-min.d.ts +9 -0
  80. package/dist/lang/ast/expressions/expr-min.js +16 -0
  81. package/dist/lang/ast/expressions/expr-minus.d.ts +9 -0
  82. package/dist/lang/ast/expressions/expr-minus.js +48 -0
  83. package/dist/lang/ast/expressions/expr-mul-div.d.ts +4 -0
  84. package/dist/lang/ast/expressions/expr-mul-div.js +34 -0
  85. package/dist/lang/ast/expressions/expr-not.d.ts +10 -0
  86. package/dist/lang/ast/expressions/expr-not.js +49 -0
  87. package/dist/lang/ast/expressions/expr-now.d.ts +7 -0
  88. package/dist/lang/ast/expressions/expr-now.js +46 -0
  89. package/dist/lang/ast/expressions/expr-null.d.ts +6 -0
  90. package/dist/lang/ast/expressions/expr-null.js +41 -0
  91. package/dist/lang/ast/expressions/expr-number.d.ts +10 -0
  92. package/dist/lang/ast/expressions/expr-number.js +42 -0
  93. package/dist/lang/ast/expressions/expr-parens.d.ts +10 -0
  94. package/dist/lang/ast/expressions/expr-parens.js +42 -0
  95. package/dist/lang/ast/expressions/expr-regex.d.ts +8 -0
  96. package/dist/lang/ast/expressions/expr-regex.js +42 -0
  97. package/dist/lang/ast/expressions/expr-string.d.ts +9 -0
  98. package/dist/lang/ast/expressions/expr-string.js +50 -0
  99. package/dist/lang/ast/expressions/expr-sum.d.ts +6 -0
  100. package/dist/lang/ast/expressions/expr-sum.js +12 -0
  101. package/dist/lang/ast/expressions/expr-time-extract.d.ts +13 -0
  102. package/dist/lang/ast/expressions/expr-time-extract.js +117 -0
  103. package/dist/lang/ast/expressions/expr-time.d.ts +11 -0
  104. package/dist/lang/ast/expressions/expr-time.js +62 -0
  105. package/dist/lang/ast/expressions/expr-ungroup.d.ts +14 -0
  106. package/dist/lang/ast/expressions/expr-ungroup.js +83 -0
  107. package/dist/lang/ast/expressions/for-range.d.ts +19 -0
  108. package/dist/lang/ast/expressions/for-range.js +101 -0
  109. package/dist/lang/ast/expressions/granular-literal.d.ts +20 -0
  110. package/dist/lang/ast/expressions/granular-literal.js +148 -0
  111. package/dist/lang/ast/expressions/partial-compare.d.ts +14 -0
  112. package/dist/lang/ast/expressions/partial-compare.js +50 -0
  113. package/dist/lang/ast/expressions/pick-when.d.ts +19 -0
  114. package/dist/lang/ast/expressions/pick-when.js +156 -0
  115. package/dist/lang/ast/expressions/range.d.ts +12 -0
  116. package/dist/lang/ast/expressions/range.js +80 -0
  117. package/dist/lang/ast/expressions/time-frame.d.ts +7 -0
  118. package/dist/lang/ast/expressions/time-frame.js +40 -0
  119. package/dist/lang/ast/expressions/top-by.d.ts +10 -0
  120. package/dist/lang/ast/expressions/top-by.js +51 -0
  121. package/dist/lang/ast/expressions/unary.d.ts +5 -0
  122. package/dist/lang/ast/expressions/unary.js +34 -0
  123. package/dist/lang/ast/expressions/utils.d.ts +12 -0
  124. package/dist/lang/ast/expressions/utils.js +87 -0
  125. package/dist/lang/ast/field-space/column-space-field.d.ts +9 -0
  126. package/dist/lang/ast/field-space/column-space-field.js +40 -0
  127. package/dist/lang/ast/field-space/dynamic-space.d.ts +22 -0
  128. package/dist/lang/ast/field-space/dynamic-space.js +140 -0
  129. package/dist/lang/ast/field-space/field-definition-value.d.ts +16 -0
  130. package/dist/lang/ast/field-space/field-definition-value.js +55 -0
  131. package/dist/lang/ast/field-space/index-field-space.d.ts +10 -0
  132. package/dist/lang/ast/field-space/index-field-space.js +57 -0
  133. package/dist/lang/ast/field-space/join-space-field.d.ts +8 -0
  134. package/dist/lang/ast/field-space/join-space-field.js +35 -0
  135. package/dist/lang/ast/field-space/project-field-space.d.ts +6 -0
  136. package/dist/lang/ast/field-space/project-field-space.js +51 -0
  137. package/dist/lang/ast/field-space/query-field-struct.d.ts +10 -0
  138. package/dist/lang/ast/field-space/query-field-struct.js +46 -0
  139. package/dist/lang/ast/field-space/query-space-field.d.ts +11 -0
  140. package/dist/lang/ast/field-space/query-space-field.js +37 -0
  141. package/dist/lang/ast/field-space/query-spaces.d.ts +50 -0
  142. package/dist/lang/ast/field-space/query-spaces.js +233 -0
  143. package/dist/lang/ast/field-space/reference-field.d.ts +11 -0
  144. package/dist/lang/ast/field-space/reference-field.js +45 -0
  145. package/dist/lang/ast/field-space/refined-space.d.ts +13 -0
  146. package/dist/lang/ast/field-space/refined-space.js +100 -0
  147. package/dist/lang/ast/field-space/rename-space-field.d.ts +11 -0
  148. package/dist/lang/ast/field-space/rename-space-field.js +50 -0
  149. package/dist/lang/ast/field-space/static-space.d.ts +29 -0
  150. package/dist/lang/ast/field-space/static-space.js +156 -0
  151. package/dist/lang/ast/field-space/struct-space-field-base.d.ts +11 -0
  152. package/dist/lang/ast/field-space/struct-space-field-base.js +40 -0
  153. package/dist/lang/ast/field-space/wild-space-field.d.ts +10 -0
  154. package/dist/lang/ast/field-space/wild-space-field.js +40 -0
  155. package/dist/lang/ast/fragtype-utils.d.ts +39 -0
  156. package/dist/lang/ast/fragtype-utils.js +100 -0
  157. package/dist/lang/ast/index.d.ts +90 -0
  158. package/dist/lang/ast/index.js +129 -0
  159. package/dist/lang/ast/parameters/constant-parameter.d.ts +8 -0
  160. package/dist/lang/ast/parameters/constant-parameter.js +54 -0
  161. package/dist/lang/ast/parameters/has-parameter.d.ts +19 -0
  162. package/dist/lang/ast/parameters/has-parameter.js +64 -0
  163. package/dist/lang/ast/query-elements/anonymous-query.d.ts +9 -0
  164. package/dist/lang/ast/query-elements/anonymous-query.js +41 -0
  165. package/dist/lang/ast/query-elements/existing-query.d.ts +11 -0
  166. package/dist/lang/ast/query-elements/existing-query.js +79 -0
  167. package/dist/lang/ast/query-elements/full-query.d.ts +10 -0
  168. package/dist/lang/ast/query-elements/full-query.js +85 -0
  169. package/dist/lang/ast/query-elements/query-head-struct.d.ts +9 -0
  170. package/dist/lang/ast/query-elements/query-head-struct.js +48 -0
  171. package/dist/lang/ast/query-items/field-declaration.d.ts +31 -0
  172. package/dist/lang/ast/query-items/field-declaration.js +137 -0
  173. package/dist/lang/ast/query-items/field-references.d.ts +25 -0
  174. package/dist/lang/ast/query-items/field-references.js +80 -0
  175. package/dist/lang/ast/query-properties/aggregate.d.ts +5 -0
  176. package/dist/lang/ast/query-properties/aggregate.js +39 -0
  177. package/dist/lang/ast/query-properties/declare-fields.d.ts +5 -0
  178. package/dist/lang/ast/query-properties/declare-fields.js +33 -0
  179. package/dist/lang/ast/query-properties/dimmensions.d.ts +5 -0
  180. package/dist/lang/ast/query-properties/dimmensions.js +36 -0
  181. package/dist/lang/ast/query-properties/filters.d.ts +18 -0
  182. package/dist/lang/ast/query-properties/filters.js +89 -0
  183. package/dist/lang/ast/query-properties/group-by.d.ts +5 -0
  184. package/dist/lang/ast/query-properties/group-by.js +39 -0
  185. package/dist/lang/ast/query-properties/indexing.d.ts +10 -0
  186. package/dist/lang/ast/query-properties/indexing.js +39 -0
  187. package/dist/lang/ast/query-properties/joins.d.ts +36 -0
  188. package/dist/lang/ast/query-properties/joins.js +143 -0
  189. package/dist/lang/ast/query-properties/limit.d.ts +6 -0
  190. package/dist/lang/ast/query-properties/limit.js +35 -0
  191. package/dist/lang/ast/query-properties/measures.d.ts +5 -0
  192. package/dist/lang/ast/query-properties/measures.js +36 -0
  193. package/dist/lang/ast/query-properties/nest-reference.d.ts +7 -0
  194. package/dist/lang/ast/query-properties/nest-reference.js +35 -0
  195. package/dist/lang/ast/query-properties/nest.d.ts +31 -0
  196. package/dist/lang/ast/query-properties/nest.js +133 -0
  197. package/dist/lang/ast/query-properties/nests.d.ts +5 -0
  198. package/dist/lang/ast/query-properties/nests.js +33 -0
  199. package/dist/lang/ast/query-properties/ordering.d.ts +15 -0
  200. package/dist/lang/ast/query-properties/ordering.js +66 -0
  201. package/dist/lang/ast/query-properties/project-statement.d.ts +5 -0
  202. package/dist/lang/ast/query-properties/project-statement.js +33 -0
  203. package/dist/lang/ast/query-properties/qop-desc.d.ts +17 -0
  204. package/dist/lang/ast/query-properties/qop-desc.js +124 -0
  205. package/dist/lang/ast/query-properties/sampling.d.ts +8 -0
  206. package/dist/lang/ast/query-properties/sampling.js +38 -0
  207. package/dist/lang/ast/query-properties/top.d.ts +13 -0
  208. package/dist/lang/ast/query-properties/top.js +60 -0
  209. package/dist/lang/ast/source-properties/field-list-edit.d.ts +8 -0
  210. package/dist/lang/ast/source-properties/field-list-edit.js +36 -0
  211. package/dist/lang/ast/source-properties/primary-key.d.ts +7 -0
  212. package/dist/lang/ast/source-properties/primary-key.js +35 -0
  213. package/dist/lang/ast/source-properties/renames.d.ts +11 -0
  214. package/dist/lang/ast/source-properties/renames.js +43 -0
  215. package/dist/lang/ast/source-properties/turtles.d.ts +5 -0
  216. package/dist/lang/ast/source-properties/turtles.js +33 -0
  217. package/dist/lang/ast/sources/named-source.d.ts +19 -0
  218. package/dist/lang/ast/sources/named-source.js +147 -0
  219. package/dist/lang/ast/sources/query-source.d.ts +9 -0
  220. package/dist/lang/ast/sources/query-source.js +41 -0
  221. package/dist/lang/ast/sources/sql-source.d.ts +7 -0
  222. package/dist/lang/ast/sources/sql-source.js +54 -0
  223. package/dist/lang/ast/sources/table-source.d.ts +8 -0
  224. package/dist/lang/ast/sources/table-source.js +60 -0
  225. package/dist/lang/ast/space-seed.d.ts +16 -0
  226. package/dist/lang/ast/space-seed.js +59 -0
  227. package/dist/lang/ast/sql-elements/sql-statement.d.ts +19 -0
  228. package/dist/lang/ast/sql-elements/sql-statement.js +80 -0
  229. package/dist/lang/ast/sql-elements/sql-string.d.ts +12 -0
  230. package/dist/lang/ast/sql-elements/sql-string.js +60 -0
  231. package/dist/lang/ast/struct-utils.d.ts +4 -0
  232. package/dist/lang/ast/struct-utils.js +48 -0
  233. package/dist/lang/ast/time-utils.d.ts +12 -0
  234. package/dist/lang/ast/time-utils.js +140 -0
  235. package/dist/lang/ast/types/comparison.d.ts +11 -0
  236. package/dist/lang/ast/types/comparison.js +41 -0
  237. package/dist/lang/ast/types/document-compile-result.d.ts +8 -0
  238. package/dist/lang/ast/types/document-compile-result.js +25 -0
  239. package/dist/lang/ast/types/equality.d.ts +7 -0
  240. package/dist/lang/ast/types/equality.js +37 -0
  241. package/dist/lang/ast/types/executor.d.ts +9 -0
  242. package/dist/lang/ast/types/executor.js +25 -0
  243. package/dist/lang/ast/types/explore-desc.d.ts +5 -0
  244. package/dist/lang/ast/types/explore-desc.js +33 -0
  245. package/dist/lang/ast/types/explore-field.d.ts +5 -0
  246. package/dist/lang/ast/types/explore-field.js +32 -0
  247. package/dist/lang/ast/types/expr-result.d.ts +5 -0
  248. package/dist/lang/ast/types/expr-result.js +25 -0
  249. package/dist/lang/ast/types/expr-value.d.ts +3 -0
  250. package/dist/lang/ast/types/expr-value.js +25 -0
  251. package/dist/lang/ast/types/expression-def.d.ts +69 -0
  252. package/dist/lang/ast/types/expression-def.js +346 -0
  253. package/dist/lang/ast/types/field-collection-member.d.ts +5 -0
  254. package/dist/lang/ast/types/field-collection-member.js +34 -0
  255. package/dist/lang/ast/types/field-decl.d.ts +7 -0
  256. package/dist/lang/ast/types/field-decl.js +37 -0
  257. package/dist/lang/ast/types/field-space.d.ts +24 -0
  258. package/dist/lang/ast/types/field-space.js +44 -0
  259. package/dist/lang/ast/types/granular-result.d.ts +7 -0
  260. package/dist/lang/ast/types/granular-result.js +11 -0
  261. package/dist/lang/ast/types/lookup-result.d.ts +10 -0
  262. package/dist/lang/ast/types/lookup-result.js +25 -0
  263. package/dist/lang/ast/types/malloy-element.d.ts +106 -0
  264. package/dist/lang/ast/types/malloy-element.js +398 -0
  265. package/dist/lang/ast/types/model-entry.d.ts +6 -0
  266. package/dist/lang/ast/types/model-entry.js +25 -0
  267. package/dist/lang/ast/types/name-space.d.ts +5 -0
  268. package/dist/lang/ast/types/name-space.js +25 -0
  269. package/dist/lang/ast/types/nested-query.d.ts +3 -0
  270. package/dist/lang/ast/types/nested-query.js +25 -0
  271. package/dist/lang/ast/types/op-desc.d.ts +6 -0
  272. package/dist/lang/ast/types/op-desc.js +25 -0
  273. package/dist/lang/ast/types/query-comp.d.ts +5 -0
  274. package/dist/lang/ast/types/query-comp.js +25 -0
  275. package/dist/lang/ast/types/query-element.d.ts +5 -0
  276. package/dist/lang/ast/types/query-element.js +32 -0
  277. package/dist/lang/ast/types/query-item.d.ts +5 -0
  278. package/dist/lang/ast/types/query-item.js +25 -0
  279. package/dist/lang/ast/types/query-property.d.ts +17 -0
  280. package/dist/lang/ast/types/query-property.js +55 -0
  281. package/dist/lang/ast/types/source-property.d.ts +10 -0
  282. package/dist/lang/ast/types/source-property.js +43 -0
  283. package/dist/lang/ast/types/space-entry.d.ts +5 -0
  284. package/dist/lang/ast/types/space-entry.js +29 -0
  285. package/dist/lang/ast/types/space-field.d.ts +10 -0
  286. package/dist/lang/ast/types/space-field.js +48 -0
  287. package/dist/lang/ast/types/space-param.d.ts +20 -0
  288. package/dist/lang/ast/types/space-param.js +61 -0
  289. package/dist/lang/ast/types/time-result.d.ts +6 -0
  290. package/dist/lang/ast/types/time-result.js +25 -0
  291. package/dist/lang/ast/types/turtle-headed-pipe.d.ts +7 -0
  292. package/dist/lang/ast/types/turtle-headed-pipe.js +37 -0
  293. package/dist/lang/ast/types/type-desc.d.ts +7 -0
  294. package/dist/lang/ast/types/type-desc.js +25 -0
  295. package/dist/lang/field-utils.d.ts +5 -0
  296. package/dist/lang/field-utils.js +43 -0
  297. package/dist/lang/index.d.ts +10 -0
  298. package/dist/lang/index.js +32 -0
  299. package/dist/lang/lib/Malloy/MalloyLexer.d.ts +165 -0
  300. package/dist/lang/lib/Malloy/MalloyLexer.js +1232 -0
  301. package/dist/lang/lib/Malloy/MalloyParser.d.ts +1947 -0
  302. package/dist/lang/lib/Malloy/MalloyParser.js +11909 -0
  303. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +2022 -0
  304. package/dist/lang/lib/Malloy/MalloyParserListener.js +4 -0
  305. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +1274 -0
  306. package/dist/lang/lib/Malloy/MalloyParserVisitor.js +4 -0
  307. package/dist/lang/malloy-to-ast.d.ts +155 -0
  308. package/dist/lang/malloy-to-ast.js +990 -0
  309. package/dist/lang/parse-error-handler.d.ts +9 -0
  310. package/dist/lang/parse-error-handler.js +44 -0
  311. package/dist/lang/parse-log.d.ts +27 -0
  312. package/dist/lang/parse-log.js +51 -0
  313. package/dist/lang/parse-malloy.d.ts +182 -0
  314. package/dist/lang/parse-malloy.js +728 -0
  315. package/dist/lang/parse-tree-walkers/document-completion-walker.d.ts +10 -0
  316. package/dist/lang/parse-tree-walkers/document-completion-walker.js +148 -0
  317. package/dist/lang/parse-tree-walkers/document-help-context-walker.d.ts +9 -0
  318. package/dist/lang/parse-tree-walkers/document-help-context-walker.js +107 -0
  319. package/dist/lang/parse-tree-walkers/document-highlight-walker.d.ts +83 -0
  320. package/dist/lang/parse-tree-walkers/document-highlight-walker.js +385 -0
  321. package/dist/lang/parse-tree-walkers/document-symbol-walker.d.ts +11 -0
  322. package/dist/lang/parse-tree-walkers/document-symbol-walker.js +182 -0
  323. package/dist/lang/parse-tree-walkers/explore-query-walker.d.ts +34 -0
  324. package/dist/lang/parse-tree-walkers/explore-query-walker.js +74 -0
  325. package/dist/lang/parse-tree-walkers/find-external-references.d.ts +11 -0
  326. package/dist/lang/parse-tree-walkers/find-external-references.js +61 -0
  327. package/dist/lang/reference-list.d.ts +10 -0
  328. package/dist/lang/reference-list.js +90 -0
  329. package/dist/lang/test/document-help-context-walker.spec.d.ts +1 -0
  330. package/dist/lang/test/document-help-context-walker.spec.js +55 -0
  331. package/dist/lang/test/document-symbol-walker.spec.d.ts +1 -0
  332. package/dist/lang/test/document-symbol-walker.spec.js +110 -0
  333. package/dist/lang/test/field-symbols.spec.d.ts +1 -0
  334. package/dist/lang/test/field-symbols.spec.js +184 -0
  335. package/dist/lang/test/parse.spec.d.ts +14 -0
  336. package/dist/lang/test/parse.spec.js +2135 -0
  337. package/dist/lang/test/test-translator.d.ts +41 -0
  338. package/dist/lang/test/test-translator.js +344 -0
  339. package/dist/lang/translate-response.d.ts +59 -0
  340. package/dist/lang/translate-response.js +30 -0
  341. package/dist/lang/zone.d.ts +57 -0
  342. package/dist/lang/zone.js +107 -0
  343. package/dist/malloy.d.ts +1146 -0
  344. package/dist/malloy.js +2413 -0
  345. package/dist/model/index.d.ts +2 -0
  346. package/dist/model/index.js +45 -0
  347. package/dist/model/malloy_query.d.ts +351 -0
  348. package/dist/model/malloy_query.js +3174 -0
  349. package/dist/model/malloy_types.d.ts +551 -0
  350. package/dist/model/malloy_types.js +378 -0
  351. package/dist/model/sql_block.d.ts +7 -0
  352. package/dist/model/sql_block.js +56 -0
  353. package/dist/model/utils.d.ts +22 -0
  354. package/dist/model/utils.js +102 -0
  355. package/dist/run_sql_options.d.ts +3 -0
  356. package/dist/run_sql_options.js +25 -0
  357. package/dist/runtime_types.d.ts +105 -0
  358. package/dist/runtime_types.js +25 -0
  359. package/package.json +2 -2
@@ -0,0 +1,3174 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.flattenQuery = exports.QueryModel = exports.Segment = void 0;
26
+ const lodash_1 = require("lodash");
27
+ const standardsql_1 = require("../dialect/standardsql");
28
+ const dialect_1 = require("../dialect");
29
+ const malloy_types_1 = require("./malloy_types");
30
+ const utils_1 = require("./utils");
31
+ // quote a string for SQL use. Perhaps should be in dialect.
32
+ function generateSQLStringLiteral(sourceString) {
33
+ return `'${sourceString}'`;
34
+ }
35
+ class StageWriter {
36
+ constructor(useCTE = true, parent) {
37
+ this.withs = [];
38
+ this.udfs = [];
39
+ this.pdts = [];
40
+ this.stagePrefix = "__stage";
41
+ this.parent = parent;
42
+ this.useCTE = useCTE;
43
+ }
44
+ getName(id) {
45
+ return `${this.stagePrefix}${id}`;
46
+ }
47
+ root() {
48
+ if (this.parent === undefined) {
49
+ return this;
50
+ }
51
+ else {
52
+ return this.parent.root();
53
+ }
54
+ }
55
+ addStage(sql) {
56
+ if (this.useCTE) {
57
+ this.withs.push(sql);
58
+ return this.getName(this.withs.length - 1);
59
+ }
60
+ else {
61
+ this.withs[0] = sql;
62
+ return (0, utils_1.indent)(`\n(${sql})\n`);
63
+ }
64
+ }
65
+ addUDF(stageWriter, dialect, structDef) {
66
+ // eslint-disable-next-line prefer-const
67
+ let { sql, lastStageName } = stageWriter.combineStages(true);
68
+ if (lastStageName === undefined) {
69
+ throw new Error("Internal Error: no stage to combine");
70
+ }
71
+ sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, structDef);
72
+ const id = `${dialect.udfPrefix}${this.root().udfs.length}`;
73
+ sql = dialect.sqlCreateFunction(id, sql);
74
+ this.root().udfs.push(sql);
75
+ return id;
76
+ }
77
+ addPDT(baseName, dialect) {
78
+ const sql = this.combineStages(false).sql + this.withs[this.withs.length - 1];
79
+ const tableName = "scratch." + baseName + (0, utils_1.generateHash)(sql);
80
+ this.root().pdts.push(dialect.sqlCreateTableAsSelect(tableName, sql));
81
+ return tableName;
82
+ }
83
+ // combine all the stages except the last one into a WITH statement
84
+ // return SQL and the last stage name
85
+ combineStages(includeLastStage) {
86
+ if (!this.useCTE) {
87
+ return { sql: this.withs[0], lastStageName: this.withs[0] };
88
+ }
89
+ let lastStageName = this.getName(0);
90
+ let prefix = `WITH `;
91
+ let w = "";
92
+ for (let i = 0; i < this.withs.length - (includeLastStage ? 0 : 1); i++) {
93
+ const sql = this.withs[i];
94
+ lastStageName = this.getName(i);
95
+ if (sql === undefined) {
96
+ throw new Error(`Expected sql WITH to be present for stage ${lastStageName}.`);
97
+ }
98
+ w += `${prefix}${lastStageName} AS (\n${(0, utils_1.indent)(sql)})\n`;
99
+ prefix = ", ";
100
+ }
101
+ return { sql: w, lastStageName };
102
+ }
103
+ /** emit the SQL for all the stages. */
104
+ generateSQLStages() {
105
+ const lastStageNum = this.withs.length - 1;
106
+ if (lastStageNum < 0) {
107
+ throw new Error("No SQL generated");
108
+ }
109
+ const udfs = this.udfs.join(`\n`);
110
+ const pdts = this.pdts.join(`\n`);
111
+ const sql = this.combineStages(false).sql;
112
+ return udfs + pdts + sql + this.withs[lastStageNum];
113
+ }
114
+ generateCoorelatedSubQuery(dialect, structDef) {
115
+ if (!this.useCTE) {
116
+ return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, structDef);
117
+ }
118
+ else {
119
+ return (this.combineStages(true).sql +
120
+ dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), structDef));
121
+ }
122
+ }
123
+ }
124
+ // type QueryNodeType =
125
+ // | "abstract"
126
+ // | "dimension"
127
+ // | "measure"
128
+ // | "query"
129
+ // | "turtle"
130
+ // | "struct";
131
+ class GenerateState {
132
+ constructor() {
133
+ this.totalGroupSet = -1;
134
+ }
135
+ withWhere(s) {
136
+ const newState = new GenerateState();
137
+ newState.whereSQL = s;
138
+ newState.applyValue = this.applyValue;
139
+ newState.totalGroupSet = this.totalGroupSet;
140
+ return newState;
141
+ }
142
+ withApply(s) {
143
+ const newState = new GenerateState();
144
+ newState.whereSQL = this.whereSQL;
145
+ newState.applyValue = s;
146
+ newState.totalGroupSet = this.totalGroupSet;
147
+ return newState;
148
+ }
149
+ withTotal(groupSet) {
150
+ const newState = new GenerateState();
151
+ newState.whereSQL = this.whereSQL;
152
+ newState.applyValue = this.applyValue;
153
+ newState.totalGroupSet = groupSet;
154
+ return newState;
155
+ }
156
+ }
157
+ class QueryNode {
158
+ constructor(fieldDef) {
159
+ this.fieldDef = fieldDef;
160
+ }
161
+ getIdentifier() {
162
+ return (0, malloy_types_1.getIdentifier)(this.fieldDef);
163
+ }
164
+ getChildByName(_name) {
165
+ return undefined;
166
+ }
167
+ }
168
+ class QueryField extends QueryNode {
169
+ constructor(fieldDef, parent) {
170
+ super(fieldDef);
171
+ this.parent = parent;
172
+ this.fieldDef = fieldDef;
173
+ }
174
+ mayNeedUniqueKey() {
175
+ return false;
176
+ }
177
+ getJoinableParent() {
178
+ // if it is inline it should always have a parent
179
+ const parent = this.parent;
180
+ if (parent.fieldDef.structRelationship.type === "inline") {
181
+ if (parent) {
182
+ return parent.getJoinableParent();
183
+ }
184
+ else {
185
+ throw new Error(`Internal Error: inline struct cannot be root`);
186
+ }
187
+ }
188
+ return parent;
189
+ }
190
+ caseGroup(groupSets, s) {
191
+ if (groupSets.length === 0) {
192
+ return s;
193
+ }
194
+ else {
195
+ const exp = groupSets.length === 1
196
+ ? `=${groupSets[0]}`
197
+ : ` IN (${groupSets.join(",")})`;
198
+ return `CASE WHEN group_set${exp} THEN\n ${s}\n END`;
199
+ }
200
+ }
201
+ getFullOutputName() {
202
+ return this.parent.getFullOutputName() + this.getIdentifier();
203
+ }
204
+ generateFieldFragment(resultSet, context, expr, state) {
205
+ // find the structDef and return the path to the field...
206
+ const field = context.getFieldByName(expr.path);
207
+ if ((0, malloy_types_1.hasExpression)(field.fieldDef)) {
208
+ let ret = this.generateExpressionFromExpr(resultSet, field.parent, field.fieldDef.e, state);
209
+ if (!ret.match(/^\(.*\)$/)) {
210
+ ret = `(${ret})`;
211
+ }
212
+ return ret;
213
+ }
214
+ else {
215
+ // return field.parent.getIdentifier() + "." + field.fieldDef.name;
216
+ return field.generateExpression(resultSet);
217
+ }
218
+ }
219
+ generateParameterFragment(resultSet, context, expr, state) {
220
+ // find the structDef and return the path to the field...
221
+ const param = context.parameters()[expr.path];
222
+ if ((0, malloy_types_1.isValueParameter)(param)) {
223
+ if (param.value) {
224
+ return this.generateExpressionFromExpr(resultSet, context, param.value, state);
225
+ }
226
+ }
227
+ else if (param.condition) {
228
+ return this.generateExpressionFromExpr(resultSet, context, param.condition, state);
229
+ }
230
+ throw new Error(`Can't generate SQL, no value for ${expr.path}`);
231
+ }
232
+ generateFilterFragment(resultSet, context, expr, state) {
233
+ const allWhere = new utils_1.AndChain(state.whereSQL);
234
+ for (const cond of expr.filterList) {
235
+ allWhere.add(this.generateExpressionFromExpr(resultSet, context, cond.expression, state.withWhere()));
236
+ }
237
+ return this.generateExpressionFromExpr(resultSet, context, expr.e, state.withWhere(allWhere.sql()));
238
+ }
239
+ generateDimFragment(resultSet, context, expr, state) {
240
+ let dim = this.generateExpressionFromExpr(resultSet, context, expr.e, state);
241
+ if (state.whereSQL) {
242
+ dim = `CASE WHEN ${state.whereSQL} THEN ${dim} END`;
243
+ }
244
+ return dim;
245
+ }
246
+ generateUngroupedFragment(resultSet, context, expr, state) {
247
+ if (state.totalGroupSet !== -1) {
248
+ throw new Error(`Already in ALL. Cannot nest within an all calcuation.`);
249
+ }
250
+ let totalGroupSet;
251
+ let ungroupSet;
252
+ if (expr.fields && expr.fields.length > 0) {
253
+ const key = expr.fields.sort().join("|") + expr.type;
254
+ ungroupSet = resultSet.ungroupedSets.get(key);
255
+ if (ungroupSet === undefined) {
256
+ throw new Error(`Internal Error, cannot find groupset with key ${key}`);
257
+ }
258
+ totalGroupSet = ungroupSet.groupSet;
259
+ }
260
+ else {
261
+ totalGroupSet = resultSet.parent ? resultSet.parent.groupSet : 0;
262
+ }
263
+ const s = this.generateExpressionFromExpr(resultSet, context, expr.e, state.withTotal(totalGroupSet));
264
+ const fields = resultSet.getUngroupPartitions(ungroupSet);
265
+ let partitionBy = "";
266
+ const fieldsString = fields.map((f) => f.getPartitionSQL()).join(", ");
267
+ if (fieldsString.length > 0) {
268
+ partitionBy = `PARTITION BY ${fieldsString}`;
269
+ }
270
+ return `MAX(${s}) OVER (${partitionBy})`;
271
+ }
272
+ generateDistinctKeyIfNecessary(resultSet, context, structPath) {
273
+ let struct = context;
274
+ if (structPath) {
275
+ struct = this.parent.root().getStructByName(structPath);
276
+ }
277
+ if (struct.needsSymetricCalculation(resultSet)) {
278
+ return struct.getDistinctKey().generateExpression(resultSet);
279
+ }
280
+ else {
281
+ return undefined;
282
+ }
283
+ }
284
+ generateSumFragment(resultSet, context, expr, state) {
285
+ const dimSQL = this.generateDimFragment(resultSet, context, expr, state);
286
+ const distinctKeySQL = this.generateDistinctKeyIfNecessary(resultSet, context, expr.structPath);
287
+ let ret;
288
+ if (distinctKeySQL) {
289
+ if (this.parent.dialect.supportsSumDistinctFunction) {
290
+ ret = this.parent.dialect.sqlSumDistinct(distinctKeySQL, dimSQL);
291
+ }
292
+ else {
293
+ ret = sqlSumDistinct(this.parent.dialect, dimSQL, distinctKeySQL);
294
+ }
295
+ }
296
+ else {
297
+ ret = `SUM(${dimSQL})`;
298
+ }
299
+ return `COALESCE(${ret},0)`;
300
+ }
301
+ generateSymmetricFragment(resultSet, context, expr, state) {
302
+ const dimSQL = this.generateDimFragment(resultSet, context, expr, state);
303
+ const f = expr.function === "count_distinct"
304
+ ? "count(distinct "
305
+ : expr.function + "(";
306
+ return `${f}${dimSQL})`;
307
+ }
308
+ generateAvgFragment(resultSet, context, expr, state) {
309
+ // find the structDef and return the path to the field...
310
+ const dimSQL = this.generateDimFragment(resultSet, context, expr, state);
311
+ const distinctKeySQL = this.generateDistinctKeyIfNecessary(resultSet, context, expr.structPath);
312
+ if (distinctKeySQL) {
313
+ let countDistinctKeySQL = distinctKeySQL;
314
+ if (state.whereSQL) {
315
+ countDistinctKeySQL = `CASE WHEN ${state.whereSQL} THEN ${distinctKeySQL} END`;
316
+ }
317
+ let sumDistinctSQL;
318
+ if (this.parent.dialect.supportsSumDistinctFunction) {
319
+ sumDistinctSQL = this.parent.dialect.sqlSumDistinct(distinctKeySQL, dimSQL);
320
+ }
321
+ else {
322
+ sumDistinctSQL = sqlSumDistinct(this.parent.dialect, dimSQL, distinctKeySQL);
323
+ }
324
+ return `(${sumDistinctSQL})/NULLIF(COUNT(DISTINCT ${countDistinctKeySQL}),0)`;
325
+ }
326
+ else {
327
+ return `AVG(${dimSQL})`;
328
+ }
329
+ }
330
+ generateCountFragment(resultSet, context, expr, state) {
331
+ let func = "COUNT(";
332
+ let thing = "1";
333
+ const distinctKeySQL = this.generateDistinctKeyIfNecessary(resultSet, context, expr.structPath);
334
+ if (distinctKeySQL) {
335
+ func = "COUNT(DISTINCT";
336
+ thing = distinctKeySQL;
337
+ }
338
+ // find the structDef and return the path to the field...
339
+ if (state.whereSQL) {
340
+ return `${func} CASE WHEN ${state.whereSQL} THEN ${thing} END)`;
341
+ }
342
+ else {
343
+ return `${func} ${thing})`;
344
+ }
345
+ }
346
+ generateDialect(resultSet, context, expr, state) {
347
+ return this.generateExpressionFromExpr(resultSet, context, context.dialect.dialectExpr(expr), state);
348
+ }
349
+ generateAnalyticFragment(resultStruct, context, expr, state) {
350
+ const fields = resultStruct.getUngroupPartitions(undefined);
351
+ let partitionBy = "";
352
+ const fieldsString = fields.map((f) => f.getPartitionSQL()).join(", ");
353
+ if (fieldsString.length > 0) {
354
+ partitionBy = `PARTITION BY ${fieldsString}`;
355
+ }
356
+ let orderBy = "";
357
+ // calculate the ordering.
358
+ const obSQL = [];
359
+ let orderingField;
360
+ const orderByDef = resultStruct.firstSegment.orderBy ||
361
+ resultStruct.calculateDefaultOrderBy();
362
+ for (const ordering of orderByDef) {
363
+ if (typeof ordering.field === "string") {
364
+ orderingField = {
365
+ name: ordering.field,
366
+ fif: resultStruct.getField(ordering.field),
367
+ };
368
+ }
369
+ else {
370
+ orderingField = resultStruct.getFieldByNumber(ordering.field);
371
+ }
372
+ if (resultStruct.firstSegment.type === "reduce") {
373
+ obSQL.push(" " +
374
+ orderingField.fif.getSQL() +
375
+ // this.parent.dialect.sqlMaybeQuoteIdentifier(
376
+ // `${orderingField.name}__${resultStruct.groupSet}`
377
+ // ) +
378
+ ` ${ordering.dir || "ASC"}`);
379
+ }
380
+ else if (resultStruct.firstSegment.type === "project") {
381
+ obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir || "ASC"}`);
382
+ }
383
+ }
384
+ const func = malloy_types_1.malloyFunctions[expr.function];
385
+ const paramSQL = [];
386
+ if (expr.parameters !== undefined) {
387
+ for (const e of expr.parameters) {
388
+ if (typeof e === "string") {
389
+ paramSQL.push(e); // need to map to dimensional expression.
390
+ }
391
+ else if (typeof e === "number") {
392
+ paramSQL.push(e.toString());
393
+ }
394
+ else {
395
+ paramSQL.push(this.generateExpressionFromExpr(resultStruct, context, e, state));
396
+ }
397
+ }
398
+ }
399
+ if (obSQL.length > 0) {
400
+ orderBy = " " + this.parent.dialect.sqlOrderBy(obSQL);
401
+ }
402
+ const sqlName = func.sqlName || expr.function;
403
+ return `${sqlName}(${paramSQL.join(", ")}) OVER(${partitionBy} ${orderBy} )`;
404
+ }
405
+ generateExpressionFromExpr(resultSet, context, e, state = new GenerateState()) {
406
+ let s = "";
407
+ for (const expr of e) {
408
+ if (typeof expr === "string") {
409
+ s += expr;
410
+ }
411
+ else if ((0, malloy_types_1.isFieldFragment)(expr)) {
412
+ s += this.generateFieldFragment(resultSet, context, expr, state);
413
+ }
414
+ else if ((0, malloy_types_1.isParameterFragment)(expr)) {
415
+ s += this.generateParameterFragment(resultSet, context, expr, state);
416
+ }
417
+ else if ((0, malloy_types_1.isFilterFragment)(expr)) {
418
+ s += this.generateFilterFragment(resultSet, context, expr, state);
419
+ }
420
+ else if ((0, malloy_types_1.isUngroupFragment)(expr)) {
421
+ s += this.generateUngroupedFragment(resultSet, context, expr, state);
422
+ }
423
+ else if ((0, malloy_types_1.isAnalyticFragment)(expr)) {
424
+ s += this.generateAnalyticFragment(resultSet, context, expr, state);
425
+ }
426
+ else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
427
+ let agg;
428
+ if (expr.function === "sum") {
429
+ agg = this.generateSumFragment(resultSet, context, expr, state);
430
+ }
431
+ else if (expr.function === "avg") {
432
+ agg = this.generateAvgFragment(resultSet, context, expr, state);
433
+ }
434
+ else if (expr.function === "count") {
435
+ agg = this.generateCountFragment(resultSet, context, expr, state);
436
+ }
437
+ else if (["count_distinct", "min", "max"].includes(expr.function)) {
438
+ agg = this.generateSymmetricFragment(resultSet, context, expr, state);
439
+ }
440
+ else {
441
+ throw new Error(`Internal Error: Unknown aggregate function ${expr.function}`);
442
+ }
443
+ if (resultSet.root().isComplexQuery) {
444
+ let groupSet = resultSet.groupSet;
445
+ if (state.totalGroupSet !== -1) {
446
+ groupSet = state.totalGroupSet;
447
+ }
448
+ s += this.caseGroup([groupSet], agg);
449
+ }
450
+ else {
451
+ s += agg;
452
+ }
453
+ }
454
+ else if ((0, malloy_types_1.isApplyFragment)(expr)) {
455
+ const applyVal = this.generateExpressionFromExpr(resultSet, context, expr.value, state);
456
+ s += this.generateExpressionFromExpr(resultSet, context, expr.to, state.withApply(applyVal));
457
+ }
458
+ else if ((0, malloy_types_1.isApplyValue)(expr)) {
459
+ if (state.applyValue) {
460
+ s += state.applyValue;
461
+ }
462
+ else {
463
+ throw new Error(`Internal Error: Partial application value referenced but not provided`);
464
+ }
465
+ }
466
+ else if (expr.type == "dialect") {
467
+ s += this.generateDialect(resultSet, context, expr, state);
468
+ }
469
+ else {
470
+ throw new Error(`Internal Error: Unknown expression fragment ${JSON.stringify(expr, undefined, 2)}`);
471
+ }
472
+ }
473
+ return s;
474
+ }
475
+ getExpr() {
476
+ if ((0, malloy_types_1.hasExpression)(this.fieldDef)) {
477
+ return this.fieldDef.e;
478
+ }
479
+ return [
480
+ this.parent.dialect.sqlFieldReference(this.parent.getSQLIdentifier(), this.fieldDef.name, this.fieldDef.type, this.parent.fieldDef.structSource.type === "nested" ||
481
+ this.parent.fieldDef.structSource.type === "inline" ||
482
+ (this.parent.fieldDef.structSource.type === "sql" &&
483
+ this.parent.fieldDef.structSource.method === "nested"), this.parent.fieldDef.structRelationship.type === "nested" &&
484
+ this.parent.fieldDef.structRelationship.isArray),
485
+ ];
486
+ }
487
+ generateExpression(resultSet) {
488
+ return this.generateExpressionFromExpr(resultSet, this.parent, this.getExpr());
489
+ }
490
+ }
491
+ function isCalculatedField(f) {
492
+ return f instanceof QueryAtomicField && f.isCalculated();
493
+ }
494
+ function isScalarField(f) {
495
+ return f instanceof QueryAtomicField && !f.isCalculated();
496
+ }
497
+ class QueryAtomicField extends QueryField {
498
+ includeInWildcard() {
499
+ return true;
500
+ }
501
+ isCalculated() {
502
+ return (0, malloy_types_1.expressionIsCalculation)(this.fieldDef.expressionType);
503
+ }
504
+ getFilterList() {
505
+ return [];
506
+ }
507
+ hasExpression() {
508
+ return (0, malloy_types_1.hasExpression)(this.fieldDef);
509
+ }
510
+ }
511
+ // class QueryMeasure extends QueryField {}
512
+ class QueryFieldString extends QueryAtomicField {
513
+ }
514
+ class QueryFieldNumber extends QueryAtomicField {
515
+ }
516
+ class QueryFieldBoolean extends QueryAtomicField {
517
+ }
518
+ class QueryFieldJSON extends QueryAtomicField {
519
+ }
520
+ // in a query a struct can be referenced. The struct will
521
+ // emit the primary key field in the actual result set and
522
+ // will include the StructDef as a foreign key join in the output
523
+ // StructDef.
524
+ class QueryFieldStruct extends QueryAtomicField {
525
+ constructor(fieldDef, parent, primaryKey) {
526
+ super(fieldDef, parent);
527
+ this.primaryKey = primaryKey;
528
+ }
529
+ getName() {
530
+ return (0, malloy_types_1.getIdentifier)(this.fieldDef);
531
+ }
532
+ getAsJoinedStructDef(foreignKeyName) {
533
+ return {
534
+ ...this.parent.fieldDef,
535
+ structRelationship: {
536
+ type: "one",
537
+ onExpression: [
538
+ {
539
+ type: "field",
540
+ path: this.primaryKey,
541
+ },
542
+ "=",
543
+ { type: "field", path: foreignKeyName },
544
+ ],
545
+ },
546
+ };
547
+ }
548
+ }
549
+ class QueryFieldDate extends QueryAtomicField {
550
+ generateExpression(resultSet) {
551
+ const fd = this.fieldDef;
552
+ if (!fd.timeframe) {
553
+ return super.generateExpression(resultSet);
554
+ }
555
+ else {
556
+ const truncated = this.parent.dialect.sqlTrunc({ value: this.getExpr(), valueType: "date" }, fd.timeframe);
557
+ return this.generateExpressionFromExpr(resultSet, this.parent, truncated);
558
+ }
559
+ }
560
+ // clone ourselves on demand as a timeframe.
561
+ getChildByName(name) {
562
+ const fieldDef = {
563
+ ...this.fieldDef,
564
+ as: `${this.getIdentifier()}_${name}`,
565
+ timeframe: name,
566
+ };
567
+ return new QueryFieldDate(fieldDef, this.parent);
568
+ }
569
+ }
570
+ class QueryFieldTimestamp extends QueryAtomicField {
571
+ // clone ourselves on demand as a timeframe.
572
+ getChildByName(name) {
573
+ const fieldDef = {
574
+ ...this.fieldDef,
575
+ as: `${this.getIdentifier()}_${name}`,
576
+ timeframe: name,
577
+ };
578
+ return new QueryFieldTimestamp(fieldDef, this.parent);
579
+ }
580
+ }
581
+ class QueryFieldDistinctKey extends QueryAtomicField {
582
+ generateExpression(resultSet) {
583
+ var _a;
584
+ if (this.parent.primaryKey()) {
585
+ const pk = this.parent.getPrimaryKeyField(this.fieldDef);
586
+ return pk.generateExpression(resultSet);
587
+ }
588
+ else if (this.parent.fieldDef.structSource.type === "nested") {
589
+ const parentKey = (_a = this.parent.parent) === null || _a === void 0 ? void 0 : _a.getDistinctKey().generateExpression(resultSet);
590
+ return `CONCAT(${parentKey}, 'x', ${this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(), "__row_id", "string", true, false)})`;
591
+ }
592
+ else {
593
+ // return this.parent.getIdentifier() + "." + "__distinct_key";
594
+ return this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(), "__distinct_key", "string", this.parent.fieldDef.structRelationship.type === "nested", false);
595
+ }
596
+ }
597
+ includeInWildcard() {
598
+ return false;
599
+ }
600
+ }
601
+ const NUMERIC_DECIMAL_PRECISION = 9;
602
+ function sqlSumDistinct(dialect, sqlExp, sqlDistintKey) {
603
+ const precision = 9;
604
+ const uniqueInt = dialect.sqlSumDistinctHashedKey(sqlDistintKey);
605
+ const multiplier = 10 ** (precision - NUMERIC_DECIMAL_PRECISION);
606
+ const sumSQL = `
607
+ (
608
+ SUM(DISTINCT
609
+ (CAST(ROUND(COALESCE(${sqlExp},0)*(${multiplier}*1.0), ${NUMERIC_DECIMAL_PRECISION}) AS NUMERIC) +
610
+ ${uniqueInt}
611
+ ))
612
+ -
613
+ SUM(DISTINCT ${uniqueInt})
614
+ )`;
615
+ let ret = `(${sumSQL}/(${multiplier}*1.0))`;
616
+ ret = `CAST(${ret} as ${dialect.defaultNumberType})`;
617
+ return ret;
618
+ }
619
+ class FieldInstanceField {
620
+ constructor(f, fieldUsage, parent) {
621
+ this.type = "field";
622
+ this.additionalGroupSets = [];
623
+ this.f = f;
624
+ this.fieldUsage = fieldUsage;
625
+ this.parent = parent;
626
+ }
627
+ root() {
628
+ return this.parent.root();
629
+ }
630
+ getSQL() {
631
+ let exp = this.f.generateExpression(this.parent);
632
+ if (isScalarField(this.f)) {
633
+ exp = this.f.caseGroup(this.parent.childGroups.concat(this.additionalGroupSets),
634
+ // this.parent.groupSet > 0 ? this.parent.childGroups : [],
635
+ exp);
636
+ }
637
+ return exp;
638
+ }
639
+ getPartitionSQL() {
640
+ if (this.partitionSQL === undefined) {
641
+ return this.getSQL();
642
+ }
643
+ else {
644
+ return this.partitionSQL;
645
+ }
646
+ }
647
+ }
648
+ class FieldInstanceResult {
649
+ constructor(turtleDef, parent) {
650
+ this.type = "query";
651
+ this.allFields = new Map();
652
+ this.groupSet = 0;
653
+ this.depth = 0;
654
+ this.childGroups = [];
655
+ this.hasHaving = false;
656
+ this.ungroupedSets = new Map();
657
+ // query: QueryQuery;
658
+ this.resultUsesUngrouped = false;
659
+ this.parent = parent;
660
+ this.turtleDef = turtleDef;
661
+ this.firstSegment = turtleDef.pipeline[0];
662
+ }
663
+ addField(as, field, usage) {
664
+ let fi;
665
+ if ((fi = this.allFields.get(as))) {
666
+ if (fi.type === "query") {
667
+ throw new Error(`Redefinition of field ${field.fieldDef.name} as struct`);
668
+ }
669
+ const fif = fi;
670
+ if (fif.fieldUsage.type === "result") {
671
+ if (usage.type !== "result") {
672
+ // its already in the result, we can just ignore it.
673
+ return;
674
+ }
675
+ else {
676
+ throw new Error(`Ambiguous output field name '${field.fieldDef.name}'.`);
677
+ }
678
+ }
679
+ }
680
+ this.add(as, new FieldInstanceField(field, usage, this));
681
+ }
682
+ parentGroupSet() {
683
+ if (this.parent) {
684
+ return this.parent.groupSet;
685
+ }
686
+ else {
687
+ return 0;
688
+ }
689
+ }
690
+ add(name, f) {
691
+ this.allFields.set(name, f);
692
+ }
693
+ hasField(name) {
694
+ const fi = this.allFields.get(name);
695
+ return fi !== undefined && fi instanceof FieldInstanceField;
696
+ }
697
+ getField(name) {
698
+ const fi = this.allFields.get(name);
699
+ if (fi === undefined) {
700
+ throw new Error(`Internal Error, field Not defined ${name}`);
701
+ }
702
+ else if (fi instanceof FieldInstanceField) {
703
+ return fi;
704
+ }
705
+ throw new Error(`can't use a query here ${name}`);
706
+ }
707
+ getFieldByNumber(index) {
708
+ for (const [name, fi] of this.allFields) {
709
+ if (fi instanceof FieldInstanceField) {
710
+ if (fi.fieldUsage.type === "result" &&
711
+ fi.fieldUsage.resultIndex === index) {
712
+ return { name, fif: fi };
713
+ }
714
+ }
715
+ }
716
+ throw new Error(`Invalid Order By index '${index}`);
717
+ }
718
+ // loops through all the turtled queries and computes recomputes the group numbers
719
+ computeGroups(nextGroupSetNumber, depth) {
720
+ // if the root node uses a total, start at 1.
721
+ if (nextGroupSetNumber === 0 && this.resultUsesUngrouped) {
722
+ this.root().computeOnlyGroups.push(nextGroupSetNumber++);
723
+ }
724
+ // make a groupset for each unique ungrouping expression
725
+ for (const [_key, grouping] of this.ungroupedSets) {
726
+ const groupSet = nextGroupSetNumber++;
727
+ grouping.groupSet = groupSet;
728
+ this.root().computeOnlyGroups.push(groupSet);
729
+ }
730
+ this.groupSet = nextGroupSetNumber++;
731
+ this.depth = depth;
732
+ let maxDepth = depth;
733
+ let isComplex = false;
734
+ let children = [this.groupSet];
735
+ for (const [_name, fi] of this.allFields) {
736
+ if (fi.type === "query") {
737
+ const fir = fi;
738
+ isComplex = true;
739
+ if (fir.firstSegment.type === "reduce") {
740
+ const r = fir.computeGroups(nextGroupSetNumber, depth + 1);
741
+ children = children.concat(r.children);
742
+ nextGroupSetNumber = r.nextGroupSetNumber;
743
+ if (r.maxDepth > maxDepth) {
744
+ maxDepth = r.maxDepth;
745
+ }
746
+ }
747
+ }
748
+ }
749
+ this.childGroups = children;
750
+ return { nextGroupSetNumber, maxDepth, children, isComplex };
751
+ }
752
+ fields(fn = undefined) {
753
+ const ret = [];
754
+ for (const e of this.allFields.values()) {
755
+ if (e instanceof FieldInstanceField) {
756
+ if (fn === undefined || fn(e)) {
757
+ ret.push(e);
758
+ }
759
+ }
760
+ }
761
+ return ret;
762
+ }
763
+ fieldNames(fn) {
764
+ const ret = [];
765
+ for (const [name, fi] of this.allFields) {
766
+ if (fi instanceof FieldInstanceField) {
767
+ if (fn === undefined || fn(fi)) {
768
+ ret.push(name);
769
+ }
770
+ }
771
+ }
772
+ return ret;
773
+ }
774
+ // if a turtled result is all measures, we emit use ANY_VALUE for the aggregation
775
+ // and emit the resulting structure as a RECORD instead of REPEATED
776
+ // if we have all numbers, we need to know because we'll have to conjur a record.
777
+ getRepeatedResultType() {
778
+ let ret = "inline_all_numbers";
779
+ for (const f of this.fields()) {
780
+ if (f.fieldUsage.type === "result") {
781
+ if (isScalarField(f.f)) {
782
+ return "nested";
783
+ }
784
+ if (f.f instanceof QueryStruct) {
785
+ ret = "inline";
786
+ }
787
+ }
788
+ }
789
+ return ret;
790
+ }
791
+ structs() {
792
+ const ret = [];
793
+ for (const e of this.allFields.values()) {
794
+ if (e instanceof FieldInstanceResult) {
795
+ ret.push(e);
796
+ }
797
+ }
798
+ return ret;
799
+ }
800
+ // return a list of structs that match the criteria
801
+ // specified in the function.
802
+ selectStructs(result, fn) {
803
+ if (fn(this)) {
804
+ result.push(this);
805
+ }
806
+ for (const e of this.structs()) {
807
+ e.selectStructs(result, fn);
808
+ }
809
+ return result;
810
+ }
811
+ calculateDefaultOrderBy() {
812
+ // LookML rules for default ordering.
813
+ // Date or time or ordnal based fields, that field ascending
814
+ // First Measure Descending.
815
+ let firstField;
816
+ for (const [_name, fi] of this.allFields) {
817
+ if (fi instanceof FieldInstanceField) {
818
+ if (fi.fieldUsage.type === "result") {
819
+ firstField || (firstField = fi.fieldUsage.resultIndex);
820
+ if (["date", "timestamp"].indexOf(fi.f.fieldDef.type) > -1) {
821
+ return [{ dir: "desc", field: fi.fieldUsage.resultIndex }];
822
+ }
823
+ else if (isCalculatedField(fi.f)) {
824
+ return [{ dir: "desc", field: fi.fieldUsage.resultIndex }];
825
+ }
826
+ }
827
+ }
828
+ }
829
+ if (firstField) {
830
+ return [{ dir: "asc", field: firstField }];
831
+ }
832
+ return [];
833
+ }
834
+ addStructToJoin(qs, query, mayNeedUniqueKey, joinStack) {
835
+ var _a;
836
+ const name = qs.getIdentifier();
837
+ // we're already chasing the dependency for this join.
838
+ if (joinStack.indexOf(name) !== -1) {
839
+ return;
840
+ }
841
+ let join;
842
+ if ((join = this.root().joins.get(name))) {
843
+ join.mayNeedUniqueKey || (join.mayNeedUniqueKey = mayNeedUniqueKey);
844
+ return;
845
+ }
846
+ // if we have a parent, join it first.
847
+ let parent;
848
+ const parentStruct = (_a = qs.parent) === null || _a === void 0 ? void 0 : _a.getJoinableParent();
849
+ if (parentStruct) {
850
+ // add dependant expressions first...
851
+ this.addStructToJoin(parentStruct, query, false, joinStack);
852
+ parent = this.root().joins.get(parentStruct.getIdentifier());
853
+ }
854
+ // add any dependant joins based on the ON
855
+ const sr = qs.fieldDef.structRelationship;
856
+ if ((0, malloy_types_1.isJoinOn)(sr) &&
857
+ qs.parent && // if the join has an ON, it must thave a parent
858
+ sr.onExpression !== undefined &&
859
+ joinStack.indexOf(name) === -1) {
860
+ query.addDependantExpr(this, qs.parent, sr.onExpression, [
861
+ ...joinStack,
862
+ name,
863
+ ]);
864
+ }
865
+ if (!(join = this.root().joins.get(name))) {
866
+ join = new JoinInstance(qs, name, parent);
867
+ this.root().joins.set(name, join);
868
+ }
869
+ join.mayNeedUniqueKey || (join.mayNeedUniqueKey = mayNeedUniqueKey);
870
+ }
871
+ findJoins(query) {
872
+ for (const dim of this.fields()) {
873
+ this.addStructToJoin(dim.f.getJoinableParent(), query, dim.f.mayNeedUniqueKey(), []);
874
+ }
875
+ for (const s of this.structs()) {
876
+ s.findJoins(query);
877
+ }
878
+ }
879
+ root() {
880
+ if (this.parent) {
881
+ return this.parent.root();
882
+ }
883
+ throw new Error(`Internal Error, Null parent FieldInstanceResult`);
884
+ }
885
+ getUngroupPartitions(ungroupSet) {
886
+ let ret = [];
887
+ let p = this;
888
+ let excludeFields = [];
889
+ let inScopeFieldNames = [];
890
+ // all defaults to all fields at the current level.
891
+ if (ungroupSet === undefined || ungroupSet.type === "all") {
892
+ // fields specified an an all, convert it to an exclude set.
893
+ const allFields = (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || [];
894
+ // convert an All into the equivalent exclude
895
+ excludeFields = this.fields((fi) => isScalarField(fi.f) &&
896
+ fi.fieldUsage.type === "result" &&
897
+ allFields.indexOf(fi.f.getIdentifier()) === -1).map((fi) => fi.f.getIdentifier());
898
+ }
899
+ else {
900
+ excludeFields = ungroupSet.fields;
901
+ }
902
+ let firstScope = true;
903
+ while (p !== undefined) {
904
+ // get a list of valid fieldnames for the current scope.
905
+ if (firstScope || (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type) === "exclude") {
906
+ inScopeFieldNames = inScopeFieldNames.concat(p
907
+ .fields((fi) => isScalarField(fi.f) && fi.fieldUsage.type === "result")
908
+ .map((fi) => fi.f.getIdentifier()));
909
+ }
910
+ ret = ret.concat(p.fields((fi) => isScalarField(fi.f) &&
911
+ fi.fieldUsage.type === "result" &&
912
+ excludeFields.indexOf(fi.f.getIdentifier()) === -1));
913
+ p = p.parent;
914
+ firstScope = false;
915
+ }
916
+ // verify that all names specified are available in the current scope.
917
+ for (const fieldName of (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || []) {
918
+ if (inScopeFieldNames.indexOf(fieldName) === -1) {
919
+ throw new Error(`${ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type}(): unknown field name "${fieldName}" or name not in scope.`);
920
+ }
921
+ }
922
+ return ret;
923
+ }
924
+ assignFieldsToGroups() {
925
+ for (const [_key, grouping] of this.ungroupedSets) {
926
+ for (const fieldInstance of this.getUngroupPartitions(grouping)) {
927
+ fieldInstance.additionalGroupSets.push(grouping.groupSet);
928
+ }
929
+ }
930
+ for (const child of this.structs()) {
931
+ child.assignFieldsToGroups();
932
+ }
933
+ }
934
+ }
935
+ /* Root Result as opposed to a turtled result */
936
+ class FieldInstanceResultRoot extends FieldInstanceResult {
937
+ constructor(turtleDef) {
938
+ super(turtleDef, undefined);
939
+ this.joins = new Map();
940
+ this.havings = new utils_1.AndChain();
941
+ this.isComplexQuery = false;
942
+ this.queryUsesPartitioning = false;
943
+ this.computeOnlyGroups = [];
944
+ this.elimatedComputeGroups = false;
945
+ }
946
+ root() {
947
+ return this;
948
+ }
949
+ // in the stage immediately following stage0 we need to elimiate any of the
950
+ // groups that were used in ungroup calculations. We need to do this only
951
+ // once and in the very next stage.
952
+ eliminateComputeGroupsSQL() {
953
+ if (this.elimatedComputeGroups || this.computeOnlyGroups.length == 0) {
954
+ return "";
955
+ }
956
+ else {
957
+ this.elimatedComputeGroups = true;
958
+ return `group_set NOT IN (${this.computeOnlyGroups.join(",")})`;
959
+ }
960
+ }
961
+ // look at all the fields again in the structs in the query
962
+ calculateSymmetricAggregates() {
963
+ let leafiest;
964
+ for (const [name, join] of this.joins) {
965
+ // first join is by default the
966
+ const relationship = join.parentRelationship();
967
+ if (relationship === "many_to_many") {
968
+ // everything must be calculated with symmetric aggregates
969
+ leafiest = "0never";
970
+ }
971
+ else if (leafiest === undefined) {
972
+ leafiest = name;
973
+ }
974
+ else if (join.parentRelationship() === "one_to_many") {
975
+ // check up the parent relationship until you find
976
+ // the current leafiest node. If it isn't in the direct path
977
+ // we need symmetric aggregate for everything.
978
+ // if it is in the path, than this one becomes leafiest
979
+ const s = join.queryStruct;
980
+ if (s.parent && s.parent.getIdentifier() === leafiest) {
981
+ leafiest = name;
982
+ }
983
+ else {
984
+ // we have more than on one_to_many join chain, all bets are off.
985
+ leafiest = "0never";
986
+ }
987
+ }
988
+ }
989
+ // console.log(`LEAFIEST: ${leafiest}`);
990
+ for (const [name, join] of this.joins) {
991
+ join.leafiest = name === leafiest;
992
+ }
993
+ // figure out which joins we need to manufacture distinct keys for.
994
+ // Nested Unique keys are dependant on the primary key of the parent
995
+ // and the table.
996
+ for (const [_name, join] of this.joins) {
997
+ // don't need keys on leafiest
998
+ if (!join.leafiest && join.mayNeedUniqueKey) {
999
+ let j = join;
1000
+ while (j) {
1001
+ if (!j.queryStruct.primaryKey()) {
1002
+ j.makeUniqueKey = true;
1003
+ }
1004
+ if (j.queryStruct.fieldDef.structRelationship.type === "nested") {
1005
+ j = j.parent;
1006
+ }
1007
+ else {
1008
+ j = undefined;
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+ class JoinInstance {
1016
+ constructor(queryStruct, alias, parent) {
1017
+ this.mayNeedUniqueKey = false;
1018
+ this.makeUniqueKey = false;
1019
+ this.leafiest = false;
1020
+ this.children = [];
1021
+ this.queryStruct = queryStruct;
1022
+ this.parent = parent;
1023
+ this.alias = alias;
1024
+ if (parent) {
1025
+ parent.children.push(this);
1026
+ }
1027
+ // convert the filter list into a list of boolean fields so we can
1028
+ // generate dependancies and code for them.
1029
+ if (this.queryStruct.fieldDef.filterList) {
1030
+ this.joinFilterConditions = [];
1031
+ for (const filter of this.queryStruct.fieldDef.filterList) {
1032
+ const qf = new QueryFieldBoolean({
1033
+ type: "boolean",
1034
+ name: "ignoreme",
1035
+ e: filter.expression,
1036
+ }, this.queryStruct);
1037
+ this.joinFilterConditions.push(qf);
1038
+ }
1039
+ }
1040
+ }
1041
+ parentRelationship() {
1042
+ if (this.queryStruct.parent === undefined) {
1043
+ return "root";
1044
+ }
1045
+ switch (this.queryStruct.fieldDef.structRelationship.type) {
1046
+ case "one":
1047
+ return "many_to_one";
1048
+ case "cross":
1049
+ return "many_to_many";
1050
+ case "many":
1051
+ return "one_to_many";
1052
+ case "nested":
1053
+ return "one_to_many";
1054
+ case "inline":
1055
+ return "one_to_one";
1056
+ default:
1057
+ throw new Error(`Internal error unknown relationship type to parent for ${this.queryStruct.fieldDef.name}`);
1058
+ }
1059
+ }
1060
+ // postgres unnest needs to know the names of the physical fields.
1061
+ getDialectFieldList() {
1062
+ const dialectFieldList = [];
1063
+ for (const f of this.queryStruct.fieldDef.fields.filter(malloy_types_1.isPhysical)) {
1064
+ dialectFieldList.push({
1065
+ type: f.type,
1066
+ sqlExpression: (0, malloy_types_1.getIdentifier)(f),
1067
+ sqlOutputName: (0, malloy_types_1.getIdentifier)(f),
1068
+ });
1069
+ }
1070
+ return dialectFieldList;
1071
+ }
1072
+ }
1073
+ /** nested query */
1074
+ class QueryTurtle extends QueryField {
1075
+ }
1076
+ /**
1077
+ * Used by the translator to get the output StructDef of a pipe segment
1078
+ *
1079
+ * half translated to the new world of types ..
1080
+ */
1081
+ class Segment {
1082
+ // static nextStructDef(s: StructDef, q: AnonymousQueryDef): StructDef
1083
+ static nextStructDef(structDef, segment) {
1084
+ const qs = new QueryStruct(structDef, {
1085
+ model: new QueryModel(undefined),
1086
+ });
1087
+ const turtleDef = {
1088
+ type: "turtle",
1089
+ name: "ignoreme",
1090
+ pipeline: [segment],
1091
+ };
1092
+ const queryQueryQuery = QueryQuery.makeQuery(turtleDef, qs, new StageWriter(true, undefined) // stage write indicates we want to get a result.
1093
+ );
1094
+ return queryQueryQuery.getResultStructDef();
1095
+ }
1096
+ }
1097
+ exports.Segment = Segment;
1098
+ /** Query builder object. */
1099
+ class QueryQuery extends QueryField {
1100
+ constructor(fieldDef, parent, stageWriter) {
1101
+ super(fieldDef, parent);
1102
+ this.prepared = false;
1103
+ this.maxDepth = 0;
1104
+ this.maxGroupSet = 0;
1105
+ this.fieldDef = fieldDef;
1106
+ this.rootResult = new FieldInstanceResultRoot(fieldDef);
1107
+ this.stageWriter = stageWriter;
1108
+ // do some magic here to get the first segment.
1109
+ this.firstSegment = fieldDef.pipeline[0];
1110
+ }
1111
+ static makeQuery(fieldDef, parentStruct, stageWriter = undefined) {
1112
+ let flatTurtleDef = parentStruct.flattenTurtleDef(fieldDef);
1113
+ let parent = parentStruct;
1114
+ const firstStage = flatTurtleDef.pipeline[0];
1115
+ // if we are generating code
1116
+ // and have extended declaration, we need to make a new QueryStruct
1117
+ // copy the definitions into a new structdef
1118
+ // edit the declations from the pipeline
1119
+ if (stageWriter !== undefined &&
1120
+ (0, malloy_types_1.isQuerySegment)(firstStage) &&
1121
+ firstStage.extendSource !== undefined) {
1122
+ parent = new QueryStruct({
1123
+ ...parentStruct.fieldDef,
1124
+ fields: [...parentStruct.fieldDef.fields, ...firstStage.extendSource],
1125
+ }, parent.parent ? { struct: parent } : { model: parent.model });
1126
+ flatTurtleDef = {
1127
+ ...flatTurtleDef,
1128
+ pipeline: [
1129
+ {
1130
+ ...firstStage,
1131
+ extendSource: undefined,
1132
+ },
1133
+ ...flatTurtleDef.pipeline.slice(1),
1134
+ ],
1135
+ };
1136
+ }
1137
+ switch (firstStage.type) {
1138
+ case "reduce":
1139
+ return new QueryQueryReduce(flatTurtleDef, parent, stageWriter);
1140
+ case "project":
1141
+ return new QueryQueryProject(flatTurtleDef, parent, stageWriter);
1142
+ case "index":
1143
+ return new QueryQueryIndex(flatTurtleDef, parent, stageWriter);
1144
+ }
1145
+ }
1146
+ getFieldList() {
1147
+ switch (this.firstSegment.type) {
1148
+ // case "index":
1149
+ // return this.firstSegment.fields || [];
1150
+ case "reduce":
1151
+ return this.firstSegment.fields;
1152
+ // probably need some way of checking type class of field here...
1153
+ // project should only contain scalars
1154
+ case "project":
1155
+ return this.firstSegment.fields;
1156
+ default:
1157
+ throw new Error(`Query contains no fields ${JSON.stringify(this.fieldDef)}`);
1158
+ }
1159
+ }
1160
+ // get a field ref and expand it.
1161
+ expandField(f) {
1162
+ let as;
1163
+ let field;
1164
+ // if it is a string
1165
+ if (typeof f === "string") {
1166
+ field = this.parent.getQueryFieldByName(f);
1167
+ }
1168
+ else if ("type" in f) {
1169
+ field = this.parent.makeQueryField(f);
1170
+ }
1171
+ // or FilteredAliasedName or a hacked timestamp field.
1172
+ else if ("name" in f && "as" in f) {
1173
+ field = this.parent.getQueryFieldByName(f.name);
1174
+ // QueryFieldStructs return new names...
1175
+ as = field.fieldDef.as || f.as;
1176
+ if (field instanceof QueryFieldStruct) {
1177
+ throw new Error("Syntax currently disallowed. Semantics up for discussion");
1178
+ }
1179
+ // Types of aliased fields.
1180
+ // turtles
1181
+ // Timestamps and Dates (are just fine to leave as is).
1182
+ // measures
1183
+ // let e: Expr;
1184
+ if (field instanceof QueryQuery) {
1185
+ const newFieldDef = (0, lodash_1.cloneDeep)(field.fieldDef);
1186
+ newFieldDef.as = f.name;
1187
+ newFieldDef.filterList = f.filterList;
1188
+ field = QueryQuery.makeQuery(newFieldDef, this.parent);
1189
+ }
1190
+ else if (!(field instanceof QueryFieldTimestamp ||
1191
+ field instanceof QueryFieldDate)) {
1192
+ throw new Error(`No longer generate code this way. \n ${JSON.stringify(f, undefined, 2)}`);
1193
+ // // its a measure
1194
+ // e = [{ type: "field", path: field.getFullOutputName() }];
1195
+ // if ("filterList" in f && f.filterList) {
1196
+ // e = [{ type: "filterExpression", filterList: f.filterList, e: e }];
1197
+ // }
1198
+ // const newFieldDef = {
1199
+ // type: field.fieldDef.type,
1200
+ // name: f.as,
1201
+ // e,
1202
+ // };
1203
+ // field = this.parent.makeQueryField(newFieldDef as FieldDef);
1204
+ }
1205
+ // or inline field FieldTypeDef
1206
+ }
1207
+ else {
1208
+ throw new Error(`Unrecognized field definition ${JSON.stringify(f, undefined, 2)}`);
1209
+ }
1210
+ if (!as) {
1211
+ as = field.getIdentifier();
1212
+ }
1213
+ return { as, field };
1214
+ }
1215
+ expandDependantField(resultStruct, fieldRef) {
1216
+ this.expandField(fieldRef);
1217
+ }
1218
+ // find all the fieldNames in the struct (and children)
1219
+ // that match the filter
1220
+ expandWildCardStruct(struct, expandChildren, filter = undefined) {
1221
+ let fieldNames = [];
1222
+ const structs = [];
1223
+ for (const [_name, f] of struct.nameMap) {
1224
+ if (f instanceof QueryAtomicField &&
1225
+ isScalarField(f) &&
1226
+ f.includeInWildcard() &&
1227
+ !this.parent.dialect.ignoreInProject(f.fieldDef.name) &&
1228
+ (!filter || filter(f))) {
1229
+ // fieldNames.push(`${struct.getFullOutputName()}${name}`);
1230
+ fieldNames.push(f.getFullOutputName());
1231
+ }
1232
+ else if (f instanceof QueryStruct && expandChildren) {
1233
+ structs.push(f);
1234
+ }
1235
+ }
1236
+ for (const s of structs) {
1237
+ fieldNames = fieldNames.concat(this.expandWildCardStruct(s, expandChildren, filter));
1238
+ }
1239
+ return fieldNames;
1240
+ }
1241
+ // Do any '*' expansion.
1242
+ expandWildCards(fields, filter = undefined) {
1243
+ let ret = [];
1244
+ for (const f of fields) {
1245
+ if (typeof f !== "string") {
1246
+ ret.push(f);
1247
+ }
1248
+ else {
1249
+ const fieldName = f;
1250
+ const path = fieldName.split(".");
1251
+ if (!path[path.length - 1].startsWith("*")) {
1252
+ ret.push(f);
1253
+ }
1254
+ else {
1255
+ const expandChildren = path.pop() === "**";
1256
+ let struct = this.parent;
1257
+ let pathElementName;
1258
+ while (path.length > 0 && (pathElementName = path.shift())) {
1259
+ const structNode = struct.getChildByName(pathElementName);
1260
+ if (structNode === undefined) {
1261
+ throw new Error(`Nested explore not found '${pathElementName}'`);
1262
+ }
1263
+ if (structNode instanceof QueryStruct) {
1264
+ struct = structNode;
1265
+ }
1266
+ else {
1267
+ throw new Error(`'${pathElementName}' is not an explore object`);
1268
+ }
1269
+ }
1270
+ ret = ret.concat(this.expandWildCardStruct(struct, expandChildren, filter));
1271
+ }
1272
+ }
1273
+ }
1274
+ return ret;
1275
+ }
1276
+ addDependantPath(resultStruct, context, path, mayNeedUniqueKey, joinStack) {
1277
+ const node = context.getFieldByName(path);
1278
+ let struct;
1279
+ if (node instanceof QueryField) {
1280
+ struct = node.parent;
1281
+ }
1282
+ else if (node instanceof QueryStruct) {
1283
+ struct = node;
1284
+ }
1285
+ else {
1286
+ throw new Error(`Internal Error: Unknown object type`);
1287
+ }
1288
+ resultStruct
1289
+ .root()
1290
+ .addStructToJoin(struct.getJoinableParent(), this, mayNeedUniqueKey, joinStack);
1291
+ }
1292
+ addDependantExpr(resultStruct, context, e, joinStack) {
1293
+ for (const expr of e) {
1294
+ if ((0, malloy_types_1.isUngroupFragment)(expr)) {
1295
+ resultStruct.resultUsesUngrouped = true;
1296
+ resultStruct.root().isComplexQuery = true;
1297
+ resultStruct.root().queryUsesPartitioning = true;
1298
+ if (expr.fields && expr.fields.length > 0) {
1299
+ const key = expr.fields.sort().join("|") + expr.type;
1300
+ if (resultStruct.ungroupedSets.get(key) === undefined) {
1301
+ resultStruct.ungroupedSets.set(key, {
1302
+ type: expr.type,
1303
+ fields: expr.fields,
1304
+ groupSet: -1,
1305
+ });
1306
+ }
1307
+ }
1308
+ this.addDependantExpr(resultStruct, context, expr.e, joinStack);
1309
+ }
1310
+ else if ((0, malloy_types_1.isFieldFragment)(expr)) {
1311
+ const field = context.getDimensionOrMeasureByName(expr.path);
1312
+ if ((0, malloy_types_1.hasExpression)(field.fieldDef)) {
1313
+ this.addDependantExpr(resultStruct, field.parent, field.fieldDef.e, joinStack);
1314
+ }
1315
+ else {
1316
+ resultStruct
1317
+ .root()
1318
+ .addStructToJoin(field.parent.getJoinableParent(), this, false, joinStack);
1319
+ // this.addDependantPath(resultStruct, field.parent, expr.path, false);
1320
+ }
1321
+ }
1322
+ else if ((0, malloy_types_1.isFilterFragment)(expr)) {
1323
+ for (const filterCond of expr.filterList) {
1324
+ this.addDependantExpr(resultStruct, context, filterCond.expression, joinStack);
1325
+ }
1326
+ }
1327
+ else if ((0, malloy_types_1.isDialectFragment)(expr)) {
1328
+ const expressions = [];
1329
+ switch (expr.function) {
1330
+ case "now":
1331
+ break;
1332
+ case "div":
1333
+ expressions.push(expr.denominator);
1334
+ expressions.push(expr.numerator);
1335
+ break;
1336
+ case "timeLiteral":
1337
+ break;
1338
+ case "stringLiteral":
1339
+ break;
1340
+ case "timeDiff":
1341
+ expressions.push(expr.left.value, expr.right.value);
1342
+ break;
1343
+ case "delta":
1344
+ expressions.push(expr.base.value, expr.delta);
1345
+ break;
1346
+ case "trunc":
1347
+ case "extract":
1348
+ expressions.push(expr.expr.value);
1349
+ break;
1350
+ case "regexpMatch":
1351
+ case "cast":
1352
+ expressions.push(expr.expr);
1353
+ break;
1354
+ default:
1355
+ throw new Error("Unknown dialect Fragment type. Can't generate dependancies");
1356
+ }
1357
+ for (const e of expressions) {
1358
+ this.addDependantExpr(resultStruct, context, e, joinStack);
1359
+ }
1360
+ }
1361
+ else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
1362
+ if ((0, malloy_types_1.isAsymmetricFragment)(expr)) {
1363
+ if (expr.structPath) {
1364
+ this.addDependantPath(resultStruct, context, expr.structPath, true, joinStack);
1365
+ }
1366
+ else {
1367
+ // we are doing a sum in the root. It may need symetric aggregates
1368
+ resultStruct.addStructToJoin(context, this, true, joinStack);
1369
+ }
1370
+ }
1371
+ this.addDependantExpr(resultStruct, context, expr.e, joinStack);
1372
+ }
1373
+ else if ((0, malloy_types_1.isAnalyticFragment)(expr)) {
1374
+ resultStruct.root().queryUsesPartitioning = true;
1375
+ }
1376
+ }
1377
+ }
1378
+ addDependancies(resultStruct, field) {
1379
+ if ((0, malloy_types_1.hasExpression)(field.fieldDef)) {
1380
+ this.addDependantExpr(resultStruct, field.parent, field.fieldDef.e, []);
1381
+ }
1382
+ }
1383
+ expandFields(resultStruct) {
1384
+ let resultIndex = 1;
1385
+ for (const f of this.expandWildCards(resultStruct.firstSegment.fields)) {
1386
+ const { as, field } = this.expandField(f);
1387
+ if (field instanceof QueryTurtle || field instanceof QueryQuery) {
1388
+ if (this.firstSegment.type === "project") {
1389
+ throw new Error(`Turtled Queries cannot be used in PROJECT - '${field.fieldDef.name}'`);
1390
+ }
1391
+ const fir = new FieldInstanceResult(field.fieldDef, resultStruct);
1392
+ this.expandFields(fir);
1393
+ resultStruct.add(as, fir);
1394
+ }
1395
+ else if (field instanceof QueryAtomicField) {
1396
+ resultStruct.addField(as, field, {
1397
+ resultIndex,
1398
+ type: "result",
1399
+ });
1400
+ this.addDependancies(resultStruct, field);
1401
+ if (isCalculatedField(field)) {
1402
+ if (this.firstSegment.type === "project") {
1403
+ throw new Error(`Aggregate Fields cannot be used in PROJECT - '${field.fieldDef.name}'`);
1404
+ }
1405
+ }
1406
+ // } else if (field instanceof QueryStruct) {
1407
+ // // this could probably be optimized. We are adding the primary key of the joined structure
1408
+ // // instead of the foreignKey. We have to do this in at least the INNER join case
1409
+ // // so i'm just going to let the SQL database do the optimization (which is pretty rudimentary)
1410
+ // const pkFieldDef = field.getAsQueryField();
1411
+ // resultStruct.addField(as, pkFieldDef, {
1412
+ // resultIndex,
1413
+ // type: "result",
1414
+ // });
1415
+ // resultStruct.addStructToJoin(field, false);
1416
+ }
1417
+ // else if (
1418
+ // this.firstSegment.type === "project" &&
1419
+ // field instanceof QueryStruct
1420
+ // ) {
1421
+ // // TODO lloyd refactor or comment why we do nothing here
1422
+ // } else {
1423
+ // throw new Error(`'${as}' cannot be used as in this way.`);
1424
+ // }
1425
+ resultIndex++;
1426
+ }
1427
+ this.expandFilters(resultStruct);
1428
+ }
1429
+ expandFilters(resultStruct) {
1430
+ if (resultStruct.firstSegment.filterList === undefined) {
1431
+ return;
1432
+ }
1433
+ // Go through the filters and make or find dependant fields
1434
+ // add them to the field index. Place the individual filters
1435
+ // in the correct catgory.
1436
+ for (const cond of resultStruct.firstSegment.filterList || []) {
1437
+ const context = this.parent;
1438
+ this.addDependantExpr(resultStruct, context, cond.expression, []);
1439
+ }
1440
+ for (const join of resultStruct.root().joins.values() || []) {
1441
+ for (const qf of join.joinFilterConditions || []) {
1442
+ if (qf.fieldDef.type === "boolean" && qf.fieldDef.e) {
1443
+ this.addDependantExpr(resultStruct, qf.parent, qf.fieldDef.e, []);
1444
+ }
1445
+ }
1446
+ }
1447
+ }
1448
+ generateSQLFilters(resultStruct, which, filterList = undefined) {
1449
+ const resultFilters = new utils_1.AndChain();
1450
+ const list = filterList || resultStruct.firstSegment.filterList;
1451
+ if (list === undefined) {
1452
+ return resultFilters;
1453
+ }
1454
+ // Go through the filters and make or find dependant fields
1455
+ // add them to the field index. Place the individual filters
1456
+ // in the correct catgory.
1457
+ for (const cond of list || []) {
1458
+ const context = this.parent;
1459
+ if ((which === "having" && (0, malloy_types_1.expressionIsCalculation)(cond.expressionType)) ||
1460
+ (which === "where" && cond.expressionType === "scalar")) {
1461
+ const sqlClause = this.generateExpressionFromExpr(resultStruct, context, cond.expression, undefined);
1462
+ resultFilters.add(sqlClause);
1463
+ }
1464
+ }
1465
+ return resultFilters;
1466
+ }
1467
+ prepare(_stageWriter) {
1468
+ if (!this.prepared) {
1469
+ this.expandFields(this.rootResult);
1470
+ this.rootResult.findJoins(this);
1471
+ this.rootResult.calculateSymmetricAggregates();
1472
+ this.prepared = true;
1473
+ }
1474
+ }
1475
+ // get the source fieldname and filters associated with the field (so we can drill later)
1476
+ getResultMetadata(fi) {
1477
+ if (fi instanceof FieldInstanceField) {
1478
+ if (fi.fieldUsage.type === "result") {
1479
+ const fieldDef = fi.f.fieldDef;
1480
+ let filterList;
1481
+ const sourceField = fi.f.parent.getFullOutputName() + (fieldDef.name || fieldDef.as);
1482
+ const sourceExpression = fieldDef.code;
1483
+ const sourceClasses = [sourceField];
1484
+ if (isCalculatedField(fi.f)) {
1485
+ filterList = fi.f.getFilterList();
1486
+ return {
1487
+ sourceField,
1488
+ sourceExpression,
1489
+ filterList,
1490
+ sourceClasses,
1491
+ fieldKind: "measure",
1492
+ };
1493
+ }
1494
+ if (isScalarField(fi.f)) {
1495
+ return {
1496
+ sourceField,
1497
+ sourceExpression,
1498
+ filterList,
1499
+ sourceClasses,
1500
+ fieldKind: "dimension",
1501
+ };
1502
+ }
1503
+ else {
1504
+ return undefined;
1505
+ }
1506
+ }
1507
+ return undefined;
1508
+ }
1509
+ else if (fi instanceof FieldInstanceResult) {
1510
+ const sourceField = fi.turtleDef.name || fi.turtleDef.as;
1511
+ const sourceClasses = sourceField ? [sourceField] : [];
1512
+ const filterList = fi.firstSegment.filterList;
1513
+ const limit = fi.turtleDef.pipeline[fi.turtleDef.pipeline.length - 1].limit;
1514
+ if (sourceField) {
1515
+ return {
1516
+ sourceField,
1517
+ filterList,
1518
+ sourceClasses,
1519
+ fieldKind: "struct",
1520
+ limit,
1521
+ };
1522
+ }
1523
+ }
1524
+ return undefined;
1525
+ }
1526
+ /** returns a fields and primary key of a struct for this query */
1527
+ getResultStructDef(resultStruct = this.rootResult, isRoot = true) {
1528
+ const fields = [];
1529
+ let primaryKey;
1530
+ this.prepare(undefined);
1531
+ let dimCount = 0;
1532
+ for (const [name, fi] of resultStruct.allFields) {
1533
+ const resultMetadata = this.getResultMetadata(fi);
1534
+ if (fi instanceof FieldInstanceResult) {
1535
+ const { structDef } = this.generateTurtlePipelineSQL(fi, new StageWriter(true, undefined), "<nosource>");
1536
+ // LTNOTE: This is probably broken now. Need to look at the last stage
1537
+ // to figure out the resulting nested/inline state...
1538
+ const resultType = fi.getRepeatedResultType() === "nested" ? "nested" : "inline";
1539
+ structDef.name = name;
1540
+ structDef.structRelationship = {
1541
+ field: name,
1542
+ type: resultType,
1543
+ isArray: false,
1544
+ };
1545
+ structDef.structSource = { type: resultType };
1546
+ structDef.resultMetadata = resultMetadata;
1547
+ fields.push(structDef);
1548
+ }
1549
+ else if (fi instanceof FieldInstanceField) {
1550
+ if (fi.fieldUsage.type === "result") {
1551
+ if (fi.f instanceof QueryFieldStruct) {
1552
+ fields.push(fi.f.getAsJoinedStructDef(name));
1553
+ }
1554
+ // if there is only one dimension, it is the primaryKey
1555
+ // if there are more, primaryKey is undefined.
1556
+ if (isScalarField(fi.f)) {
1557
+ if (dimCount === 0 && isRoot) {
1558
+ primaryKey = name;
1559
+ }
1560
+ else {
1561
+ primaryKey = undefined;
1562
+ }
1563
+ dimCount++;
1564
+ }
1565
+ const location = fi.f.fieldDef.location;
1566
+ // build out the result fields...
1567
+ switch (fi.f.fieldDef.type) {
1568
+ case "boolean":
1569
+ case "json":
1570
+ case "string":
1571
+ fields.push({
1572
+ name,
1573
+ type: fi.f.fieldDef.type,
1574
+ resultMetadata,
1575
+ location,
1576
+ });
1577
+ break;
1578
+ case "timestamp": {
1579
+ const timeframe = fi.f.fieldDef.timeframe;
1580
+ switch (timeframe) {
1581
+ case "year":
1582
+ case "month":
1583
+ case "week":
1584
+ case "quarter":
1585
+ case "day":
1586
+ fields.push({
1587
+ name,
1588
+ type: "date",
1589
+ timeframe,
1590
+ resultMetadata,
1591
+ location,
1592
+ });
1593
+ break;
1594
+ case "second":
1595
+ case "minute":
1596
+ case "hour":
1597
+ fields.push({
1598
+ name,
1599
+ type: "timestamp",
1600
+ timeframe,
1601
+ resultMetadata,
1602
+ location,
1603
+ });
1604
+ break;
1605
+ default:
1606
+ fields.push({
1607
+ name,
1608
+ type: "timestamp",
1609
+ resultMetadata,
1610
+ location,
1611
+ });
1612
+ break;
1613
+ }
1614
+ break;
1615
+ }
1616
+ case "date": {
1617
+ fields.push({
1618
+ name,
1619
+ type: fi.f.fieldDef.type,
1620
+ timeframe: fi.f.fieldDef.timeframe,
1621
+ resultMetadata,
1622
+ location,
1623
+ });
1624
+ break;
1625
+ }
1626
+ case "number":
1627
+ fields.push({
1628
+ name,
1629
+ numberType: fi.f.fieldDef.numberType,
1630
+ type: "number",
1631
+ resultMetadata,
1632
+ location,
1633
+ });
1634
+ break;
1635
+ default:
1636
+ throw new Error(`unknown Field Type in query ${JSON.stringify(fi.f.fieldDef)}`);
1637
+ }
1638
+ }
1639
+ }
1640
+ }
1641
+ return {
1642
+ fields,
1643
+ name: this.resultStage || "result",
1644
+ dialect: this.parent.dialect.name,
1645
+ primaryKey,
1646
+ structRelationship: {
1647
+ type: "basetable",
1648
+ connectionName: this.parent.connectionName,
1649
+ },
1650
+ structSource: { type: "query_result" },
1651
+ resultMetadata: this.getResultMetadata(this.rootResult),
1652
+ type: "struct",
1653
+ };
1654
+ }
1655
+ generateSQLJoinBlock(stageWriter, ji) {
1656
+ let s = "";
1657
+ const qs = ji.queryStruct;
1658
+ const structRelationship = qs.fieldDef.structRelationship;
1659
+ let structSQL = qs.structSourceSQL(stageWriter);
1660
+ if ((0, malloy_types_1.isJoinOn)(structRelationship)) {
1661
+ if (ji.makeUniqueKey) {
1662
+ structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as __distinct_key, * FROM ${structSQL})`;
1663
+ }
1664
+ let onCondition = "";
1665
+ if (qs.parent === undefined) {
1666
+ throw new Error("Expected joined struct to have a parent.");
1667
+ }
1668
+ if (structRelationship.onExpression) {
1669
+ onCondition = new QueryFieldBoolean({
1670
+ type: "boolean",
1671
+ name: "ignoreme",
1672
+ e: structRelationship.onExpression,
1673
+ }, qs.parent).generateExpression(this.rootResult);
1674
+ }
1675
+ else {
1676
+ onCondition = "1=1";
1677
+ }
1678
+ let filters = "";
1679
+ let conditions = undefined;
1680
+ if (ji.joinFilterConditions) {
1681
+ conditions = ji.joinFilterConditions.map((qf) => qf.generateExpression(this.rootResult));
1682
+ }
1683
+ if (ji.children.length === 0 || conditions === undefined) {
1684
+ if (conditions !== undefined && conditions.length >= 1) {
1685
+ filters = ` AND (${conditions.join(" AND ")})`;
1686
+ }
1687
+ s += `LEFT JOIN ${structSQL} AS ${ji.alias}\n ON ${onCondition}${filters}\n`;
1688
+ }
1689
+ else {
1690
+ let select = `SELECT ${ji.alias}.*`;
1691
+ let joins = "";
1692
+ for (const childJoin of ji.children) {
1693
+ joins += this.generateSQLJoinBlock(stageWriter, childJoin);
1694
+ const physicalFields = (0, malloy_types_1.getPhysicalFields)(childJoin.queryStruct.fieldDef).map((fieldDef) => this.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name));
1695
+ select += `, ${this.parent.dialect.sqlSelectAliasAsStruct(childJoin.alias, physicalFields)} AS ${childJoin.alias}`;
1696
+ }
1697
+ select += `\nFROM ${structSQL} AS ${ji.alias}\n${joins}\nWHERE ${conditions === null || conditions === void 0 ? void 0 : conditions.join(" AND ")}\n`;
1698
+ s += `LEFT JOIN (\n${(0, utils_1.indent)(select)}) AS ${ji.alias}\n ON ${onCondition}\n`;
1699
+ return s;
1700
+ }
1701
+ }
1702
+ else if (structRelationship.type === "nested") {
1703
+ if (qs.parent === undefined || ji.parent === undefined) {
1704
+ throw new Error("Internal Error, nested structure with no parent.");
1705
+ }
1706
+ const fieldExpression = this.parent.dialect.sqlFieldReference(qs.parent.getSQLIdentifier(), structRelationship.field, "struct", qs.parent.fieldDef.structRelationship.type === "nested", this.parent.fieldDef.structRelationship.type === "nested" &&
1707
+ this.parent.fieldDef.structRelationship.isArray);
1708
+ // we need to generate primary key. If parent has a primary key combine
1709
+ s += `${this.parent.dialect.sqlUnnestAlias(fieldExpression, ji.alias, ji.getDialectFieldList(), ji.makeUniqueKey, structRelationship.isArray)}\n`;
1710
+ }
1711
+ else if (structRelationship.type === "inline") {
1712
+ throw new Error("Internal Error: inline structs should never appear in join trees");
1713
+ }
1714
+ else {
1715
+ throw new Error(`Join type not implemented ${JSON.stringify(qs.fieldDef.structRelationship)}`);
1716
+ }
1717
+ for (const childJoin of ji.children) {
1718
+ s += this.generateSQLJoinBlock(stageWriter, childJoin);
1719
+ }
1720
+ return s;
1721
+ }
1722
+ generateSQLJoins(stageWriter) {
1723
+ let s = "";
1724
+ // get the first value from the map (weird, I know)
1725
+ const [[, ji]] = this.rootResult.joins;
1726
+ const qs = ji.queryStruct;
1727
+ // Joins
1728
+ let structSQL = qs.structSourceSQL(stageWriter);
1729
+ if ((0, malloy_types_1.isIndexSegment)(this.firstSegment)) {
1730
+ structSQL = this.parent.dialect.sqlSampleTable(structSQL, this.firstSegment.sample);
1731
+ if (this.firstSegment.sample) {
1732
+ structSQL = stageWriter.addStage(`SELECT * from ${structSQL} as x limit 100000 `);
1733
+ }
1734
+ }
1735
+ const structRelationship = qs.fieldDef.structRelationship;
1736
+ if (structRelationship.type === "basetable") {
1737
+ if (ji.makeUniqueKey) {
1738
+ structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as __distinct_key, * FROM ${structSQL} as x)`;
1739
+ }
1740
+ s += `FROM ${structSQL} as ${this.parent.getIdentifier()}\n`;
1741
+ }
1742
+ else {
1743
+ throw new Error("Internal Error, queries must start from a basetable");
1744
+ }
1745
+ for (const childJoin of ji.children) {
1746
+ s += this.generateSQLJoinBlock(stageWriter, childJoin);
1747
+ }
1748
+ return s;
1749
+ }
1750
+ genereateSQLOrderBy(queryDef, resultStruct) {
1751
+ let s = "";
1752
+ if (this.firstSegment.type === "project" && !queryDef.orderBy) {
1753
+ return ""; // No default ordering for project.
1754
+ }
1755
+ const orderBy = queryDef.orderBy || resultStruct.calculateDefaultOrderBy();
1756
+ const o = [];
1757
+ for (const f of orderBy) {
1758
+ if (typeof f.field === "string") {
1759
+ // convert name to an index
1760
+ const fi = resultStruct.getField(f.field);
1761
+ if (fi && fi.fieldUsage.type === "result") {
1762
+ o.push(`${fi.fieldUsage.resultIndex} ${f.dir || "ASC"}`);
1763
+ }
1764
+ else {
1765
+ throw new Error(`Unknown field in ORDER BY ${f.field}`);
1766
+ }
1767
+ }
1768
+ else {
1769
+ o.push(`${f.field} ${f.dir || "ASC"}`);
1770
+ }
1771
+ }
1772
+ if (o.length > 0) {
1773
+ s = this.parent.dialect.sqlOrderBy(o) + `\n`;
1774
+ }
1775
+ return s;
1776
+ }
1777
+ generateSimpleSQL(stageWriter) {
1778
+ let s = "";
1779
+ s += "SELECT \n";
1780
+ const fields = [];
1781
+ for (const [name, field] of this.rootResult.allFields) {
1782
+ const fi = field;
1783
+ const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
1784
+ if (fi.fieldUsage.type === "result") {
1785
+ fields.push(` ${fi.f.generateExpression(this.rootResult)} as ${sqlName}`);
1786
+ }
1787
+ }
1788
+ s += (0, utils_1.indent)(fields.join(",\n")) + "\n";
1789
+ s += this.generateSQLJoins(stageWriter);
1790
+ s += this.generateSQLFilters(this.rootResult, "where").sql("where");
1791
+ // group by
1792
+ if (this.firstSegment.type === "reduce") {
1793
+ const n = [];
1794
+ for (const field of this.rootResult.fields()) {
1795
+ const fi = field;
1796
+ if (fi.fieldUsage.type === "result" && isScalarField(fi.f)) {
1797
+ n.push(fi.fieldUsage.resultIndex.toString());
1798
+ }
1799
+ }
1800
+ if (n.length > 0) {
1801
+ s += `GROUP BY ${n.join(",")}\n`;
1802
+ }
1803
+ }
1804
+ s += this.generateSQLFilters(this.rootResult, "having").sql("having");
1805
+ // order by
1806
+ s += this.genereateSQLOrderBy(this.firstSegment, this.rootResult);
1807
+ // limit
1808
+ if (this.firstSegment.limit) {
1809
+ s += `LIMIT ${this.firstSegment.limit}\n`;
1810
+ }
1811
+ this.resultStage = stageWriter.addStage(s);
1812
+ return this.resultStage;
1813
+ }
1814
+ // This probably should be generated in a dialect independat way.
1815
+ // but for now, it is just googleSQL.
1816
+ generatePipelinedStages(outputPipelinedSQL, lastStageName, stageWriter) {
1817
+ if (outputPipelinedSQL.length === 0) {
1818
+ return lastStageName;
1819
+ }
1820
+ const pipelinesSQL = outputPipelinedSQL
1821
+ .map((o) => `${o.pipelineSQL} as ${o.sqlFieldName}
1822
+ `)
1823
+ .join(",\n");
1824
+ return stageWriter.addStage(`SELECT * replace (${pipelinesSQL}) FROM ${lastStageName}
1825
+ `);
1826
+ }
1827
+ generateStage0Fields(resultSet, output, stageWriter) {
1828
+ for (const [name, fi] of resultSet.allFields) {
1829
+ const outputName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
1830
+ if (fi instanceof FieldInstanceField) {
1831
+ if (fi.fieldUsage.type === "result") {
1832
+ const exp = fi.getSQL();
1833
+ if (isScalarField(fi.f)) {
1834
+ if (this.parent.dialect.name === "standardsql" &&
1835
+ this.rootResult.queryUsesPartitioning) {
1836
+ // BigQuery can't partition aggregate function except when the field has no
1837
+ // expression. Additionally it can't partition by floats. We stuff expressions
1838
+ // and numbers as strings into a lateral join when the query has ungrouped expressions
1839
+ if (fi.f.fieldDef.type === "number") {
1840
+ // make an extra dimension as a string
1841
+ output.sql.push(`${exp} as ${outputName}`);
1842
+ const outputFieldName = `__lateral_join_bag.${outputName}_string`;
1843
+ output.sql.push(outputFieldName);
1844
+ output.dimensionIndexes.push(output.fieldIndex++);
1845
+ output.lateralJoinSQLExpressions.push(`CAST(${exp} as STRING) as ${outputName}_string`);
1846
+ fi.partitionSQL = outputFieldName;
1847
+ }
1848
+ else {
1849
+ const outputFieldName = `__lateral_join_bag.${outputName}`;
1850
+ fi.partitionSQL = outputFieldName;
1851
+ output.lateralJoinSQLExpressions.push(`${exp} as ${outputName}`);
1852
+ output.sql.push(outputFieldName);
1853
+ }
1854
+ }
1855
+ else {
1856
+ // just treat it like a regular field.
1857
+ output.sql.push(`${exp} as ${outputName}`);
1858
+ }
1859
+ output.dimensionIndexes.push(output.fieldIndex++);
1860
+ }
1861
+ else if (isCalculatedField(fi.f)) {
1862
+ output.sql.push(`${exp} as ${outputName}`);
1863
+ output.fieldIndex++;
1864
+ }
1865
+ }
1866
+ }
1867
+ else if (fi instanceof FieldInstanceResult) {
1868
+ if (fi.firstSegment.type === "reduce") {
1869
+ this.generateStage0Fields(fi, output, stageWriter);
1870
+ }
1871
+ else if (fi.firstSegment.type === "project") {
1872
+ const s = this.generateTurtleSQL(fi, stageWriter, outputName, output.outputPipelinedSQL);
1873
+ output.sql.push(`${s} as ${outputName}`);
1874
+ output.fieldIndex++;
1875
+ }
1876
+ }
1877
+ }
1878
+ // LTNOTE: we could optimize here in the future.
1879
+ // leaf turtles can have their having clauses in the main query
1880
+ // turtles with leaves need to promote their state to their
1881
+ // children.
1882
+ const having = this.generateSQLFilters(resultSet, "having");
1883
+ if (!having.empty()) {
1884
+ // if we have no children, the having can run at the root level
1885
+ if (resultSet.childGroups.length === 1) {
1886
+ resultSet
1887
+ .root()
1888
+ .havings.add(`(group_set<>${resultSet.groupSet} OR (group_set=${resultSet.groupSet} AND ${having.sql()}))`);
1889
+ }
1890
+ else {
1891
+ resultSet.hasHaving = true;
1892
+ output.sql.push(`CASE WHEN group_set=${resultSet.groupSet} THEN CASE WHEN ${having.sql()} THEN 0 ELSE 1 END END as __delete__${resultSet.groupSet}`);
1893
+ output.fieldIndex++;
1894
+ }
1895
+ }
1896
+ }
1897
+ generateSQLWhereChildren(resultStruct) {
1898
+ const wheres = new utils_1.AndChain();
1899
+ for (const [, field] of resultStruct.allFields) {
1900
+ if (field.type === "query") {
1901
+ const fir = field;
1902
+ const turtleWhere = this.generateSQLFilters(fir, "where");
1903
+ if (turtleWhere.present()) {
1904
+ const groupSets = fir.childGroups.join(",");
1905
+ wheres.add(`(group_set NOT IN (${groupSets})` +
1906
+ ` OR (group_set IN (${groupSets}) AND ${turtleWhere.sql()}))`);
1907
+ }
1908
+ wheres.addChain(this.generateSQLWhereChildren(fir));
1909
+ }
1910
+ }
1911
+ return wheres;
1912
+ }
1913
+ generateSQLWhereTurtled() {
1914
+ const wheres = this.generateSQLFilters(this.rootResult, "where");
1915
+ wheres.addChain(this.generateSQLWhereChildren(this.rootResult));
1916
+ return wheres.sql("where");
1917
+ }
1918
+ // iterate over the nested queries looking for Havings (and someday limits).
1919
+ // if you find any, generate a new stage(s) to perform these functions.
1920
+ generateSQLHavingLimit(stageWriter, lastStageName) {
1921
+ const fields = [];
1922
+ const resultsWithHaving = this.rootResult.selectStructs([], (result) => result.hasHaving);
1923
+ if (resultsWithHaving.length > 0) {
1924
+ for (const result of resultsWithHaving) {
1925
+ // find all the parent dimension names.
1926
+ const dimensions = [];
1927
+ let r = result;
1928
+ while (r) {
1929
+ for (const name of r.fieldNames((fi) => isScalarField(fi.f))) {
1930
+ dimensions.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${r.groupSet}`));
1931
+ }
1932
+ r = r.parent;
1933
+ }
1934
+ fields.push(`MAX(CASE WHEN group_set IN (${result.childGroups.join(",")}) THEN __delete__${result.groupSet} END) OVER(partition by ${dimensions
1935
+ .map((x) => `CAST(${x} AS ${this.parent.dialect.stringTypeName}) `)
1936
+ .join(",")}) as __shaving__${result.groupSet}`);
1937
+ }
1938
+ }
1939
+ if (resultsWithHaving.length > 0) {
1940
+ lastStageName = stageWriter.addStage(`SELECT\n *,\n ${fields.join(",\n ")} \nFROM ${lastStageName}`);
1941
+ const havings = new utils_1.AndChain();
1942
+ for (const result of resultsWithHaving) {
1943
+ havings.add(`group_set IN (${result.childGroups.join(",")}) AND __shaving__${result.groupSet}=1`);
1944
+ }
1945
+ lastStageName = stageWriter.addStage(`SELECT *\nFROM ${lastStageName}\nWHERE NOT (${havings.sqlOr()})`);
1946
+ }
1947
+ return lastStageName;
1948
+ }
1949
+ generateSQLStage0(stageWriter) {
1950
+ let s = "SELECT\n";
1951
+ let from = this.generateSQLJoins(stageWriter);
1952
+ const wheres = this.generateSQLWhereTurtled();
1953
+ const f = {
1954
+ dimensionIndexes: [1],
1955
+ fieldIndex: 2,
1956
+ sql: ["group_set"],
1957
+ lateralJoinSQLExpressions: [],
1958
+ groupsAggregated: [],
1959
+ outputPipelinedSQL: [],
1960
+ };
1961
+ this.generateStage0Fields(this.rootResult, f, stageWriter);
1962
+ if (this.firstSegment.type === "project") {
1963
+ throw new Error("PROJECT cannot be used on queries with turtles");
1964
+ }
1965
+ const groupBy = "GROUP BY " + f.dimensionIndexes.join(",") + "\n";
1966
+ from += this.parent.dialect.sqlGroupSetTable(this.maxGroupSet) + "\n";
1967
+ s += (0, utils_1.indent)(f.sql.join(",\n")) + "\n";
1968
+ // this should only happen on standard SQL, BigQuery can't partition by expressions and
1969
+ // aggregates.
1970
+ if (f.lateralJoinSQLExpressions.length > 0) {
1971
+ from += `LEFT JOIN UNNEST([STRUCT(${f.lateralJoinSQLExpressions.join(",\n")})]) as __lateral_join_bag\n`;
1972
+ }
1973
+ s += from + wheres + groupBy + this.rootResult.havings.sql("having");
1974
+ // generate the stage
1975
+ const resultStage = stageWriter.addStage(s);
1976
+ // generate stages for havings and limits
1977
+ this.resultStage = this.generateSQLHavingLimit(stageWriter, resultStage);
1978
+ this.resultStage = this.generatePipelinedStages(f.outputPipelinedSQL, this.resultStage, stageWriter);
1979
+ return this.resultStage;
1980
+ }
1981
+ generateDepthNFields(depth, resultSet, output, stageWriter) {
1982
+ const groupsToMap = [];
1983
+ for (const [name, fi] of resultSet.allFields) {
1984
+ const sqlFieldName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
1985
+ if (fi instanceof FieldInstanceField) {
1986
+ if (fi.fieldUsage.type === "result") {
1987
+ if (isScalarField(fi.f)) {
1988
+ const exp = this.caseGroup(resultSet.groupSet > 0 ? resultSet.childGroups : [], sqlFieldName);
1989
+ output.sql.push(`${exp} as ${sqlFieldName}`);
1990
+ output.dimensionIndexes.push(output.fieldIndex++);
1991
+ }
1992
+ else if (isCalculatedField(fi.f)) {
1993
+ const exp = this.parent.dialect.sqlAnyValue(resultSet.groupSet, sqlFieldName);
1994
+ output.sql.push(`${exp} as ${sqlFieldName}`);
1995
+ output.fieldIndex++;
1996
+ }
1997
+ }
1998
+ }
1999
+ else if (fi instanceof FieldInstanceResult) {
2000
+ if (fi.depth > depth) {
2001
+ // ignore it, we've already dealt with it.
2002
+ }
2003
+ else if (fi.depth === depth) {
2004
+ const s = this.generateTurtleSQL(fi, stageWriter, sqlFieldName, output.outputPipelinedSQL);
2005
+ output.groupsAggregated.push({
2006
+ fromGroup: fi.groupSet,
2007
+ toGroup: resultSet.groupSet,
2008
+ });
2009
+ groupsToMap.push(fi.groupSet);
2010
+ output.sql.push(`${s} as ${sqlFieldName}`);
2011
+ output.fieldIndex++;
2012
+ }
2013
+ else {
2014
+ this.generateDepthNFields(depth, fi, output, stageWriter);
2015
+ }
2016
+ }
2017
+ }
2018
+ if (output.groupsAggregated.length > 0) {
2019
+ output.sql[0] = `CASE `;
2020
+ for (const m of output.groupsAggregated) {
2021
+ output.sql[0] += `WHEN group_set=${m.fromGroup} THEN ${m.toGroup} `;
2022
+ }
2023
+ output.sql[0] += ` ELSE group_set END as group_set`;
2024
+ }
2025
+ }
2026
+ generateSQLDepthN(depth, stageWriter, stageName) {
2027
+ let s = "SELECT \n";
2028
+ const f = {
2029
+ dimensionIndexes: [1],
2030
+ fieldIndex: 2,
2031
+ sql: ["group_set"],
2032
+ lateralJoinSQLExpressions: [],
2033
+ groupsAggregated: [],
2034
+ outputPipelinedSQL: [],
2035
+ };
2036
+ this.generateDepthNFields(depth, this.rootResult, f, stageWriter);
2037
+ s += (0, utils_1.indent)(f.sql.join(",\n")) + "\n";
2038
+ s += `FROM ${stageName}\n`;
2039
+ const where = this.rootResult.eliminateComputeGroupsSQL();
2040
+ if (where.length > 0) {
2041
+ s += `WHERE ${where}\n`;
2042
+ }
2043
+ if (f.dimensionIndexes.length > 0) {
2044
+ s += `GROUP BY ${f.dimensionIndexes.join(",")}\n`;
2045
+ }
2046
+ this.resultStage = stageWriter.addStage(s);
2047
+ this.resultStage = this.generatePipelinedStages(f.outputPipelinedSQL, this.resultStage, stageWriter);
2048
+ return this.resultStage;
2049
+ }
2050
+ genereateSQLCombineTurtles(stageWriter, stage0Name) {
2051
+ let s = "SELECT\n";
2052
+ const fieldsSQL = [];
2053
+ let fieldIndex = 1;
2054
+ const outputPipelinedSQL = [];
2055
+ const dimensionIndexes = [];
2056
+ for (const [name, fi] of this.rootResult.allFields) {
2057
+ const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
2058
+ if (fi instanceof FieldInstanceField) {
2059
+ if (fi.fieldUsage.type === "result") {
2060
+ if (isScalarField(fi.f)) {
2061
+ fieldsSQL.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
2062
+ dimensionIndexes.push(fieldIndex++);
2063
+ }
2064
+ else if (isCalculatedField(fi.f)) {
2065
+ fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(name, this.rootResult.groupSet, sqlName));
2066
+ fieldIndex++;
2067
+ }
2068
+ }
2069
+ }
2070
+ else if (fi instanceof FieldInstanceResult) {
2071
+ if (fi.firstSegment.type === "reduce") {
2072
+ fieldsSQL.push(`${this.generateTurtleSQL(fi, stageWriter, sqlName, outputPipelinedSQL)} as ${sqlName}`);
2073
+ fieldIndex++;
2074
+ }
2075
+ else if (fi.firstSegment.type === "project") {
2076
+ fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(name, this.rootResult.groupSet, sqlName));
2077
+ fieldIndex++;
2078
+ }
2079
+ }
2080
+ }
2081
+ s += (0, utils_1.indent)(fieldsSQL.join(",\n")) + `\nFROM ${stage0Name}\n`;
2082
+ const where = this.rootResult.eliminateComputeGroupsSQL();
2083
+ if (where.length > 0) {
2084
+ s += `WHERE ${where}\n`;
2085
+ }
2086
+ if (dimensionIndexes.length > 0) {
2087
+ s += `GROUP BY ${dimensionIndexes.join(",")}\n`;
2088
+ }
2089
+ // order by
2090
+ s += this.genereateSQLOrderBy(this.firstSegment, this.rootResult);
2091
+ // limit
2092
+ if (this.firstSegment.limit) {
2093
+ s += `LIMIT ${this.firstSegment.limit}\n`;
2094
+ }
2095
+ this.resultStage = stageWriter.addStage(s);
2096
+ this.resultStage = this.generatePipelinedStages(outputPipelinedSQL, this.resultStage, stageWriter);
2097
+ return this.resultStage;
2098
+ }
2099
+ generateTurtleSQL(resultStruct, stageWriter, sqlFieldName, outputPipelinedSQL) {
2100
+ // let fieldsSQL: string[] = [];
2101
+ const dialectFieldList = [];
2102
+ let orderBy = "";
2103
+ const limit = resultStruct.firstSegment.limit;
2104
+ // calculate the ordering.
2105
+ const obSQL = [];
2106
+ let orderingField;
2107
+ const orderByDef = resultStruct.firstSegment.orderBy ||
2108
+ resultStruct.calculateDefaultOrderBy();
2109
+ for (const ordering of orderByDef) {
2110
+ if (typeof ordering.field === "string") {
2111
+ orderingField = {
2112
+ name: ordering.field,
2113
+ fif: resultStruct.getField(ordering.field),
2114
+ };
2115
+ }
2116
+ else {
2117
+ orderingField = resultStruct.getFieldByNumber(ordering.field);
2118
+ }
2119
+ if (resultStruct.firstSegment.type === "reduce") {
2120
+ obSQL.push(" " +
2121
+ this.parent.dialect.sqlMaybeQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`) +
2122
+ ` ${ordering.dir || "ASC"}`);
2123
+ }
2124
+ else if (resultStruct.firstSegment.type === "project") {
2125
+ obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir || "ASC"}`);
2126
+ }
2127
+ }
2128
+ if (obSQL.length > 0) {
2129
+ orderBy = " " + this.parent.dialect.sqlOrderBy(obSQL);
2130
+ }
2131
+ for (const [name, field] of resultStruct.allFields) {
2132
+ const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
2133
+ //
2134
+ if (resultStruct.firstSegment.type === "reduce" &&
2135
+ (field instanceof FieldInstanceResult ||
2136
+ (field instanceof FieldInstanceField &&
2137
+ field.fieldUsage.type === "result"))) {
2138
+ // fieldsSQL.push(`${name}__${resultStruct.groupSet} as ${sqlName}`);
2139
+ // outputFieldNames.push(name);
2140
+ dialectFieldList.push({
2141
+ type: field instanceof FieldInstanceField
2142
+ ? field.f.fieldDef.type
2143
+ : "struct",
2144
+ sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
2145
+ sqlOutputName: sqlName,
2146
+ });
2147
+ }
2148
+ else if (resultStruct.firstSegment.type === "project" &&
2149
+ field instanceof FieldInstanceField &&
2150
+ field.fieldUsage.type === "result") {
2151
+ // fieldsSQL.push(
2152
+ // `${field.f.generateExpression(resultStruct)} as ${sqlName}`
2153
+ // );
2154
+ dialectFieldList.push({
2155
+ type: field.type,
2156
+ sqlExpression: field.f.generateExpression(resultStruct),
2157
+ sqlOutputName: sqlName,
2158
+ });
2159
+ }
2160
+ }
2161
+ let resultType;
2162
+ let ret;
2163
+ if ((resultType = resultStruct.getRepeatedResultType()) !== "nested") {
2164
+ if (resultType === "inline_all_numbers") {
2165
+ ret = this.parent.dialect.sqlCoaleseMeasuresInline(resultStruct.groupSet, dialectFieldList);
2166
+ }
2167
+ else {
2168
+ ret = this.parent.dialect.sqlAnyValueTurtle(resultStruct.groupSet, dialectFieldList);
2169
+ }
2170
+ }
2171
+ else {
2172
+ ret = this.parent.dialect.sqlAggregateTurtle(resultStruct.groupSet, dialectFieldList, orderBy, limit);
2173
+ }
2174
+ // If the turtle is a pipeline, generate a UDF to compute it.
2175
+ const newStageWriter = new StageWriter(this.parent.dialect.supportsCTEinCoorelatedSubQueries, stageWriter);
2176
+ const { structDef, pipeOut } = this.generateTurtlePipelineSQL(resultStruct, newStageWriter, this.parent.dialect.supportUnnestArrayAgg ? ret : sqlFieldName);
2177
+ // if there was a pipeline.
2178
+ if (pipeOut !== undefined) {
2179
+ const sql = newStageWriter.generateCoorelatedSubQuery(this.parent.dialect, structDef);
2180
+ if (this.parent.dialect.supportUnnestArrayAgg) {
2181
+ ret = `(${sql})`;
2182
+ }
2183
+ else {
2184
+ outputPipelinedSQL.push({
2185
+ sqlFieldName,
2186
+ pipelineSQL: `(${sql})`,
2187
+ });
2188
+ }
2189
+ }
2190
+ return ret;
2191
+ // return `${aggregateFunction}(CASE WHEN group_set=${
2192
+ // resultStruct.groupSet
2193
+ // } THEN STRUCT(${fieldsSQL.join(",\n")}) END${tailSQL})`;
2194
+ }
2195
+ generateTurtlePipelineSQL(fi, stageWriter, sourceSQLExpression) {
2196
+ let structDef = this.getResultStructDef(fi, false);
2197
+ const repeatedResultType = fi.getRepeatedResultType();
2198
+ const hasPipeline = fi.turtleDef.pipeline.length > 1;
2199
+ let pipeOut;
2200
+ if (hasPipeline) {
2201
+ const pipeline = [...fi.turtleDef.pipeline];
2202
+ pipeline.shift();
2203
+ const newTurtle = {
2204
+ type: "turtle",
2205
+ name: "starthere",
2206
+ pipeline,
2207
+ };
2208
+ structDef.name = this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType === "inline_all_numbers", sourceSQLExpression);
2209
+ structDef.structSource = { type: "sql", method: "nested" };
2210
+ const qs = new QueryStruct(structDef, {
2211
+ model: this.parent.getModel(),
2212
+ });
2213
+ const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter);
2214
+ pipeOut = q.generateSQLFromPipeline(stageWriter);
2215
+ // console.log(stageWriter.generateSQLStages());
2216
+ structDef = pipeOut.outputStruct;
2217
+ }
2218
+ return {
2219
+ structDef,
2220
+ pipeOut,
2221
+ };
2222
+ }
2223
+ generateComplexSQL(stageWriter) {
2224
+ let stageName = this.generateSQLStage0(stageWriter);
2225
+ if (this.maxDepth > 1) {
2226
+ let i = this.maxDepth;
2227
+ while (i > 1) {
2228
+ stageName = this.generateSQLDepthN(i, stageWriter, stageName);
2229
+ i--;
2230
+ }
2231
+ }
2232
+ // nest the turtles.
2233
+ return this.genereateSQLCombineTurtles(stageWriter, stageName);
2234
+ }
2235
+ generateSQL(stageWriter) {
2236
+ var _a;
2237
+ const r = this.rootResult.computeGroups(0, 0);
2238
+ this.maxDepth = r.maxDepth;
2239
+ this.maxGroupSet = r.nextGroupSetNumber - 1;
2240
+ this.rootResult.assignFieldsToGroups();
2241
+ (_a = this.rootResult).isComplexQuery || (_a.isComplexQuery = this.maxDepth > 0 || r.isComplex);
2242
+ if (this.rootResult.isComplexQuery) {
2243
+ return this.generateComplexSQL(stageWriter);
2244
+ }
2245
+ else {
2246
+ return this.generateSimpleSQL(stageWriter);
2247
+ }
2248
+ }
2249
+ toMalloy() {
2250
+ let ret = `EXPLORE ${(0, malloy_types_1.getIdentifier)(this.parent.fieldDef)} | `;
2251
+ ret += this.fieldDef.type.toUpperCase() + " ";
2252
+ return ret;
2253
+ }
2254
+ generateSQLFromPipeline(stageWriter) {
2255
+ this.prepare(stageWriter);
2256
+ let lastStageName = this.generateSQL(stageWriter);
2257
+ let outputStruct = this.getResultStructDef();
2258
+ if (this.fieldDef.pipeline.length > 1) {
2259
+ // console.log(pretty(outputStruct));
2260
+ const pipeline = [...this.fieldDef.pipeline];
2261
+ let structDef = {
2262
+ ...outputStruct,
2263
+ structSource: { type: "sql", method: "lastStage" },
2264
+ };
2265
+ pipeline.shift();
2266
+ for (const transform of pipeline) {
2267
+ const s = new QueryStruct(structDef, {
2268
+ model: this.parent.getModel(),
2269
+ });
2270
+ const q = QueryQuery.makeQuery({ type: "turtle", name: "ignoreme", pipeline: [transform] }, s, stageWriter);
2271
+ q.prepare(stageWriter);
2272
+ lastStageName = q.generateSQL(stageWriter);
2273
+ outputStruct = q.getResultStructDef();
2274
+ structDef = {
2275
+ ...outputStruct,
2276
+ structSource: { type: "sql", method: "lastStage" },
2277
+ };
2278
+ }
2279
+ }
2280
+ return { lastStageName, outputStruct };
2281
+ }
2282
+ }
2283
+ class QueryQueryReduce extends QueryQuery {
2284
+ }
2285
+ class QueryQueryProject extends QueryQuery {
2286
+ }
2287
+ // generates a single stage query for the index.
2288
+ // wildcards have been expanded
2289
+ // nested repeated fields are safe to use.
2290
+ class QueryQueryIndexStage extends QueryQuery {
2291
+ constructor(fieldDef, parent, stageWriter) {
2292
+ super(fieldDef, parent, stageWriter);
2293
+ this.fieldDef = fieldDef;
2294
+ }
2295
+ // get a field ref and expand it.
2296
+ expandField(f) {
2297
+ const field = this.parent.getFieldByName(f);
2298
+ return { as: f, field };
2299
+ }
2300
+ expandFields(resultStruct) {
2301
+ let resultIndex = 1;
2302
+ const groupIndex = resultStruct.groupSet;
2303
+ this.maxGroupSet = groupIndex;
2304
+ const fieldNames = this.firstSegment.fields || [];
2305
+ for (const f of fieldNames) {
2306
+ const { as, field } = this.expandField(f);
2307
+ resultStruct.addField(as, field, {
2308
+ resultIndex,
2309
+ type: "result",
2310
+ });
2311
+ if (field instanceof QueryAtomicField) {
2312
+ this.addDependancies(resultStruct, field);
2313
+ }
2314
+ resultIndex++;
2315
+ }
2316
+ const measure = this.firstSegment.weightMeasure;
2317
+ if (measure !== undefined) {
2318
+ const f = this.parent.getFieldByName(measure);
2319
+ resultStruct.addField(measure, f, {
2320
+ resultIndex,
2321
+ type: "result",
2322
+ });
2323
+ this.addDependancies(resultStruct, f);
2324
+ }
2325
+ this.expandFilters(resultStruct);
2326
+ }
2327
+ generateSQL(stageWriter) {
2328
+ let measureSQL = "COUNT(*)";
2329
+ const dialect = this.parent.dialect;
2330
+ const fieldNameColumn = dialect.sqlMaybeQuoteIdentifier("fieldName");
2331
+ const fieldValueColumn = dialect.sqlMaybeQuoteIdentifier("fieldValue");
2332
+ const fieldTypeColumn = dialect.sqlMaybeQuoteIdentifier("fieldType");
2333
+ const fieldRangeColumn = dialect.sqlMaybeQuoteIdentifier("fieldRange");
2334
+ const measureName = this.firstSegment.weightMeasure;
2335
+ if (measureName) {
2336
+ measureSQL = this.rootResult
2337
+ .getField(measureName)
2338
+ .f.generateExpression(this.rootResult);
2339
+ }
2340
+ const fields = [];
2341
+ for (const [name, field] of this.rootResult.allFields) {
2342
+ const fi = field;
2343
+ if (fi.fieldUsage.type === "result" && isScalarField(fi.f)) {
2344
+ const expression = fi.f.generateExpression(this.rootResult);
2345
+ fields.push({ name, type: fi.f.fieldDef.type, expression });
2346
+ }
2347
+ }
2348
+ let s = `SELECT\n group_set,\n`;
2349
+ s += ` CASE group_set\n`;
2350
+ for (let i = 0; i < fields.length; i++) {
2351
+ s += ` WHEN ${i} THEN '${fields[i].name}'\n`;
2352
+ }
2353
+ s += ` END as ${fieldNameColumn},`;
2354
+ s += ` CASE group_set\n`;
2355
+ for (let i = 0; i < fields.length; i++) {
2356
+ s += ` WHEN ${i} THEN '${fields[i].type}'\n`;
2357
+ }
2358
+ s += ` END as ${fieldTypeColumn},`;
2359
+ s += ` CASE group_set WHEN 99999 THEN NULL\n`;
2360
+ for (let i = 0; i < fields.length; i++) {
2361
+ if (fields[i].type === "string") {
2362
+ s += ` WHEN ${i} THEN ${fields[i].expression}\n`;
2363
+ }
2364
+ }
2365
+ s += ` END as ${fieldValueColumn},\n`;
2366
+ s += ` ${measureSQL} as weight,\n`;
2367
+ // just in case we don't have any field types, force the case statement to have at least one value.
2368
+ s += ` CASE group_set\n WHEN 99999 THEN ''`;
2369
+ for (let i = 0; i < fields.length; i++) {
2370
+ if (fields[i].type === "number") {
2371
+ s += ` WHEN ${i} THEN CAST(MIN(${fields[i].expression}) AS ${dialect.stringTypeName}) || ' to ' || CAST(MAX(${fields[i].expression}) AS ${dialect.stringTypeName})\n`;
2372
+ }
2373
+ if (fields[i].type === "timestamp" || fields[i].type === "date") {
2374
+ s += ` WHEN ${i} THEN MIN(${dialect.sqlDateToString(fields[i].expression)}) || ' to ' || MAX(${dialect.sqlDateToString(fields[i].expression)})\n`;
2375
+ }
2376
+ }
2377
+ s += ` END as ${fieldRangeColumn}\n`;
2378
+ // CASE
2379
+ // WHEN field_type = 'timestamp' or field_type = 'date'
2380
+ // THEN MIN(field_value) || ' to ' || MAX(field_value)
2381
+ // WHEN field_type = 'number'
2382
+ // THEN
2383
+ // ELSE NULL
2384
+ // END as field_range\n`;
2385
+ s += this.generateSQLJoins(stageWriter);
2386
+ s += dialect.sqlGroupSetTable(fields.length) + "\n";
2387
+ s += this.generateSQLFilters(this.rootResult, "where").sql("where");
2388
+ s += "GROUP BY 1,2,3,4\n";
2389
+ // limit
2390
+ if (this.firstSegment.limit) {
2391
+ s += `LIMIT ${this.firstSegment.limit}\n`;
2392
+ }
2393
+ // console.log(s);
2394
+ const resultStage = stageWriter.addStage(s);
2395
+ this.resultStage = stageWriter.addStage(`SELECT
2396
+ ${fieldNameColumn},
2397
+ ${fieldTypeColumn},
2398
+ COALESCE(${fieldValueColumn}, ${fieldRangeColumn}) as ${fieldValueColumn},
2399
+ weight
2400
+ FROM ${resultStage}\n`);
2401
+ return this.resultStage;
2402
+ }
2403
+ }
2404
+ class QueryQueryIndex extends QueryQuery {
2405
+ constructor(fieldDef, parent, stageWriter) {
2406
+ super(fieldDef, parent, stageWriter);
2407
+ this.rootFields = [];
2408
+ this.fanPrefixMap = {};
2409
+ this.fieldDef = fieldDef;
2410
+ this.findFanPrefexes(parent);
2411
+ }
2412
+ // we want to generate a different query for each
2413
+ // nested structure so we don't do a crazy cross product.
2414
+ findFanPrefexes(qs) {
2415
+ for (const [_name, f] of qs.nameMap) {
2416
+ if (f instanceof QueryStruct &&
2417
+ (f.fieldDef.structRelationship.type === "many" ||
2418
+ f.fieldDef.structRelationship.type === "nested") &&
2419
+ f.fieldDef.fields.length > 1 && // leave arrays in parent.
2420
+ this.parent.dialect.dontUnionIndex === false) {
2421
+ const key = f.getFullOutputName();
2422
+ this.fanPrefixMap[key] = [];
2423
+ this.findFanPrefexes(f);
2424
+ }
2425
+ }
2426
+ }
2427
+ expandIndexWildCards() {
2428
+ // if no fields were specified, look in the parent struct for strings.
2429
+ let fieldNames = this.firstSegment.fields || [];
2430
+ if (fieldNames.length === 0) {
2431
+ fieldNames.push("**");
2432
+ }
2433
+ fieldNames = this.expandWildCards(fieldNames, (qf) => ["string", "number", "timestamp", "date"].indexOf(qf.fieldDef.type) !==
2434
+ -1);
2435
+ return fieldNames;
2436
+ }
2437
+ // return the number of stages it is going to take to generate this index.
2438
+ getStageFields() {
2439
+ const s = [];
2440
+ if (this.rootFields.length > 0) {
2441
+ s.push(this.rootFields);
2442
+ }
2443
+ for (const fieldList of Object.values(this.fanPrefixMap)) {
2444
+ if (fieldList.length > 0) {
2445
+ s.push(fieldList);
2446
+ }
2447
+ }
2448
+ return s;
2449
+ }
2450
+ // Map fields into stages based on their level of repeated nesting
2451
+ //
2452
+ mapFieldsIntoStages(fieldNames) {
2453
+ // find all the fanned prefixes, longest ones first.
2454
+ const fannedPrefixes = Object.keys(this.fanPrefixMap).sort((k1, k2) => {
2455
+ if (k1.length < k2.length) {
2456
+ return 1;
2457
+ }
2458
+ if (k1.length > k2.length) {
2459
+ return -1;
2460
+ }
2461
+ return 0;
2462
+ });
2463
+ // Find the deepest fanned prefix
2464
+ for (const fn of fieldNames) {
2465
+ let found = false;
2466
+ for (const prefix of fannedPrefixes) {
2467
+ if (fn.startsWith(prefix)) {
2468
+ this.fanPrefixMap[prefix].push(fn);
2469
+ found = true;
2470
+ }
2471
+ }
2472
+ if (!found) {
2473
+ this.rootFields.push(fn);
2474
+ }
2475
+ }
2476
+ }
2477
+ expandFields(_resultStruct) {
2478
+ const fieldNames = this.expandIndexWildCards();
2479
+ this.mapFieldsIntoStages(fieldNames);
2480
+ }
2481
+ generateSQL(stageWriter) {
2482
+ const stages = this.getStageFields();
2483
+ const outputStageNames = [];
2484
+ for (const fields of stages) {
2485
+ const q = new QueryQueryIndexStage({
2486
+ ...this.fieldDef,
2487
+ pipeline: [
2488
+ {
2489
+ ...this.fieldDef.pipeline[0],
2490
+ fields: fields,
2491
+ },
2492
+ ],
2493
+ }, this.parent, stageWriter);
2494
+ q.prepare(stageWriter);
2495
+ const lastStageName = q.generateSQL(stageWriter);
2496
+ outputStageNames.push(lastStageName);
2497
+ }
2498
+ if (outputStageNames.length === 1) {
2499
+ this.resultStage = outputStageNames[0];
2500
+ }
2501
+ else {
2502
+ this.resultStage = stageWriter.addStage(outputStageNames
2503
+ .map((n) => `SELECT * FROM ${n}\n`)
2504
+ .join(" UNION ALL \n"));
2505
+ }
2506
+ return this.resultStage;
2507
+ }
2508
+ /** All Indexes have the same output schema */
2509
+ getResultStructDef() {
2510
+ return {
2511
+ type: "struct",
2512
+ name: this.resultStage || "result",
2513
+ dialect: this.parent.fieldDef.dialect,
2514
+ fields: [
2515
+ { type: "string", name: "fieldName" },
2516
+ { type: "string", name: "fieldValue" },
2517
+ { type: "string", name: "fieldType" },
2518
+ { type: "number", name: "weight", numberType: "integer" },
2519
+ ],
2520
+ structRelationship: {
2521
+ type: "basetable",
2522
+ connectionName: this.parent.connectionName,
2523
+ },
2524
+ structSource: { type: "query_result" },
2525
+ };
2526
+ }
2527
+ }
2528
+ /** Structure object as it is used to build a query */
2529
+ class QueryStruct extends QueryNode {
2530
+ constructor(fieldDef, parent) {
2531
+ super(fieldDef);
2532
+ this.nameMap = new Map();
2533
+ this.setParent(parent);
2534
+ if ("model" in parent) {
2535
+ this.model = parent.model;
2536
+ this.pathAliasMap = new Map();
2537
+ if (fieldDef.structRelationship.type === "basetable") {
2538
+ this.connectionName = fieldDef.structRelationship.connectionName;
2539
+ }
2540
+ else {
2541
+ throw new Error("All root StructDefs should be a baseTable");
2542
+ }
2543
+ }
2544
+ else {
2545
+ this.model = this.getModel();
2546
+ this.pathAliasMap = this.root().pathAliasMap;
2547
+ this.connectionName = this.root().connectionName;
2548
+ }
2549
+ this.fieldDef = fieldDef; // shouldn't have to do this, but
2550
+ // type script is missing a beat here.
2551
+ this.dialect = (0, dialect_1.getDialect)(this.fieldDef.dialect);
2552
+ this.addFieldsFromFieldList(this.fieldDef.fields);
2553
+ }
2554
+ parameters() {
2555
+ return this.fieldDef.parameters || {};
2556
+ }
2557
+ addFieldsFromFieldList(fields) {
2558
+ for (const field of fields) {
2559
+ const as = (0, malloy_types_1.getIdentifier)(field);
2560
+ switch (field.type) {
2561
+ case "struct": {
2562
+ this.addFieldToNameMap(as, new QueryStruct(field, {
2563
+ struct: this,
2564
+ }));
2565
+ break;
2566
+ }
2567
+ // case "reduce" || "project" || "index": {
2568
+ case "turtle": {
2569
+ // not sure why we need to cast here...
2570
+ this.addFieldToNameMap(as, QueryQuery.makeQuery(field, this));
2571
+ break;
2572
+ }
2573
+ default: {
2574
+ this.addFieldToNameMap(as, this.makeQueryField(field));
2575
+ }
2576
+ }
2577
+ }
2578
+ // if we don't have distinct key yet for this struct, add it.
2579
+ if (!this.nameMap.has("__distinct_key")) {
2580
+ this.addFieldToNameMap("__distinct_key", new QueryFieldDistinctKey({ type: "string", name: "__distinct_key" }, this));
2581
+ }
2582
+ }
2583
+ // generate unique string for the alias.
2584
+ // return a string that can be used to represent the full
2585
+ // join path to a struct.
2586
+ getAliasIdentifier() {
2587
+ const path = this.getFullOutputName();
2588
+ const ret = this.pathAliasMap.get(path);
2589
+ // make a unique alias name
2590
+ if (ret === undefined) {
2591
+ const aliases = Array.from(this.pathAliasMap.values());
2592
+ const base = (0, malloy_types_1.getIdentifier)(this.fieldDef);
2593
+ let name = `${base}_0`;
2594
+ let n = 1;
2595
+ while (aliases.includes(name) && n < 1000) {
2596
+ n++;
2597
+ name = `${base}_${n}`;
2598
+ }
2599
+ if (n < 1000) {
2600
+ this.pathAliasMap.set(path, name);
2601
+ return name;
2602
+ }
2603
+ else {
2604
+ throw new Error("Internal Error: cannot create unique alias name");
2605
+ }
2606
+ // get the malloy name for this struct (will include a trailing dot)
2607
+ // return this.getFullOutputName().replace(/\.$/, "").replace(/\./g, "_o_");
2608
+ }
2609
+ else {
2610
+ return ret;
2611
+ }
2612
+ }
2613
+ // when structs are referenced in queries, incorporate the
2614
+ // primary key of struct and add the struct as a join to the result.
2615
+ getAsQueryField() {
2616
+ if (this.fieldDef.primaryKey === undefined) {
2617
+ throw new Error(`Joined explores can only be included in queries if a primary key is defined: '${this.getFullOutputName()}' has no primary key`);
2618
+ }
2619
+ const pkField = this.getPrimaryKeyField(this.fieldDef);
2620
+ const pkType = pkField.fieldDef.type;
2621
+ if (pkType !== "string" && pkType !== "number") {
2622
+ throw new Error(`Unknown Primary key data type for ${pkField.fieldDef.name}`);
2623
+ }
2624
+ const aliasName = (0, malloy_types_1.getIdentifier)(this.fieldDef);
2625
+ const pkName = this.fieldDef.primaryKey;
2626
+ const fieldDef = {
2627
+ type: pkType,
2628
+ name: `${aliasName}_id`,
2629
+ e: [
2630
+ {
2631
+ type: "field",
2632
+ // path: pkField.getFullOutputName(),
2633
+ path: pkField.getIdentifier(),
2634
+ },
2635
+ ],
2636
+ };
2637
+ return new QueryFieldStruct(fieldDef, this, `${aliasName}.${pkName}`);
2638
+ }
2639
+ getSQLIdentifier() {
2640
+ if (this.unnestWithNumbers() && this.parent !== undefined) {
2641
+ const x = this.parent.getSQLIdentifier() +
2642
+ "." +
2643
+ (0, malloy_types_1.getIdentifier)(this.fieldDef) +
2644
+ `[${this.getIdentifier()}.__row_id]`;
2645
+ return x;
2646
+ }
2647
+ else {
2648
+ return this.getIdentifier();
2649
+ }
2650
+ }
2651
+ // return the name of the field in SQL
2652
+ getIdentifier() {
2653
+ // if it is the root table, use provided alias if we have one.
2654
+ if (this.fieldDef.structRelationship.type === "basetable") {
2655
+ if (this.fieldDef.as === undefined) {
2656
+ return "base";
2657
+ }
2658
+ else {
2659
+ return super.getIdentifier();
2660
+ }
2661
+ }
2662
+ // if this is an inline object, include the parents alias.
2663
+ if (this.fieldDef.structRelationship.type === "inline" && this.parent) {
2664
+ return this.parent.getSQLIdentifier() + "." + super.getIdentifier();
2665
+ }
2666
+ // we are somewhere in the join tree. Make sure the alias is unique.
2667
+ return this.getAliasIdentifier();
2668
+ }
2669
+ // return the name of the field in Malloy
2670
+ getFullOutputName() {
2671
+ if (this.parent) {
2672
+ return (this.parent.getFullOutputName() + (0, malloy_types_1.getIdentifier)(this.fieldDef) + ".");
2673
+ }
2674
+ else {
2675
+ return "";
2676
+ }
2677
+ }
2678
+ needsSymetricCalculation(resultSet) {
2679
+ const joinName = this.getJoinableParent().getIdentifier();
2680
+ const join = resultSet.root().joins.get(joinName);
2681
+ if (join) {
2682
+ return !join.leafiest;
2683
+ }
2684
+ throw new Error(`Join ${joinName} not found in result set`);
2685
+ }
2686
+ unnestWithNumbers() {
2687
+ return (this.dialect.unnestWithNumbers &&
2688
+ this.fieldDef.structRelationship.type === "nested");
2689
+ }
2690
+ getJoinableParent() {
2691
+ // if it is inline it should always have a parent
2692
+ if (this.fieldDef.structRelationship.type === "inline") {
2693
+ if (this.parent) {
2694
+ return this.parent.getJoinableParent();
2695
+ }
2696
+ else {
2697
+ throw new Error(`Internal Error: inline struct cannot be root`);
2698
+ }
2699
+ }
2700
+ return this;
2701
+ }
2702
+ addFieldToNameMap(as, n) {
2703
+ if (this.nameMap.has(as)) {
2704
+ throw new Error(`Redefinition of ${as}`);
2705
+ }
2706
+ this.nameMap.set(as, n);
2707
+ }
2708
+ /** the the primary key or throw an error. */
2709
+ getPrimaryKeyField(fieldDef) {
2710
+ let pk;
2711
+ if ((pk = this.primaryKey())) {
2712
+ return pk;
2713
+ }
2714
+ else {
2715
+ throw new Error(`Missing primary key for ${fieldDef}`);
2716
+ }
2717
+ }
2718
+ /**
2719
+ * called after all structure has been loaded. Examine this structure to see
2720
+ * if if it is based on a query and if it is, add the output fields (unless
2721
+ * they exist) to the structure.
2722
+ */
2723
+ resolveQueryFields() {
2724
+ if (this.fieldDef.structSource.type === "query") {
2725
+ const structDef = this.model
2726
+ .loadQuery(this.fieldDef.structSource.query, undefined)
2727
+ .structs.pop();
2728
+ // should never happen.
2729
+ if (!structDef) {
2730
+ throw new Error("Internal Error, query didn't produce a struct");
2731
+ }
2732
+ const fieldDef = { ...this.fieldDef };
2733
+ for (const f of structDef.fields) {
2734
+ let as;
2735
+ if (!this.nameMap.has((as = (0, malloy_types_1.getIdentifier)(f)))) {
2736
+ fieldDef.fields.push(f);
2737
+ this.nameMap.set(as, this.makeQueryField(f));
2738
+ }
2739
+ }
2740
+ this.fieldDef = fieldDef;
2741
+ if (!this.fieldDef.primaryKey && structDef.primaryKey) {
2742
+ this.fieldDef.primaryKey = structDef.primaryKey;
2743
+ }
2744
+ }
2745
+ for (const [, v] of this.nameMap) {
2746
+ if (v instanceof QueryStruct) {
2747
+ v.resolveQueryFields();
2748
+ }
2749
+ }
2750
+ }
2751
+ getModel() {
2752
+ if (this.model) {
2753
+ return this.model;
2754
+ }
2755
+ else {
2756
+ if (this.parent === undefined) {
2757
+ throw new Error("Expected this query struct to have a parent, as no model was present.");
2758
+ }
2759
+ return this.parent.getModel();
2760
+ }
2761
+ }
2762
+ setParent(parent) {
2763
+ if ("struct" in parent) {
2764
+ this.parent = parent.struct;
2765
+ }
2766
+ if ("model" in parent) {
2767
+ this.model = parent.model;
2768
+ }
2769
+ else {
2770
+ this.model = this.getModel();
2771
+ }
2772
+ }
2773
+ /** makes a new queryable field object from a fieldDef */
2774
+ makeQueryField(field) {
2775
+ switch (field.type) {
2776
+ case "string":
2777
+ return new QueryFieldString(field, this);
2778
+ case "date":
2779
+ return new QueryFieldDate(field, this);
2780
+ case "timestamp":
2781
+ return new QueryFieldTimestamp(field, this);
2782
+ case "number":
2783
+ return new QueryFieldNumber(field, this);
2784
+ case "boolean":
2785
+ return new QueryFieldBoolean(field, this);
2786
+ case "json":
2787
+ return new QueryFieldJSON(field, this);
2788
+ // case "reduce":
2789
+ // case "project":
2790
+ // case "index":
2791
+ case "turtle":
2792
+ return new QueryTurtle(field, this);
2793
+ default:
2794
+ throw new Error(`unknown field definition ${JSON.stringify(field)}`);
2795
+ }
2796
+ }
2797
+ /**
2798
+ * return a field if it exists, make one if we are passed a field definition.
2799
+ */
2800
+ getOrMakeField(fieldRef) {
2801
+ if (typeof fieldRef === "string") {
2802
+ return this.getFieldByName(fieldRef);
2803
+ }
2804
+ else {
2805
+ return this.makeQueryField(fieldRef);
2806
+ }
2807
+ }
2808
+ /** returns a dimension for the given name or make one. */
2809
+ getOrMakeDimension(fieldRef) {
2810
+ const dim = this.getOrMakeField(fieldRef);
2811
+ if (dim instanceof QueryAtomicField && isScalarField(dim)) {
2812
+ return dim;
2813
+ }
2814
+ else {
2815
+ throw new Error(`${fieldRef} is not of type a scalar'`);
2816
+ }
2817
+ }
2818
+ structSourceSQL(stageWriter) {
2819
+ switch (this.fieldDef.structSource.type) {
2820
+ case "table": {
2821
+ const tablePath = this.fieldDef.structSource.tablePath;
2822
+ return this.dialect.quoteTablePath(tablePath);
2823
+ }
2824
+ case "sql":
2825
+ if (this.fieldDef.structSource.method === "nested" ||
2826
+ this.fieldDef.structSource.method === "lastStage") {
2827
+ return this.fieldDef.name;
2828
+ }
2829
+ else if (this.fieldDef.structSource.method === "subquery") {
2830
+ return `(${this.fieldDef.structSource.sqlBlock.selectStr})`;
2831
+ }
2832
+ throw new Error("Internal Error: Unknown structSource type 'sql' method");
2833
+ case "nested":
2834
+ // 'name' is always the source field even if has been renamed through
2835
+ // 'as'
2836
+ return `UNNEST(this.fieldDef.name)`;
2837
+ case "inline":
2838
+ return "";
2839
+ case "query": {
2840
+ // cache derived table.
2841
+ const name = (0, malloy_types_1.getIdentifier)(this.fieldDef);
2842
+ // this is a hack for now. Need some way to denote this table
2843
+ // should be cached.
2844
+ if (name.includes("cache")) {
2845
+ const dtStageWriter = new StageWriter(true, stageWriter);
2846
+ this.model.loadQuery(this.fieldDef.structSource.query, dtStageWriter);
2847
+ return dtStageWriter.addPDT(name, this.dialect);
2848
+ }
2849
+ else {
2850
+ // returns the stage name.
2851
+ return this.model.loadQuery(this.fieldDef.structSource.query, stageWriter).lastStageName;
2852
+ }
2853
+ }
2854
+ default:
2855
+ throw new Error(`unknown structSource ${this.fieldDef}`);
2856
+ }
2857
+ }
2858
+ root() {
2859
+ if (this.parent === undefined) {
2860
+ return this;
2861
+ }
2862
+ else {
2863
+ return this.parent.root();
2864
+ }
2865
+ }
2866
+ primaryKey() {
2867
+ if (this.fieldDef.primaryKey) {
2868
+ return this.getDimensionByName(this.fieldDef.primaryKey);
2869
+ }
2870
+ else {
2871
+ return undefined;
2872
+ }
2873
+ }
2874
+ /** get the componennts of a field path */
2875
+ static resolvePath(name) {
2876
+ return name.split(".");
2877
+ }
2878
+ getChildByName(name) {
2879
+ return this.nameMap.get(name);
2880
+ }
2881
+ /** convert a name into a field reference */
2882
+ getFieldByName(name) {
2883
+ const path = QueryStruct.resolvePath(name);
2884
+ let ret = this;
2885
+ for (const n of path) {
2886
+ const r = ret.getChildByName(n);
2887
+ if (r === undefined) {
2888
+ throw new Error(`Path not found ${name}`);
2889
+ }
2890
+ ret = r;
2891
+ }
2892
+ return ret;
2893
+ }
2894
+ // structs referenced in queries are converted to fields.
2895
+ getQueryFieldByName(name) {
2896
+ let field = this.getFieldByName(name);
2897
+ if (field instanceof QueryStruct) {
2898
+ field = field.getAsQueryField();
2899
+ }
2900
+ return field;
2901
+ }
2902
+ getDimensionOrMeasureByName(name) {
2903
+ const query = this.getFieldByName(name);
2904
+ if (query instanceof QueryAtomicField) {
2905
+ return query;
2906
+ }
2907
+ else {
2908
+ throw new Error(`${name} is not of type a scalar'`);
2909
+ }
2910
+ }
2911
+ /** returns a query object for the given name */
2912
+ getDimensionByName(name) {
2913
+ const query = this.getFieldByName(name);
2914
+ if (query instanceof QueryAtomicField && isScalarField(query)) {
2915
+ return query;
2916
+ }
2917
+ else {
2918
+ throw new Error(`${name} is not of type a scalar'`);
2919
+ }
2920
+ }
2921
+ /** returns a query object for the given name */
2922
+ getStructByName(name) {
2923
+ const struct = this.getFieldByName(name);
2924
+ if (struct instanceof QueryStruct) {
2925
+ return struct;
2926
+ }
2927
+ else {
2928
+ throw new Error(`Error: Path to structure not found '${name}'`);
2929
+ }
2930
+ }
2931
+ getDistinctKey() {
2932
+ if (this.fieldDef.structRelationship.type !== "inline") {
2933
+ return this.getDimensionByName("__distinct_key");
2934
+ }
2935
+ else if (this.parent) {
2936
+ return this.parent.getDistinctKey();
2937
+ }
2938
+ else {
2939
+ throw new Error("Internal Error. inline struct can not be top level");
2940
+ }
2941
+ }
2942
+ // take a TurtleDef that might have names and make it so it doesn't.
2943
+ flattenTurtleDef(turtleDef) {
2944
+ let pipeline = turtleDef.pipeline;
2945
+ let pipeHead = turtleDef.pipeHead;
2946
+ while (pipeHead) {
2947
+ const field = this.getFieldByName(pipeHead.name);
2948
+ if (field instanceof QueryQuery) {
2949
+ pipeHead = field.fieldDef.pipeHead;
2950
+ pipeline = field.fieldDef.pipeline.concat(pipeline);
2951
+ }
2952
+ else {
2953
+ throw new Error(`Only Turtles can be used in a pipeline ${pipeHead.name}`);
2954
+ }
2955
+ }
2956
+ const addedFilters = turtleDef.filterList || [];
2957
+ pipeline = (0, lodash_1.cloneDeep)(pipeline);
2958
+ pipeline[0].filterList = addedFilters.concat(pipeline[0].filterList || [], this.fieldDef.filterList || []);
2959
+ const flatTurtleDef = {
2960
+ type: "turtle",
2961
+ name: turtleDef.name,
2962
+ pipeline,
2963
+ location: turtleDef.location,
2964
+ };
2965
+ return flatTurtleDef;
2966
+ }
2967
+ }
2968
+ // const exploreSearchSQLMap = new Map<string, string>();
2969
+ /** start here */
2970
+ class QueryModel {
2971
+ constructor(modelDef) {
2972
+ this.dialect = new standardsql_1.StandardSQLDialect();
2973
+ // dialect: Dialect = new PostgresDialect();
2974
+ this.modelDef = undefined;
2975
+ this.structs = new Map();
2976
+ this.exploreSearchSQLMap = new Map();
2977
+ if (modelDef) {
2978
+ this.loadModelFromDef(modelDef);
2979
+ }
2980
+ }
2981
+ loadModelFromDef(modelDef) {
2982
+ this.modelDef = modelDef;
2983
+ for (const s of Object.values(this.modelDef.contents)) {
2984
+ let qs;
2985
+ if (s.type === "struct") {
2986
+ qs = new QueryStruct(s, { model: this });
2987
+ this.structs.set((0, malloy_types_1.getIdentifier)(s), qs);
2988
+ qs.resolveQueryFields();
2989
+ }
2990
+ else if (s.type === "query") {
2991
+ /* TODO */
2992
+ }
2993
+ else {
2994
+ throw new Error("Internal Error: Unknown structure type");
2995
+ }
2996
+ }
2997
+ }
2998
+ parseQueryPath(name) {
2999
+ const path = name.split(".");
3000
+ let struct;
3001
+ if ((struct = this.structs.get(path[0]))) {
3002
+ if (path.length > 1) {
3003
+ path.shift();
3004
+ }
3005
+ else {
3006
+ throw new Error(`No query specified in Struct '${path[0]}'`);
3007
+ }
3008
+ return { queryName: path.join("."), struct };
3009
+ }
3010
+ else {
3011
+ throw new Error(`Cannot find Struct '${path[0]}' Model`);
3012
+ }
3013
+ }
3014
+ getStructByName(name) {
3015
+ let s;
3016
+ if ((s = this.structs.get(name))) {
3017
+ return s;
3018
+ }
3019
+ else {
3020
+ throw new Error(`Struct ${name} not found in model.`);
3021
+ }
3022
+ }
3023
+ getStructFromRef(structRef) {
3024
+ let structDef;
3025
+ if (typeof structRef === "string") {
3026
+ return this.getStructByName(structRef);
3027
+ }
3028
+ else if (structRef.type === "struct") {
3029
+ structDef = structRef;
3030
+ }
3031
+ else {
3032
+ throw new Error("Broken for now");
3033
+ }
3034
+ return new QueryStruct(structDef, { model: this });
3035
+ }
3036
+ loadQuery(query, stageWriter, emitFinalStage = false) {
3037
+ const malloy = "";
3038
+ if (!stageWriter) {
3039
+ stageWriter = new StageWriter(true, undefined);
3040
+ }
3041
+ const turtleDef = {
3042
+ type: "turtle",
3043
+ name: "ignoreme",
3044
+ pipeHead: query.pipeHead,
3045
+ pipeline: query.pipeline,
3046
+ filterList: query.filterList,
3047
+ };
3048
+ const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef), stageWriter);
3049
+ const ret = q.generateSQLFromPipeline(stageWriter);
3050
+ if (emitFinalStage && q.parent.dialect.hasFinalStage) {
3051
+ // const fieldNames: string[] = [];
3052
+ // for (const f of ret.outputStruct.fields) {
3053
+ // fieldNames.push(getIdentifier(f));
3054
+ // }
3055
+ const fieldNames = (0, malloy_types_1.getPhysicalFields)(ret.outputStruct).map((fieldDef) => q.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name));
3056
+ ret.lastStageName = stageWriter.addStage(q.parent.dialect.sqlFinalStage(ret.lastStageName, fieldNames));
3057
+ }
3058
+ return {
3059
+ lastStageName: ret.lastStageName,
3060
+ malloy,
3061
+ stageWriter,
3062
+ structs: [ret.outputStruct],
3063
+ connectionName: q.parent.connectionName,
3064
+ };
3065
+ }
3066
+ compileQuery(query, finalize = true) {
3067
+ let newModel;
3068
+ const m = newModel || this;
3069
+ const ret = m.loadQuery(query, undefined, finalize);
3070
+ const sourceExplore = typeof query.structRef === "string"
3071
+ ? query.structRef
3072
+ : // LTNOTE: the parser needs to capture the query before the |. This will work
3073
+ // in most cases but isn't actually complete.
3074
+ query.structRef.type === "struct"
3075
+ ? query.structRef.as || query.structRef.name
3076
+ : "(need to figure this out)";
3077
+ // LTNote: I don't understand why this might be here. It should have happened in loadQuery...
3078
+ if (finalize && this.dialect.hasFinalStage) {
3079
+ ret.lastStageName = ret.stageWriter.addStage(
3080
+ // note this will be broken on duckDB waiting on a real fix.
3081
+ this.dialect.sqlFinalStage(ret.lastStageName, []));
3082
+ }
3083
+ return {
3084
+ lastStageName: ret.lastStageName,
3085
+ malloy: ret.malloy,
3086
+ sql: ret.stageWriter.generateSQLStages(),
3087
+ structs: ret.structs,
3088
+ sourceExplore,
3089
+ sourceFilters: query.filterList,
3090
+ queryName: query.pipeHead && query.pipeline.length === 0
3091
+ ? query.pipeHead.name
3092
+ : undefined,
3093
+ connectionName: ret.connectionName,
3094
+ };
3095
+ }
3096
+ async searchIndex(connection, explore, searchValue, limit = 1000, searchField = undefined) {
3097
+ if (!connection.canPersist()) {
3098
+ return undefined;
3099
+ }
3100
+ // make a search index if one isn't modelled.
3101
+ const struct = this.getStructByName(explore);
3102
+ let indexQuery;
3103
+ if (!struct.nameMap.get("search_index")) {
3104
+ indexQuery = {
3105
+ structRef: explore,
3106
+ pipeline: [
3107
+ {
3108
+ type: "index",
3109
+ fields: ["*"],
3110
+ sample: struct.dialect.defaultSampling,
3111
+ },
3112
+ ],
3113
+ };
3114
+ }
3115
+ else {
3116
+ indexQuery = {
3117
+ structRef: explore,
3118
+ pipeHead: { name: "search_index" },
3119
+ pipeline: [],
3120
+ };
3121
+ }
3122
+ const fieldNameColumn = struct.dialect.sqlMaybeQuoteIdentifier("fieldName");
3123
+ const fieldValueColumn = struct.dialect.sqlMaybeQuoteIdentifier("fieldValue");
3124
+ const fieldTypeColumn = struct.dialect.sqlMaybeQuoteIdentifier("fieldType");
3125
+ // if we've compiled the SQL before use it otherwise
3126
+ let sqlPDT = this.exploreSearchSQLMap.get(explore);
3127
+ if (sqlPDT === undefined) {
3128
+ sqlPDT = (await this.compileQuery(indexQuery, false)).sql;
3129
+ this.exploreSearchSQLMap.set(explore, sqlPDT);
3130
+ }
3131
+ let query = `SELECT
3132
+ ${fieldNameColumn},
3133
+ ${fieldValueColumn},
3134
+ ${fieldTypeColumn},
3135
+ weight,
3136
+ CASE WHEN lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(searchValue + "%")}) THEN 1 ELSE 0 END as match_first
3137
+ FROM ${await connection.manifestTemporaryTable(sqlPDT)}
3138
+ WHERE lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral("%" + searchValue + "%")}) ${searchField !== undefined
3139
+ ? ` AND ${fieldNameColumn} = '` + searchField + "' \n"
3140
+ : ""}
3141
+ ORDER BY CASE WHEN lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(searchValue + "%")}) THEN 1 ELSE 0 END DESC, weight DESC
3142
+ LIMIT ${limit}
3143
+ `;
3144
+ if (struct.dialect.hasFinalStage) {
3145
+ query = `WITH __stage0 AS(\n${query}\n)\n${struct.dialect.sqlFinalStage("__stage0", [
3146
+ fieldNameColumn,
3147
+ fieldValueColumn,
3148
+ fieldTypeColumn,
3149
+ "weight",
3150
+ "match_first",
3151
+ ])}`;
3152
+ }
3153
+ const result = await connection.runSQL(query, {
3154
+ rowLimit: 1000,
3155
+ });
3156
+ return result.rows;
3157
+ }
3158
+ }
3159
+ exports.QueryModel = QueryModel;
3160
+ function flattenQuery(model, query) {
3161
+ let structRef = query.structRef;
3162
+ if (typeof structRef !== "string") {
3163
+ structRef = structRef.as || structRef.name;
3164
+ }
3165
+ const queryModel = new QueryModel(model);
3166
+ const queryStruct = queryModel.getStructByName(structRef);
3167
+ const turtleDef = queryStruct.flattenTurtleDef({
3168
+ ...query,
3169
+ type: "turtle",
3170
+ });
3171
+ return turtleDef;
3172
+ }
3173
+ exports.flattenQuery = flattenQuery;
3174
+ //# sourceMappingURL=malloy_query.js.map