@malloydata/malloy-tests 0.0.432 → 0.0.434

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/CONTEXT.md CHANGED
@@ -140,7 +140,7 @@ These commands are optimized for CI and may not work correctly in local developm
140
140
 
141
141
  ### The test corpus
142
142
 
143
- The shared cross-database parquet files live in `test/data/malloytest-parquet/` (~60 MB total, checked into git). These are the source of truth — every dialect loads from or mirrors this data. DuckDB-only files remain in `test/data/duckdb/`.
143
+ The shared cross-database parquet files live in `test/data/malloytest-parquet/` (~15 MB, checked into git). They are the only description of the test data: every database is loaded from them by a script in the repo (BigQuery excepted, see below). When the parquet changes, the loaders run again; there is no dump to edit. DuckDB-only files remain in `test/data/duckdb/`.
144
144
 
145
145
  #### Shared parquets (`test/data/malloytest-parquet/`)
146
146
 
@@ -171,14 +171,23 @@ All cross-database tests (`test/src/databases/all/`) reference tables as `malloy
171
171
 
172
172
  | Dialect | Method | Files | Notes |
173
173
  |---|---|---|---|
174
- | DuckDB | TS script: `CREATE TABLE AS SELECT FROM parquet_scan()` | `test/duckdb/load_test_data.sh` (wraps `load_test_data.ts`) | Run via `sh test/duckdb/load_test_data.sh` (or `npm run build-duckdb-db`). Creates `test/data/duckdb/duckdb_test.db` |
175
- | PostgreSQL | Compressed SQL dump loaded via Docker `psql` | `test/data/postgres/malloytest-postgres.sql.gz` | Docker script: `test/postgres/postgres_start.sh` |
176
- | MySQL | Compressed SQL dump loaded via Docker | `test/data/mysql/malloytest.mysql.gz` | Docker script: `test/mysql/mysql_start.sh` |
174
+ | DuckDB | DuckDB `CREATE TABLE AS SELECT FROM read_parquet()` | `test/duckdb/load_test_data.sh` (wraps `load_test_data.ts`) | Run via `sh test/duckdb/load_test_data.sh` (or `npm run build-duckdb-db`). Creates `test/data/duckdb/duckdb_test.db` |
175
+ | PostgreSQL | DuckDB's `postgres` extension: `ATTACH` the server, `CREATE TABLE AS SELECT FROM read_parquet()` | `test/postgres/load_test_data.sh` (wraps `load_test_data.ts`) | Run by `test/postgres/postgres_start.sh` and by CI against a running server; connects with the `PG*` variables. No `ga_sample` (no anonymous record type) |
176
+ | MySQL | DuckDB's `mysql` extension, likewise | `test/mysql/load_test_data.sh` (wraps `load_test_data.ts`) | Run by `test/mysql/mysql_start.sh`; connects with the `MYSQL_*` variables. No `ga_sample`, and `alltypes` without its array columns |
177
+ | SQL Server | DuckDB's community `mssql` extension, likewise | `test/mssql/load_test_data.sh` (wraps `load_test_data.ts`) | Run by `test/mssql/mssql_start.sh`; connects as the test runtime does (`sa`, localhost:1433). Waits for the server itself. Same table set as MySQL. Not in CI |
178
+ | Trino/Presto | The hive connector reads the parquet in place: each file is mounted into the container and declared as an external table | `test/trino/hive_ddl.ts`, `test/trino/hive.properties`, `test/presto/hive.properties` | `trino_start.sh` / `presto_start.sh` derive the DDL from the parquet schemas at container start. The hive metastore lowercases names, so a table with nested columns (`ga_sample`) is a `_hive` table plus a view that casts the nested field names back; top-level column names stay lowercase, since Trino lowercases those in every connector |
177
179
  | BigQuery | Pre-loaded manually in `malloydata-org` project | (none) | No loader script in repo. Data assumed to exist. |
178
180
  | Snowflake | SQL: `PUT` local parquet → stage, `COPY INTO` table | `test/snowflake/load_test_data.sh` (wraps `load_test_data.sql`) | Run via `sh test/snowflake/load_test_data.sh` (needs the `snowsql` CLI) |
179
- | Trino/Presto | Docker containers with pre-loaded data | `test/trino/trino_start.sh` | Uses Docker volumes |
180
181
  | Databricks | TS script: upload to Volume via REST, `CREATE TABLE AS SELECT FROM read_files()` | `test/databricks/load_test_data.sh` (wraps `load_test_data.ts`) | Run manually with the `DATABRICKS_*` env vars set: `sh test/databricks/load_test_data.sh` |
181
182
 
183
+ The DuckDB-driven loaders share `test/data/parquet_loader.ts`, which runs everything through Malloy's own `DuckDBConnection`, so a database is built by the same DuckDB the tests read it with. It reads the table list from the parquet directory and takes a `LoadPlan` naming what a target cannot hold.
184
+
185
+ ### The flights timestamps
186
+
187
+ `flights.dep_time` and `arr_time` are UTC instants whose digits are the local clock at the departure and arrival airport. The source recorded each airport's wall clock and the zone was not kept, so a 6:45 pm departure from LAX is the instant 18:45 UTC. The parquet declares them so (`tz=UTC`), every dialect holds them as instants, and under Malloy's UTC session pin every dialect renders the airport clock. An instant is the one time type every dialect can hold; Malloy has no wall-clock type, and several dialects have none either. Elapsed time between the two columns is not computable from them on a route that crosses zones; `flight_time`, `taxi_out` and `taxi_in` carry it.
188
+
189
+ A loader must carry the instant through unchanged. Malloy's `DuckDBConnection`, which the loaders run through, pins its session zone to UTC, so a target without a zoned type (MySQL `datetime`) receives the same digits, where a loader in the machine's zone would shift them. Trino and Presto read the parquet's UTC microseconds directly.
190
+
182
191
  ### Cloud warehouse considerations
183
192
 
184
193
  Cloud SQL warehouses (BigQuery, Snowflake, Databricks) can't read local files via SQL. Each needs a mechanism to get parquet data into the warehouse:
@@ -0,0 +1,106 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ /* eslint-disable no-console */
7
+
8
+ /*
9
+ * The malloytest tables are the parquet files in test/data/malloytest-parquet,
10
+ * one table per file. Each database's load_test_data script copies them into
11
+ * that database through DuckDB, so the parquet is the only description of the
12
+ * test data. See "Test Data" in test/CONTEXT.md.
13
+ */
14
+
15
+ import type {QueryRecord} from '@malloydata/malloy';
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import {DuckDBConnection} from '../../packages/malloy-db-duckdb';
19
+
20
+ export const parquetDir = path.resolve(__dirname, 'malloytest-parquet');
21
+
22
+ export function parquetTables(): string[] {
23
+ return fs
24
+ .readdirSync(parquetDir)
25
+ .filter(file => file.endsWith('.parquet'))
26
+ .map(file => file.slice(0, -'.parquet'.length))
27
+ .sort();
28
+ }
29
+
30
+ export function parquetPath(table: string): string {
31
+ return path.join(parquetDir, `${table}.parquet`);
32
+ }
33
+
34
+ export interface LoadPlan {
35
+ /** Tables the target cannot hold at all */
36
+ skip?: string[];
37
+ /** Columns to leave out of a table, for types the target lacks */
38
+ omit?: {table: string; columns: string[]}[];
39
+ }
40
+
41
+ /** The plan for a target with no array or record types */
42
+ export const scalarTypesOnly: LoadPlan = {
43
+ skip: ['ga_sample'],
44
+ omit: [{table: 'alltypes', columns: ['t_array_string', 't_array_int64']}],
45
+ };
46
+
47
+ /** A DuckDB session that runs SQL and returns the rows */
48
+ export interface TestDataDuckDB {
49
+ run(sql: string): Promise<QueryRecord[]>;
50
+ close(): Promise<void>;
51
+ }
52
+
53
+ /**
54
+ * Malloy's own DuckDB connection, so a database is built by the same DuckDB
55
+ * the tests read it with. That connection pins the session zone to UTC,
56
+ * which a loader needs: the parquet timestamps are instants, and a target
57
+ * without a zoned timestamp type must receive their UTC digits.
58
+ */
59
+ export async function openDuckDB(
60
+ databasePath = ':memory:'
61
+ ): Promise<TestDataDuckDB> {
62
+ const connection = new DuckDBConnection('loader', databasePath);
63
+ return {
64
+ run: async sql => (await connection.runRawSQL(sql)).rows,
65
+ close: () => connection.close(),
66
+ };
67
+ }
68
+
69
+ export async function loadExtension(
70
+ db: TestDataDuckDB,
71
+ extension: string,
72
+ repository?: 'community'
73
+ ): Promise<void> {
74
+ const from = repository ? ` FROM ${repository}` : '';
75
+ await db.run(`INSTALL ${extension}${from}`);
76
+ await db.run(`LOAD ${extension}`);
77
+ }
78
+
79
+ /**
80
+ * Replace `schema` with the malloytest tables. `schema` may live in an
81
+ * attached database (`pg.malloytest`).
82
+ */
83
+ export async function loadMalloytest(
84
+ db: TestDataDuckDB,
85
+ schema: string,
86
+ plan: LoadPlan = {}
87
+ ): Promise<void> {
88
+ await db.run(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
89
+ await db.run(`CREATE SCHEMA ${schema}`);
90
+ for (const table of parquetTables()) {
91
+ if (plan.skip?.includes(table)) {
92
+ console.log(` ${table}: skipped`);
93
+ continue;
94
+ }
95
+ const omit = plan.omit?.find(o => o.table === table)?.columns ?? [];
96
+ const columns =
97
+ omit.length > 0
98
+ ? `* EXCLUDE (${omit.map(c => `"${c}"`).join(', ')})`
99
+ : '*';
100
+ await db.run(
101
+ `CREATE TABLE ${schema}.${table} AS SELECT ${columns} FROM read_parquet('${parquetPath(table)}')`
102
+ );
103
+ const rows = await db.run(`SELECT count(*) AS n FROM ${schema}.${table}`);
104
+ console.log(` ${table}: ${rows[0]['n']} rows`);
105
+ }
106
+ }
@@ -5,57 +5,24 @@
5
5
 
6
6
  /* eslint-disable no-console */
7
7
 
8
- import {DuckDBConnection} from '../../packages/malloy-db-duckdb';
9
-
10
- import fs from 'fs';
11
-
12
- const parquetDir = './test/data/malloytest-parquet/';
13
- const duckdbDir = './test/data/duckdb/';
14
- const databasePath = `${duckdbDir}duckdb_test.db`;
15
- if (fs.existsSync(databasePath)) {
16
- console.log(`Database at ${databasePath} already exists, removing`);
17
- fs.rmSync(databasePath);
18
- }
19
-
20
- const database = new DuckDBConnection('duckdb', databasePath);
8
+ /*
9
+ * Build the DuckDB test database, test/data/duckdb/duckdb_test.db, from the
10
+ * parquet files.
11
+ */
21
12
 
22
- const run = (sql: string) => {
23
- return database.runRawSQL(sql);
24
- };
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import {loadMalloytest, openDuckDB} from '../data/parquet_loader';
25
16
 
26
- console.log(`Creating database at ${databasePath}`);
17
+ const databasePath = path.resolve(__dirname, '../data/duckdb/duckdb_test.db');
27
18
 
28
19
  (async () => {
29
- try {
30
- await run('CREATE SCHEMA malloytest');
31
- await run(
32
- `CREATE TABLE malloytest.aircraft AS SELECT * FROM parquet_scan('${parquetDir}aircraft.parquet')`
33
- );
34
- await run(
35
- `CREATE TABLE malloytest.aircraft_models AS SELECT * FROM parquet_scan('${parquetDir}aircraft_models.parquet')`
36
- );
37
- await run(
38
- `CREATE TABLE malloytest.airports AS SELECT * FROM parquet_scan('${parquetDir}airports.parquet')`
39
- );
40
- await run(
41
- `CREATE TABLE malloytest.alltypes AS SELECT * FROM parquet_scan('${parquetDir}alltypes.parquet')`
42
- );
43
- await run(
44
- `CREATE TABLE malloytest.carriers AS SELECT * FROM parquet_scan('${parquetDir}carriers.parquet')`
45
- );
46
- await run(
47
- `CREATE TABLE malloytest.flights AS SELECT * FROM parquet_scan('${parquetDir}flights.parquet')`
48
- );
49
- await run(
50
- `CREATE TABLE malloytest.state_facts AS SELECT * FROM parquet_scan('${parquetDir}state_facts.parquet')`
51
- );
52
- await run(
53
- `CREATE TABLE malloytest.ga_sample AS SELECT * FROM parquet_scan('${parquetDir}ga_sample.parquet')`
54
- );
55
- console.log('Finished populating database with data from parqeut files');
56
- } catch (e) {
57
- console.log(e);
20
+ if (fs.existsSync(databasePath)) {
21
+ console.log(`Database at ${databasePath} already exists, removing`);
22
+ fs.rmSync(databasePath);
58
23
  }
59
-
60
- await database.close();
24
+ console.log(`Creating database at ${databasePath}`);
25
+ const loader = await openDuckDB(databasePath);
26
+ await loadMalloytest(loader, 'malloytest');
27
+ await loader.close();
61
28
  })();
@@ -0,0 +1,12 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ /**
7
+ * The SQL Server the mssql tests and loader connect to: the container
8
+ * mssql_start.sh runs, with the password it sets.
9
+ */
10
+ export function mssqlConnectionString(database: string): string {
11
+ return `Server=localhost;Port=1433;Database=${database};User Id=sa;Password=Malloy_Test_123;TrustServerCertificate=true`;
12
+ }
@@ -0,0 +1,12 @@
1
+ #! /bin/bash
2
+ #
3
+ # Load the malloytest tables into a running SQL Server from
4
+ # test/data/malloytest-parquet. Waits for the server itself.
5
+ #
6
+ set -e
7
+
8
+ SCRIPTDIR=$(cd $(dirname $0); pwd)
9
+ ROOTDIR=$(cd "$SCRIPTDIR/../.."; pwd)
10
+
11
+ cd "$ROOTDIR"
12
+ npx ts-node test/mssql/load_test_data.ts
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ /* eslint-disable no-console */
7
+
8
+ /*
9
+ * Load the malloytest tables into a running SQL Server from the parquet
10
+ * files, through DuckDB's mssql extension. Connects as the mssql test
11
+ * connection does. Waits for the server first, since the container image
12
+ * ships no client to wait with.
13
+ */
14
+
15
+ import {
16
+ loadExtension,
17
+ loadMalloytest,
18
+ openDuckDB,
19
+ scalarTypesOnly,
20
+ } from '../data/parquet_loader';
21
+ import {mssqlConnectionString} from './connection_string';
22
+
23
+ const database = 'malloytest';
24
+
25
+ function sleep(ms: number): Promise<void> {
26
+ return new Promise(resolve => setTimeout(resolve, ms));
27
+ }
28
+
29
+ (async () => {
30
+ const db = await openDuckDB();
31
+ await loadExtension(db, 'mssql', 'community');
32
+
33
+ // Attach the always-present master database, retrying while the server
34
+ // comes up, then recreate the test database from scratch.
35
+ const deadline = Date.now() + 120_000;
36
+ for (;;) {
37
+ try {
38
+ await db.run(
39
+ `ATTACH '${mssqlConnectionString('master')}' AS ms (TYPE mssql)`
40
+ );
41
+ break;
42
+ } catch (e) {
43
+ if (Date.now() > deadline) {
44
+ throw e;
45
+ }
46
+ const reason = e instanceof Error ? e.message.split('\n')[0] : `${e}`;
47
+ console.log(` waiting for SQL Server: ${reason}`);
48
+ await sleep(2000);
49
+ }
50
+ }
51
+ await db.run(
52
+ `SELECT mssql_exec('ms', 'IF DB_ID(''${database}'') IS NOT NULL DROP DATABASE ${database}')`
53
+ );
54
+ await db.run(`SELECT mssql_exec('ms', 'CREATE DATABASE ${database}')`);
55
+ await db.run('DETACH ms');
56
+ await db.run(
57
+ `ATTACH '${mssqlConnectionString(database)}' AS ms (TYPE mssql)`
58
+ );
59
+
60
+ console.log('Loading malloytest into mssql');
61
+ await loadMalloytest(db, `ms.${database}`, scalarTypesOnly);
62
+ await db.close();
63
+ })();
@@ -1,17 +1,14 @@
1
1
  #!/bin/bash
2
+ #
3
+ # Start a SQL Server container and load the malloytest tables into it from
4
+ # test/data/malloytest-parquet.
5
+ #
2
6
  set -e
3
7
 
4
8
  SCRIPTDIR=$(cd $(dirname $0); pwd)
5
- DATADIR=$(dirname $SCRIPTDIR)/data/malloytest-parquet
6
9
  CONTAINER_NAME="mssql-malloy"
10
+ # Must match test/mssql/connection_string.ts
7
11
  SA_PASSWORD="Malloy_Test_123"
8
- DB_NAME="malloytest"
9
-
10
- # Require duckdb CLI (used for readiness check and data loading)
11
- if ! command -v duckdb > /dev/null 2>&1; then
12
- echo "Error: duckdb CLI is required but not found"
13
- exit 1
14
- fi
15
12
 
16
13
  # Check for existing container
17
14
  if docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1; then
@@ -21,13 +18,8 @@ if docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1; then
21
18
  fi
22
19
  echo "Restarting existing $CONTAINER_NAME container..."
23
20
  docker start "$CONTAINER_NAME"
24
- echo -n "Waiting for MSSQL..."
25
- for i in $(seq 1 60); do
26
- duckdb -c "LOAD mssql; ATTACH 'Server=localhost;Port=1433;Database=master;User Id=sa;Password=$SA_PASSWORD;TrustServerCertificate=true' AS _ping (TYPE mssql);" > /dev/null 2>&1 && break
27
- echo -n "."
28
- sleep 2
29
- done
30
- echo " ready"
21
+ # The loader waits for the server; reloading is cheap
22
+ sh "$SCRIPTDIR/load_test_data.sh"
31
23
  echo "MSSQL running on port 1433"
32
24
  exit 0
33
25
  fi
@@ -48,122 +40,11 @@ docker run -d \
48
40
  -p 1433:1433 \
49
41
  "$IMAGE"
50
42
 
51
- # Wait for server to accept connections (check via DuckDB mssql extension)
52
- counter=0
53
- echo -n "Waiting for MSSQL..."
54
- while true; do
55
- duckdb -c "LOAD mssql; ATTACH 'Server=localhost;Port=1433;Database=master;User Id=sa;Password=$SA_PASSWORD;TrustServerCertificate=true' AS _ping (TYPE mssql);" > /dev/null 2>&1 && break
56
- counter=$((counter + 1))
57
- if [ $counter -ge 60 ]; then
58
- echo
59
- echo "MSSQL did not start within 2 minutes"
60
- docker logs "$CONTAINER_NAME" 2>&1 | tail -20
61
- docker rm -f "$CONTAINER_NAME"
62
- exit 1
63
- fi
64
- echo -n "."
65
- sleep 2
66
- done
67
- echo " ready"
68
-
69
- # Load test data using DuckDB + mssql extension
43
+ # The loader waits for the server. A container without its tables must not
44
+ # survive to be "already running".
45
+ trap 'docker rm -f "$CONTAINER_NAME" > /dev/null; echo "MSSQL setup failed"' ERR
70
46
  echo "Loading test data..."
71
- duckdb -c "
72
- LOAD mssql;
73
-
74
- -- Create database
75
- ATTACH 'Server=localhost;Port=1433;Database=master;User Id=sa;Password=$SA_PASSWORD;TrustServerCertificate=true' AS msdb (TYPE mssql);
76
- SELECT mssql_exec('msdb', 'IF DB_ID(''$DB_NAME'') IS NOT NULL DROP DATABASE $DB_NAME');
77
- SELECT mssql_exec('msdb', 'CREATE DATABASE $DB_NAME');
78
- DETACH msdb;
79
-
80
- -- Connect to test database
81
- ATTACH 'Server=localhost;Port=1433;Database=$DB_NAME;User Id=sa;Password=$SA_PASSWORD;TrustServerCertificate=true' AS msdb (TYPE mssql);
82
-
83
- -- Create malloytest schema so table paths (malloytest.flights etc.) match DuckDB tests
84
- SELECT mssql_exec('msdb', 'CREATE SCHEMA malloytest');
85
-
86
- -- Create tables: schemas must match the parquet files in test/data/malloytest-parquet/.
87
- -- If a parquet schema changes, update the corresponding CREATE TABLE below.
88
- -- All columns explicitly nullable to match parquet semantics.
89
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.aircraft (
90
- id BIGINT NULL, tail_num NVARCHAR(MAX) NULL, aircraft_serial NVARCHAR(MAX) NULL,
91
- aircraft_model_code NVARCHAR(MAX) NULL, aircraft_engine_code NVARCHAR(MAX) NULL,
92
- year_built INT NULL, aircraft_type_id INT NULL, aircraft_engine_type_id INT NULL,
93
- registrant_type_id INT NULL, name NVARCHAR(MAX) NULL, address1 NVARCHAR(MAX) NULL,
94
- address2 NVARCHAR(MAX) NULL, city NVARCHAR(MAX) NULL, state NVARCHAR(MAX) NULL,
95
- zip NVARCHAR(MAX) NULL, region NVARCHAR(MAX) NULL, county NVARCHAR(MAX) NULL,
96
- country NVARCHAR(MAX) NULL, certification NVARCHAR(MAX) NULL, status_code NVARCHAR(MAX) NULL,
97
- mode_s_code NVARCHAR(MAX) NULL, fract_owner NVARCHAR(MAX) NULL,
98
- last_action_date NVARCHAR(MAX) NULL, cert_issue_date NVARCHAR(MAX) NULL,
99
- air_worth_date NVARCHAR(MAX) NULL
100
- )');
101
-
102
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.aircraft_models (
103
- aircraft_model_code NVARCHAR(MAX) NULL, manufacturer NVARCHAR(MAX) NULL,
104
- model NVARCHAR(MAX) NULL, aircraft_type_id INT NULL, aircraft_engine_type_id INT NULL,
105
- aircraft_category_id INT NULL, amateur INT NULL, engines INT NULL,
106
- seats INT NULL, weight INT NULL, speed INT NULL
107
- )');
108
-
109
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.airports (
110
- id INT NULL, code NVARCHAR(MAX) NULL, site_number NVARCHAR(MAX) NULL,
111
- fac_type NVARCHAR(MAX) NULL, fac_use NVARCHAR(MAX) NULL, faa_region NVARCHAR(MAX) NULL,
112
- faa_dist NVARCHAR(MAX) NULL, city NVARCHAR(MAX) NULL, county NVARCHAR(MAX) NULL,
113
- state NVARCHAR(MAX) NULL, full_name NVARCHAR(MAX) NULL, own_type NVARCHAR(MAX) NULL,
114
- longitude FLOAT NULL, latitude FLOAT NULL, elevation INT NULL,
115
- aero_cht NVARCHAR(MAX) NULL, cbd_dist INT NULL, cbd_dir NVARCHAR(MAX) NULL,
116
- act_date NVARCHAR(MAX) NULL, cert NVARCHAR(MAX) NULL, fed_agree NVARCHAR(MAX) NULL,
117
- cust_intl NVARCHAR(MAX) NULL, c_ldg_rts NVARCHAR(MAX) NULL, joint_use NVARCHAR(MAX) NULL,
118
- mil_rts NVARCHAR(MAX) NULL, cntl_twr NVARCHAR(MAX) NULL, major NVARCHAR(MAX) NULL
119
- )');
120
-
121
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.carriers (
122
- code NVARCHAR(MAX) NULL, name NVARCHAR(MAX) NULL, nickname NVARCHAR(MAX) NULL
123
- )');
124
-
125
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.flights (
126
- carrier NVARCHAR(MAX) NULL, origin NVARCHAR(MAX) NULL, destination NVARCHAR(MAX) NULL,
127
- flight_num INT NULL, flight_time INT NULL, tail_num NVARCHAR(MAX) NULL,
128
- dep_time DATETIME NULL, arr_time DATETIME NULL, dep_delay INT NULL,
129
- arr_delay INT NULL, taxi_out INT NULL, taxi_in INT NULL, distance INT NULL,
130
- cancelled NVARCHAR(MAX) NULL, diverted NVARCHAR(MAX) NULL, id2 INT NULL
131
- )');
132
-
133
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.state_facts (
134
- state NVARCHAR(MAX) NULL, aircraft_count INT NULL, airport_count INT NULL,
135
- births INT NULL, popular_name NVARCHAR(MAX) NULL
136
- )');
137
-
138
- SELECT mssql_exec('msdb', 'CREATE TABLE malloytest.alltypes (
139
- t_int64 BIGINT NULL, t_float64 FLOAT NULL,
140
- t_numeric DECIMAL(38,9) NULL, t_bignumeric DECIMAL(38,9) NULL,
141
- string NVARCHAR(MAX) NULL,
142
- t_bool_true BIT NULL, t_bool_false BIT NULL, t_bool_null BIT NULL,
143
- t_date DATE NULL, t_datetime DATETIME NULL, t_timestamp DATETIME NULL
144
- )');
145
-
146
- SELECT mssql_refresh_cache('msdb');
147
-
148
- -- Load data from parquet files
149
- INSERT INTO msdb.malloytest.aircraft SELECT * FROM read_parquet('$DATADIR/aircraft.parquet');
150
- INSERT INTO msdb.malloytest.aircraft_models SELECT * FROM read_parquet('$DATADIR/aircraft_models.parquet');
151
- INSERT INTO msdb.malloytest.airports SELECT * FROM read_parquet('$DATADIR/airports.parquet');
152
- INSERT INTO msdb.malloytest.carriers SELECT * FROM read_parquet('$DATADIR/carriers.parquet');
153
- INSERT INTO msdb.malloytest.flights SELECT * FROM read_parquet('$DATADIR/flights.parquet');
154
- INSERT INTO msdb.malloytest.state_facts SELECT * FROM read_parquet('$DATADIR/state_facts.parquet');
155
- INSERT INTO msdb.malloytest.alltypes SELECT t_int64, t_float64, t_numeric, t_bignumeric, string, t_bool_true, t_bool_false, t_bool_null, t_date, t_datetime, t_timestamp FROM read_parquet('$DATADIR/alltypes.parquet');
156
-
157
- -- Verify row counts
158
- SELECT 'aircraft' as tbl, COUNT(*) as rows FROM msdb.malloytest.aircraft
159
- UNION ALL SELECT 'aircraft_models', COUNT(*) FROM msdb.malloytest.aircraft_models
160
- UNION ALL SELECT 'airports', COUNT(*) FROM msdb.malloytest.airports
161
- UNION ALL SELECT 'carriers', COUNT(*) FROM msdb.malloytest.carriers
162
- UNION ALL SELECT 'flights', COUNT(*) FROM msdb.malloytest.flights
163
- UNION ALL SELECT 'state_facts', COUNT(*) FROM msdb.malloytest.state_facts
164
- UNION ALL SELECT 'alltypes', COUNT(*) FROM msdb.malloytest.alltypes;
165
- "
47
+ sh "$SCRIPTDIR/load_test_data.sh"
48
+ trap - ERR
166
49
 
167
- echo
168
- echo "MSSQL running on port 1433, database: $DB_NAME"
169
- echo " Connection: Server=localhost;Port=1433;Database=$DB_NAME;User Id=sa;Password=$SA_PASSWORD;TrustServerCertificate=true"
50
+ echo "MSSQL running on port 1433, database: malloytest"
@@ -0,0 +1,12 @@
1
+ #! /bin/bash
2
+ #
3
+ # Load the malloytest tables into a running mysql from
4
+ # test/data/malloytest-parquet. Connects with the MYSQL_* variables.
5
+ #
6
+ set -e
7
+
8
+ SCRIPTDIR=$(cd $(dirname $0); pwd)
9
+ ROOTDIR=$(cd "$SCRIPTDIR/../.."; pwd)
10
+
11
+ cd "$ROOTDIR"
12
+ npx ts-node test/mysql/load_test_data.ts
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ /* eslint-disable no-console */
7
+
8
+ /*
9
+ * Load the malloytest tables into a running mysql from the parquet files.
10
+ * Connects with the same MYSQL_* variables, read the same way, as the mysql
11
+ * test connection.
12
+ */
13
+
14
+ import {MySQLExecutor} from '../../packages/malloy-db-mysql';
15
+ import {
16
+ loadExtension,
17
+ loadMalloytest,
18
+ openDuckDB,
19
+ scalarTypesOnly,
20
+ } from '../data/parquet_loader';
21
+
22
+ const options = MySQLExecutor.getConnectionOptionsFromEnv();
23
+ const attach = [
24
+ ...(options.host ? [`host=${options.host}`] : []),
25
+ ...(options.port ? [`port=${options.port}`] : []),
26
+ ...(options.user ? [`user=${options.user}`] : []),
27
+ ...(options.password ? [`password=${options.password}`] : []),
28
+ // The system database is always there; the load creates malloytest itself
29
+ 'database=mysql',
30
+ ].join(' ');
31
+
32
+ (async () => {
33
+ const db = await openDuckDB();
34
+ await loadExtension(db, 'mysql');
35
+ await db.run(`ATTACH '${attach}' AS my (TYPE mysql)`);
36
+ console.log('Loading malloytest into mysql');
37
+ await loadMalloytest(db, 'my.malloytest', scalarTypesOnly);
38
+ await db.close();
39
+ })();
@@ -4,11 +4,31 @@ set -e
4
4
  rm -rf .tmp
5
5
  mkdir .tmp
6
6
 
7
- # run docker
8
7
  SCRIPTDIR=$(cd $(dirname $0); pwd)
9
- DATADIR=$(dirname $SCRIPTDIR)/data/mysql
10
8
  CONTAINER_NAME="mysql-malloy"
11
9
 
10
+ # The image starts mysqld twice: a temporary server to initialize the data
11
+ # directory, listening on the unix socket only, and then the real server on
12
+ # 3306. Both log "mysqld: ready for connections", so the log cannot say which
13
+ # one is up. Wait on a TCP connection, which is what the loader needs.
14
+ wait_for_mysql() {
15
+ local counter=0
16
+ echo -n "Waiting for $CONTAINER_NAME ..."
17
+ until docker exec "$CONTAINER_NAME" mysqladmin ping --protocol=TCP -h127.0.0.1 -uroot --silent > /dev/null 2>&1
18
+ do
19
+ counter=$((counter+1))
20
+ # give up after 2 minutes
21
+ if [ $counter -eq 60 ]
22
+ then
23
+ echo
24
+ return 1
25
+ fi
26
+ echo -n .
27
+ sleep 2
28
+ done
29
+ echo
30
+ }
31
+
12
32
  # Check for existing container
13
33
  if docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1; then
14
34
  if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME")" = "true" ]; then
@@ -17,39 +37,25 @@ if docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1; then
17
37
  fi
18
38
  echo "Restarting existing $CONTAINER_NAME container..."
19
39
  docker start "$CONTAINER_NAME"
20
- while ! docker logs "$CONTAINER_NAME" 2>&1 | grep -q "mysqld: ready for connections"; do
21
- sleep 2
22
- done
40
+ if ! wait_for_mysql; then
41
+ docker logs "$CONTAINER_NAME" >& ./.tmp/mysql-malloy.logs
42
+ echo "MySQL did not restart successfully, check .tmp/mysql-malloy.logs"
43
+ exit 1
44
+ fi
23
45
  echo "MySQL running on port 3306"
24
46
  exit 0
25
47
  fi
26
48
 
27
- docker run -p 3306:3306 -d -v $DATADIR:/init_data --name "$CONTAINER_NAME" -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d mysql:8.4.2
28
-
29
- # wait for server to start
30
- counter=0
31
- echo -n Starting Docker ...
32
- while ! docker logs "$CONTAINER_NAME" 2>&1 | grep -q "mysqld: ready for connections"
33
- do
34
- sleep 10
35
- counter=$((counter+1))
36
- # if doesn't start after 2 minutes, output logs and kill process
37
- if [ $counter -eq 120 ]
38
- then
39
- docker logs "$CONTAINER_NAME" >& ./.tmp/mysql-malloy.logs
40
- docker rm -f "$CONTAINER_NAME"
41
- echo "MySQL did not start successfully, check .tmp/mysql-malloy.logs"
42
- exit 1
43
- break
44
- fi
45
- echo -n ...
46
- done
49
+ docker run -p 3306:3306 -d --name "$CONTAINER_NAME" -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:8.4.2
50
+
51
+ if ! wait_for_mysql; then
52
+ docker logs "$CONTAINER_NAME" >& ./.tmp/mysql-malloy.logs
53
+ docker rm -f "$CONTAINER_NAME"
54
+ echo "MySQL did not start successfully, check .tmp/mysql-malloy.logs"
55
+ exit 1
56
+ fi
47
57
 
48
- # load the test data.
49
- echo
50
58
  echo Loading Test Data
51
- docker exec "$CONTAINER_NAME" cp /init_data/malloytest.mysql.gz /tmp
52
- docker exec "$CONTAINER_NAME" gunzip /tmp/malloytest.mysql.gz
53
- docker exec "$CONTAINER_NAME" mysql -P3306 -h127.0.0.1 -uroot -e 'drop database if exists malloytest; create database malloytest; use malloytest; source /tmp/malloytest.mysql;'
59
+ MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_USER=root sh "$SCRIPTDIR/load_test_data.sh"
54
60
 
55
61
  echo "MySQL running on port 3306"
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.432",
24
- "@malloydata/db-duckdb": "0.0.432",
25
- "@malloydata/db-postgres": "0.0.432",
26
- "@malloydata/db-snowflake": "0.0.432",
27
- "@malloydata/db-trino": "0.0.432",
28
- "@malloydata/malloy": "0.0.432",
29
- "@malloydata/malloy-tag": "0.0.432",
30
- "@malloydata/render": "0.0.432",
31
- "@malloydata/render-validator": "0.0.432",
23
+ "@malloydata/db-bigquery": "0.0.434",
24
+ "@malloydata/db-duckdb": "0.0.434",
25
+ "@malloydata/db-postgres": "0.0.434",
26
+ "@malloydata/db-snowflake": "0.0.434",
27
+ "@malloydata/db-trino": "0.0.434",
28
+ "@malloydata/malloy": "0.0.434",
29
+ "@malloydata/malloy-tag": "0.0.434",
30
+ "@malloydata/render": "0.0.434",
31
+ "@malloydata/render-validator": "0.0.434",
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.432"
45
+ "version": "0.0.434"
46
46
  }
@@ -0,0 +1,12 @@
1
+ #! /bin/bash
2
+ #
3
+ # Load the malloytest tables into a running postgres from
4
+ # test/data/malloytest-parquet. Connects with the PG* variables.
5
+ #
6
+ set -e
7
+
8
+ SCRIPTDIR=$(cd $(dirname $0); pwd)
9
+ ROOTDIR=$(cd "$SCRIPTDIR/../.."; pwd)
10
+
11
+ cd "$ROOTDIR"
12
+ npx ts-node test/postgres/load_test_data.ts