@malloydata/malloy-tests 0.0.425 → 0.0.426

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.
package/package.json CHANGED
@@ -20,15 +20,15 @@
20
20
  "malloyc": "ts-node ../scripts/malloy-to-json"
21
21
  },
22
22
  "dependencies": {
23
- "@malloydata/db-bigquery": "0.0.425",
24
- "@malloydata/db-duckdb": "0.0.425",
25
- "@malloydata/db-postgres": "0.0.425",
26
- "@malloydata/db-snowflake": "0.0.425",
27
- "@malloydata/db-trino": "0.0.425",
28
- "@malloydata/malloy": "0.0.425",
29
- "@malloydata/malloy-tag": "0.0.425",
30
- "@malloydata/render": "0.0.425",
31
- "@malloydata/render-validator": "0.0.425",
23
+ "@malloydata/db-bigquery": "0.0.426",
24
+ "@malloydata/db-duckdb": "0.0.426",
25
+ "@malloydata/db-postgres": "0.0.426",
26
+ "@malloydata/db-snowflake": "0.0.426",
27
+ "@malloydata/db-trino": "0.0.426",
28
+ "@malloydata/malloy": "0.0.426",
29
+ "@malloydata/malloy-tag": "0.0.426",
30
+ "@malloydata/render": "0.0.426",
31
+ "@malloydata/render-validator": "0.0.426",
32
32
  "events": "^3.3.0",
33
33
  "jsdom": "^22.1.0",
34
34
  "luxon": "^3.7.2",
@@ -42,5 +42,5 @@
42
42
  "overrides": {
43
43
  "typescript": "^6.0.3"
44
44
  },
45
- "version": "0.0.425"
45
+ "version": "0.0.426"
46
46
  }
@@ -2179,4 +2179,194 @@ describe('source persistence', () => {
2179
2179
  expect(sql).toContain('cached.by_carrier');
2180
2180
  });
2181
2181
  });
2182
+
2183
+ // Reported on #3015. Both describes assert the behavior the documentation
2184
+ // describes, so a failure here is the report reproducing.
2185
+ describe('sql_select substitution', () => {
2186
+ // One model, two persisted sources of the two persistable kinds, plus a
2187
+ // third that reaches the sql_select through interpolation.
2188
+ const TWO_KINDS = `${PERSIST_ANNOTATION}
2189
+ ${FLIGHTS_SOURCE}
2190
+
2191
+ #@ persist
2192
+ source: qs is flights -> {
2193
+ group_by: carrier
2194
+ aggregate: flight_count is count()
2195
+ }
2196
+
2197
+ #@ persist
2198
+ source: ss is ${tstDB}.sql("""
2199
+ SELECT carrier, COUNT(*) as flight_count
2200
+ FROM malloytest.flights
2201
+ GROUP BY carrier
2202
+ """)
2203
+
2204
+ #@ persist
2205
+ source: via_interp is ${tstDB}.sql("""
2206
+ SELECT * FROM %{ ss }
2207
+ """)
2208
+ `;
2209
+
2210
+ // A manifest holding a built table for every persist source in the model.
2211
+ async function manifestForAll(): Promise<BuildManifest> {
2212
+ const model = await wrapTestModel(tstRuntime, TWO_KINDS).model.getModel();
2213
+ const digest = await getDigest();
2214
+ const manifest = createManifest();
2215
+ for (const source of Object.values(model.getBuildPlan().sources)) {
2216
+ addManifestEntry(
2217
+ manifest,
2218
+ source.makeBuildId(digest, source.getSQL()),
2219
+ `cached.${source.name}_table`
2220
+ );
2221
+ }
2222
+ return manifest;
2223
+ }
2224
+
2225
+ async function sqlForQuery(query: string): Promise<string> {
2226
+ const manifest = await manifestForAll();
2227
+ return runtimeWithManifest(manifest)
2228
+ .loadQuery(`${TWO_KINDS}\n${query}`)
2229
+ .getSQL();
2230
+ }
2231
+
2232
+ it('query_source reads its built table', async () => {
2233
+ const sql = await sqlForQuery('run: qs -> { select: * }');
2234
+ expect(sql).toContain('cached.qs_table');
2235
+ expect(sql).not.toContain('COUNT(');
2236
+ });
2237
+
2238
+ // Pending #3016 — a directly-referenced sql_select never reaches the
2239
+ // manifest. The two passing tests around these are the controls: the
2240
+ // other persistable kind substitutes, and the same source substitutes
2241
+ // when interpolation reaches it, so the entry and its key are right.
2242
+ it.skip('sql_select reads its built table', async () => {
2243
+ const sql = await sqlForQuery('run: ss -> { select: * }');
2244
+ expect(sql).toContain('cached.ss_table');
2245
+ expect(sql).not.toContain('COUNT(');
2246
+ });
2247
+
2248
+ it('sql_select reached by interpolation reads its built table', async () => {
2249
+ // The manifest key and the entry are right — this path finds them.
2250
+ const sql = await sqlForQuery('run: via_interp -> { select: * }');
2251
+ expect(sql).toContain('cached.ss_table');
2252
+ expect(sql).not.toContain('COUNT(');
2253
+ });
2254
+
2255
+ it.skip('strict mode catches a missing sql_select entry', async () => {
2256
+ // Strict mode's promise is that a persist source with no manifest entry
2257
+ // throws rather than silently compiling to inline SQL.
2258
+ const strict = createManifest();
2259
+ strict.strict = true;
2260
+ await expect(
2261
+ runtimeWithManifest(strict)
2262
+ .loadQuery(`${TWO_KINDS}\nrun: ss -> { select: * }`)
2263
+ .getSQL()
2264
+ ).rejects.toThrow(/not found in manifest/);
2265
+ });
2266
+
2267
+ it.skip('a join of both kinds substitutes both', async () => {
2268
+ const sql = await sqlForQuery(`
2269
+ run: flights extend {
2270
+ join_one: q is qs on carrier = q.carrier
2271
+ join_one: s is ss on carrier = s.carrier
2272
+ } -> {
2273
+ group_by: carrier
2274
+ aggregate: a is q.flight_count.sum(), b is s.flight_count.sum()
2275
+ }
2276
+ `);
2277
+ expect(sql).toContain('cached.qs_table');
2278
+ expect(sql).toContain('cached.ss_table');
2279
+ });
2280
+ });
2281
+
2282
+ describe('per-call buildManifest on ModelMaterializer.loadQuery', () => {
2283
+ const MODEL = `${PERSIST_ANNOTATION}
2284
+ ${FLIGHTS_SOURCE}
2285
+
2286
+ #@ persist
2287
+ source: by_carrier is flights -> {
2288
+ group_by: carrier
2289
+ aggregate: flight_count is count()
2290
+ }
2291
+ `;
2292
+ const QUERY = 'run: by_carrier -> { select: * }';
2293
+
2294
+ async function manifestForByCarrier(): Promise<BuildManifest> {
2295
+ const model = await wrapTestModel(tstRuntime, MODEL).model.getModel();
2296
+ const {manifest} = await buildManifestFor(
2297
+ model.getBuildPlan(),
2298
+ 'by_carrier',
2299
+ 'cached.by_carrier'
2300
+ );
2301
+ return manifest;
2302
+ }
2303
+
2304
+ function plainRuntime() {
2305
+ return new SingleConnectionRuntime({
2306
+ connection: tstRuntime.connection,
2307
+ urlReader: testFileSpace,
2308
+ });
2309
+ }
2310
+
2311
+ it('applies a manifest passed to the call', async () => {
2312
+ const manifest = await manifestForByCarrier();
2313
+ const sql = await plainRuntime()
2314
+ .loadModel(MODEL)
2315
+ .loadQuery(QUERY, {buildManifest: manifest})
2316
+ .getSQL();
2317
+ expect(sql).toContain('cached.by_carrier');
2318
+ });
2319
+
2320
+ it('applies a manifest passed to loadModel', async () => {
2321
+ const manifest = await manifestForByCarrier();
2322
+ const sql = await plainRuntime()
2323
+ .loadModel(MODEL, {buildManifest: manifest})
2324
+ .loadQuery(QUERY)
2325
+ .getSQL();
2326
+ expect(sql).toContain('cached.by_carrier');
2327
+ });
2328
+
2329
+ it('EMPTY_BUILD_MANIFEST suppresses the runtime manifest', async () => {
2330
+ // The dangerous direction: asking for unsubstituted SQL and being
2331
+ // handed the substituted form.
2332
+ const manifest = await manifestForByCarrier();
2333
+ const sql = await runtimeWithManifest(manifest)
2334
+ .loadModel(MODEL)
2335
+ .loadQuery(QUERY, {buildManifest: EMPTY_BUILD_MANIFEST})
2336
+ .getSQL();
2337
+ expect(sql).not.toContain('cached.by_carrier');
2338
+ expect(sql).toContain('COUNT(');
2339
+ });
2340
+
2341
+ it('loadQueryByName applies a manifest passed to the call', async () => {
2342
+ // The sibling loaders merge their options; this is the control.
2343
+ const manifest = await manifestForByCarrier();
2344
+ const sql = await plainRuntime()
2345
+ .loadModel(`${MODEL}\nquery: named is by_carrier -> { select: * }`)
2346
+ .loadQueryByName('named', {buildManifest: manifest})
2347
+ .getSQL();
2348
+ expect(sql).toContain('cached.by_carrier');
2349
+ });
2350
+
2351
+ it('Runtime.loadQuery applies a manifest passed to the call', async () => {
2352
+ // The other control: the same options on the Runtime entry point.
2353
+ const manifest = await manifestForByCarrier();
2354
+ const sql = await plainRuntime()
2355
+ .loadQuery(`${MODEL}\n${QUERY}`, {buildManifest: manifest})
2356
+ .getSQL();
2357
+ expect(sql).toContain('cached.by_carrier');
2358
+ });
2359
+
2360
+ it('applies defaultRowLimit passed to the call', async () => {
2361
+ // buildManifest is not special: nothing in CompileQueryOptions
2362
+ // survives this call.
2363
+ const sql = await plainRuntime()
2364
+ .loadModel(MODEL)
2365
+ .loadQuery('run: flights -> { select: carrier }', {
2366
+ defaultRowLimit: 7,
2367
+ })
2368
+ .getSQL();
2369
+ expect(sql).toContain('LIMIT 7');
2370
+ });
2371
+ });
2182
2372
  });