@malloydata/malloy-tests 0.0.433 → 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.
@@ -0,0 +1,148 @@
1
+ /*
2
+ * Copyright Contributors to the Malloy project
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ /* eslint-disable no-console */
7
+
8
+ /*
9
+ * Print the DDL that serves test/data/malloytest-parquet through the trino or
10
+ * presto hive connector, reading each parquet file in place as an external
11
+ * table. The hive metastore lowercases every name, so a table with nested
12
+ * columns is created under a `_hive` suffix and a view casts the row types
13
+ * back to the parquet's field names.
14
+ *
15
+ * ts-node hive_ddl.ts <catalog> <location> [--presto]
16
+ *
17
+ * <location> is the directory holding one directory per table, in the
18
+ * connector's URL scheme: local:///malloytest for trino, file:///data/malloytest
19
+ * for presto.
20
+ */
21
+
22
+ import type {QueryRecord} from '@malloydata/malloy';
23
+ import {TinyParser} from '../../packages/malloy/src/dialect/tiny_parser';
24
+ import {openDuckDB, parquetPath, parquetTables} from '../data/parquet_loader';
25
+
26
+ const [catalog, location, ...flags] = process.argv.slice(2);
27
+ const presto = flags.length === 1 && flags[0] === '--presto';
28
+ if (!catalog || !location || (flags.length > 0 && !presto)) {
29
+ console.error('usage: hive_ddl.ts <catalog> <location> [--presto]');
30
+ process.exit(1);
31
+ }
32
+ // Presto's timestamp is milliseconds and takes no precision; trino's is
33
+ // declared to match hive.timestamp-precision in the catalog properties.
34
+ const timestampType = presto ? 'TIMESTAMP' : 'TIMESTAMP(6)';
35
+
36
+ // DuckDB types hive spells the same way
37
+ const sameNameTypes = new Set([
38
+ 'BIGINT',
39
+ 'INTEGER',
40
+ 'DOUBLE',
41
+ 'VARCHAR',
42
+ 'BOOLEAN',
43
+ 'DATE',
44
+ ]);
45
+
46
+ /**
47
+ * Reads a DuckDB type as DESCRIBE writes it and writes the hive type for the
48
+ * same parquet column: STRUCT(a T, b T) becomes ROW("a" T, "b" T), T[]
49
+ * becomes ARRAY(T), DECIMAL(p,s) and the same-name scalars carry over. The
50
+ * dialect's own DuckDBTypeParser reads the same text into Malloy types, which
51
+ * drop the decimal precision hive needs; this one keeps the SQL spelling.
52
+ */
53
+ class HiveTypeParser extends TinyParser {
54
+ constructor(input: string) {
55
+ super(input, {
56
+ space: /^\s+/,
57
+ qdouble: /^"([^"]|"")*"/,
58
+ precision: /^\(\d+,\d+\)/,
59
+ arrayOf: /^\[]/,
60
+ char: /^[,()]/,
61
+ id: /^\w+/,
62
+ });
63
+ }
64
+
65
+ hiveType(): string {
66
+ const id = this.expect('id').text.toUpperCase();
67
+ let type: string;
68
+ if (id === 'STRUCT') {
69
+ this.expect('(');
70
+ const fields: string[] = [];
71
+ for (;;) {
72
+ const name = this.match('qdouble') ?? this.expect('id');
73
+ fields.push(`"${name.text}" ${this.hiveType()}`);
74
+ if (!this.match(',')) {
75
+ break;
76
+ }
77
+ }
78
+ this.expect(')');
79
+ type = `ROW(${fields.join(', ')})`;
80
+ } else if (id === 'DECIMAL') {
81
+ type = `DECIMAL${this.expect('precision').text}`;
82
+ } else if (id === 'TIMESTAMP') {
83
+ this.matchText('WITH', 'TIME', 'ZONE');
84
+ type = timestampType;
85
+ } else if (sameNameTypes.has(id)) {
86
+ type = id;
87
+ } else {
88
+ throw this.parseError(`No hive type for DuckDB type ${id}`);
89
+ }
90
+ while (this.match('arrayOf')) {
91
+ type = `ARRAY(${type})`;
92
+ }
93
+ return type;
94
+ }
95
+ }
96
+
97
+ function hiveType(duckdbType: string): string {
98
+ const parser = new HiveTypeParser(duckdbType);
99
+ const type = parser.hiveType();
100
+ if (!parser.eof()) {
101
+ throw parser.parseError('Unexpected text after the type');
102
+ }
103
+ return type;
104
+ }
105
+
106
+ function isNested(duckdbType: string): boolean {
107
+ return duckdbType.includes('STRUCT(');
108
+ }
109
+
110
+ function text(row: QueryRecord, column: string): string {
111
+ const value = row[column];
112
+ if (typeof value !== 'string') {
113
+ throw new Error(`DESCRIBE gave no ${column}`);
114
+ }
115
+ return value;
116
+ }
117
+
118
+ (async () => {
119
+ const db = await openDuckDB();
120
+ const ddl = [`CREATE SCHEMA IF NOT EXISTS ${catalog}.malloytest;`];
121
+ for (const table of parquetTables()) {
122
+ const described = await db.run(
123
+ `DESCRIBE SELECT * FROM read_parquet('${parquetPath(table)}')`
124
+ );
125
+ const columns = described.map(row => ({
126
+ name: text(row, 'column_name'),
127
+ type: text(row, 'column_type'),
128
+ }));
129
+ const nested = columns.some(c => isNested(c.type));
130
+ const hiveTable = nested ? `${table}_hive` : table;
131
+ const declarations = columns.map(c => ` "${c.name}" ${hiveType(c.type)}`);
132
+ ddl.push(
133
+ `CREATE TABLE IF NOT EXISTS ${catalog}.malloytest.${hiveTable} (\n${declarations.join(',\n')}\n) WITH (format = 'PARQUET', external_location = '${location}/${table}');`
134
+ );
135
+ if (nested) {
136
+ const select = columns.map(c =>
137
+ isNested(c.type)
138
+ ? ` CAST("${c.name}" AS ${hiveType(c.type)}) AS "${c.name}"`
139
+ : ` "${c.name}"`
140
+ );
141
+ ddl.push(
142
+ `CREATE OR REPLACE VIEW ${catalog}.malloytest.${table} AS SELECT\n${select.join(',\n')}\nFROM ${catalog}.malloytest.${hiveTable};`
143
+ );
144
+ }
145
+ }
146
+ await db.close();
147
+ console.log(ddl.join('\n'));
148
+ })();
@@ -1,25 +1,36 @@
1
1
  #! /bin/bash
2
-
2
+ #
3
+ # Start a trino container whose hive catalog serves test/data/malloytest-parquet
4
+ # as the malloytest schema. The parquet is read in place; nothing is loaded.
5
+ #
3
6
  set -e
4
7
 
8
+ SCRIPTDIR=$(cd $(dirname $0); pwd)
9
+ ROOTDIR=$(cd "$SCRIPTDIR/../.."; pwd)
10
+ DATADIR=$ROOTDIR/test/data/malloytest-parquet
11
+ CONTAINER_NAME="trino-malloy"
12
+ PORT=${TRINO_PORT:-8080}
13
+
14
+ cd "$ROOTDIR"
5
15
  rm -rf .tmp
6
16
  mkdir .tmp
7
17
 
18
+ # Only the log since this script started: a restarted container's old log
19
+ # already says SERVER STARTED.
20
+ SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
8
21
 
9
- if [ "x${BQ_CREDENTIALS_KEY}" = x ]; then
10
- echo "######### BQ_CREDENTIALS_KEY is not set. Cannot start Trino"
11
- exit 1
12
- fi
13
- # generate config file
14
- > ./.tmp/bigquery-trino.properties
15
- cat << EOF > ./.tmp/bigquery-trino.properties
16
- connector.name=bigquery
17
- bigquery.project-id=advance-lacing-417917
18
- bigquery.credentials-key=$BQ_CREDENTIALS_KEY
19
- bigquery.arrow-serialization.enabled=false
20
- EOF
21
-
22
- CONTAINER_NAME="trino-malloy"
22
+ wait_for_trino() {
23
+ local counter=0
24
+ while ! docker logs --since "$SINCE" "$CONTAINER_NAME" 2>&1 | grep -q "SERVER STARTED"; do
25
+ counter=$((counter+1))
26
+ # give up after 5 minutes
27
+ if [ $counter -eq 300 ]; then
28
+ docker logs "$CONTAINER_NAME" >& ./.tmp/trino-malloy.logs
29
+ return 1
30
+ fi
31
+ sleep 1
32
+ done
33
+ }
23
34
 
24
35
  # Check for existing container
25
36
  if docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1; then
@@ -29,31 +40,36 @@ if docker container inspect "$CONTAINER_NAME" > /dev/null 2>&1; then
29
40
  fi
30
41
  echo "Restarting existing $CONTAINER_NAME container..."
31
42
  docker start "$CONTAINER_NAME"
32
- while ! docker logs "$CONTAINER_NAME" 2>&1 | grep -q "SERVER STARTED"; do
33
- sleep 1
34
- done
35
- echo "Trino running on port localhost:8080"
43
+ if ! wait_for_trino; then
44
+ echo "Trino did not restart successfully, check .tmp/trino-malloy.logs"
45
+ exit 1
46
+ fi
47
+ echo "Trino running on port localhost:$PORT"
36
48
  exit 0
37
49
  fi
38
50
 
39
- # run docker
40
- docker run -p ${TRINO_PORT:-8080}:8080 -d -e TZ=UTC -v ./.tmp/bigquery-trino.properties:/etc/trino/catalog/bigquery.properties --name "$CONTAINER_NAME" trinodb/trino
41
-
42
- # wait for server to start
43
- counter=0
44
- while ! docker logs "$CONTAINER_NAME" 2>&1 | grep -q "SERVER STARTED"
45
- do
46
- sleep 1
47
- counter=$((counter+1))
48
- # if doesn't start after 2 minutes, output logs and kill process
49
- if [ $counter -eq 300 ]
50
- then
51
- docker logs "$CONTAINER_NAME" >& ./.tmp/trino-malloy.logs
52
- docker rm -f "$CONTAINER_NAME"
53
- echo "Trino did not start successfully, check .tmp/trino-malloy.logs"
54
- exit 1
55
- break
56
- fi
51
+ # A hive external_location is a directory, so each parquet file gets one.
52
+ MOUNTS=()
53
+ for parquet in "$DATADIR"/*.parquet; do
54
+ table=$(basename "$parquet" .parquet)
55
+ MOUNTS+=(-v "$parquet:/data/malloytest/$table/$table.parquet:ro")
57
56
  done
58
57
 
59
- echo "Trino running on port localhost:8080"
58
+ docker run -p $PORT:8080 -d -e TZ=UTC "${MOUNTS[@]}" \
59
+ -v "$SCRIPTDIR/hive.properties:/etc/trino/catalog/hive.properties:ro" \
60
+ --name "$CONTAINER_NAME" trinodb/trino
61
+
62
+ # A container without its tables must not survive to be "already running"
63
+ trap 'docker rm -f "$CONTAINER_NAME" > /dev/null; echo "Trino setup failed, check .tmp/trino-malloy.logs"' ERR
64
+
65
+ if ! wait_for_trino; then
66
+ false
67
+ fi
68
+
69
+ echo "Creating the malloytest tables"
70
+ npx ts-node test/trino/hive_ddl.ts hive local:///malloytest > .tmp/hive_ddl.sql
71
+ docker cp .tmp/hive_ddl.sql "$CONTAINER_NAME":/tmp/hive_ddl.sql
72
+ docker exec "$CONTAINER_NAME" trino -f /tmp/hive_ddl.sql
73
+ trap - ERR
74
+
75
+ echo "Trino running on port localhost:$PORT"
package/tsconfig.json CHANGED
@@ -6,7 +6,18 @@
6
6
  "jsx": "react-jsx",
7
7
  "jsxImportSource": "solid-js"
8
8
  },
9
- "include": ["src", "bin", "./jest.setup.ts", "./types/jest.d.ts"],
9
+ "include": [
10
+ "src",
11
+ "bin",
12
+ "data",
13
+ "duckdb",
14
+ "mssql",
15
+ "mysql",
16
+ "postgres",
17
+ "trino",
18
+ "./jest.setup.ts",
19
+ "./types/jest.d.ts"
20
+ ],
10
21
  "references": [
11
22
  {
12
23
  "path": "../packages/malloy-interfaces"
@@ -1 +0,0 @@
1
- pg_dump -n malloytest -O -x | gzip > ../sql/malloytest-postgres.sql.gz
@@ -1,10 +0,0 @@
1
- #! /bin/bash
2
- export PGHOST=localhost
3
- pg_ctl -D .tmp/data/malloytestdb -l .tmp/logfile -o "--unix_socket_directories='$PWD/.tmp'" stop
4
- rm -rf .tmp
5
- mkdir .tmp
6
- initdb -d .tmp/data/malloytestdb --no-locale --encoding=UTF8
7
- pg_ctl -D .tmp/data/malloytestdb -l .tmp/logfile -o "--unix_socket_directories='$PWD/.tmp'" start
8
- createdb $USER
9
- gunzip -c ../data/postgres/malloytest-postgres.sql.gz | psql
10
- echo CREATE EXTENSION tsm_system_rows\; | psql
@@ -1,2 +0,0 @@
1
- #! /bin/bash
2
- pg_ctl -D .tmp/data/malloytestdb -l logfile -o "--unix_socket_directories='$PWD'" start
@@ -1,2 +0,0 @@
1
- #!/bin/bash
2
- pg_ctl -D .tmp/data/malloytestdb stop
Binary file
@@ -1,366 +0,0 @@
1
- -- example of how to take a json blob and insert it into a postgres database
2
- -- keeping around because it might be useful as we add new tables.
3
-
4
- create type t as (state varchar, aircraft_count int, airport_count int, births int, popular_name varchar);
5
- create table malloytest.state_facts as (
6
- select p.*
7
- from json_populate_recordset(null::t,'[
8
- {
9
- "state": "MN",
10
- "aircraft_count": "83",
11
- "airport_count": "507",
12
- "births": "5574819",
13
- "popular_name": "Ava"
14
- },
15
- {
16
- "state": "LA",
17
- "aircraft_count": "57",
18
- "airport_count": "500",
19
- "births": "5551025",
20
- "popular_name": "Ava"
21
- },
22
- {
23
- "state": "IA",
24
- "aircraft_count": "28",
25
- "airport_count": "319",
26
- "births": "4108689",
27
- "popular_name": "Ava"
28
- },
29
- {
30
- "state": "IN",
31
- "aircraft_count": "62",
32
- "airport_count": "643",
33
- "births": "7170553",
34
- "popular_name": "Emma"
35
- },
36
- {
37
- "state": "NC",
38
- "aircraft_count": "72",
39
- "airport_count": "400",
40
- "births": "8440235",
41
- "popular_name": "Emma"
42
- },
43
- {
44
- "state": "AR",
45
- "aircraft_count": "31",
46
- "airport_count": "299",
47
- "births": "3330382",
48
- "popular_name": "Emma"
49
- },
50
- {
51
- "state": "AL",
52
- "aircraft_count": "58",
53
- "airport_count": "260",
54
- "births": "5645599",
55
- "popular_name": "Emma"
56
- },
57
- {
58
- "state": "MT",
59
- "aircraft_count": "50",
60
- "airport_count": "259",
61
- "births": "942521",
62
- "popular_name": "Emma"
63
- },
64
- {
65
- "state": "ME",
66
- "aircraft_count": "12",
67
- "airport_count": "164",
68
- "births": "1383204",
69
- "popular_name": "Emma"
70
- },
71
- {
72
- "state": "VA",
73
- "aircraft_count": "67",
74
- "airport_count": "421",
75
- "births": "6693880",
76
- "popular_name": "Olivia"
77
- },
78
- {
79
- "state": "NE",
80
- "aircraft_count": "24",
81
- "airport_count": "308",
82
- "births": "2255039",
83
- "popular_name": "Olivia"
84
- },
85
- {
86
- "state": "ID",
87
- "aircraft_count": "36",
88
- "airport_count": "238",
89
- "births": "1147445",
90
- "popular_name": "Olivia"
91
- },
92
- {
93
- "state": "UT",
94
- "aircraft_count": "53",
95
- "airport_count": "140",
96
- "births": "2306033",
97
- "popular_name": "Olivia"
98
- },
99
- {
100
- "state": "AK",
101
- "aircraft_count": "85",
102
- "airport_count": "608",
103
- "births": "407106",
104
- "popular_name": "Sophia"
105
- },
106
- {
107
- "state": "WI",
108
- "aircraft_count": "66",
109
- "airport_count": "543",
110
- "births": "6088288",
111
- "popular_name": "Sophia"
112
- },
113
- {
114
- "state": "MI",
115
- "aircraft_count": "102",
116
- "airport_count": "489",
117
- "births": "11643455",
118
- "popular_name": "Sophia"
119
- },
120
- {
121
- "state": "WA",
122
- "aircraft_count": "114",
123
- "airport_count": "484",
124
- "births": "4631964",
125
- "popular_name": "Sophia"
126
- },
127
- {
128
- "state": "OR",
129
- "aircraft_count": "94",
130
- "airport_count": "441",
131
- "births": "2671780",
132
- "popular_name": "Sophia"
133
- },
134
- {
135
- "state": "ND",
136
- "aircraft_count": "26",
137
- "airport_count": "436",
138
- "births": "1059330",
139
- "popular_name": "Sophia"
140
- },
141
- {
142
- "state": "MD",
143
- "aircraft_count": "30",
144
- "airport_count": "229",
145
- "births": "4468717",
146
- "popular_name": "Sophia"
147
- },
148
- {
149
- "state": "SD",
150
- "aircraft_count": "23",
151
- "airport_count": "180",
152
- "births": "1049223",
153
- "popular_name": "Sophia"
154
- },
155
- {
156
- "state": "NH",
157
- "aircraft_count": "30",
158
- "airport_count": "112",
159
- "births": "955057",
160
- "popular_name": "Sophia"
161
- },
162
- {
163
- "state": "VT",
164
- "aircraft_count": "6",
165
- "airport_count": "81",
166
- "births": "545999",
167
- "popular_name": "Sophia"
168
- },
169
- {
170
- "state": "DE",
171
- "aircraft_count": "118",
172
- "airport_count": "42",
173
- "births": "615490",
174
- "popular_name": "Sophia"
175
- },
176
- {
177
- "state": "MS",
178
- "aircraft_count": "27",
179
- "airport_count": "243",
180
- "births": "3969549",
181
- "popular_name": "Madison"
182
- },
183
- {
184
- "state": "SC",
185
- "aircraft_count": "28",
186
- "airport_count": "189",
187
- "births": "4392042",
188
- "popular_name": "Madison"
189
- },
190
- {
191
- "state": "DC",
192
- "aircraft_count": "6",
193
- "airport_count": "20",
194
- "births": "1419047",
195
- "popular_name": "Madison"
196
- },
197
- {
198
- "state": "TX",
199
- "aircraft_count": "287",
200
- "airport_count": "1845",
201
- "births": "21467359",
202
- "popular_name": "Isabella"
203
- },
204
- {
205
- "state": "CA",
206
- "aircraft_count": "367",
207
- "airport_count": "984",
208
- "births": "28810563",
209
- "popular_name": "Isabella"
210
- },
211
- {
212
- "state": "IL",
213
- "aircraft_count": "111",
214
- "airport_count": "890",
215
- "births": "15178876",
216
- "popular_name": "Isabella"
217
- },
218
- {
219
- "state": "FL",
220
- "aircraft_count": "228",
221
- "airport_count": "856",
222
- "births": "9277223",
223
- "popular_name": "Isabella"
224
- },
225
- {
226
- "state": "PA",
227
- "aircraft_count": "83",
228
- "airport_count": "804",
229
- "births": "16661910",
230
- "popular_name": "Isabella"
231
- },
232
- {
233
- "state": "OH",
234
- "aircraft_count": "106",
235
- "airport_count": "749",
236
- "births": "14201526",
237
- "popular_name": "Isabella"
238
- },
239
- {
240
- "state": "NY",
241
- "aircraft_count": "91",
242
- "airport_count": "576",
243
- "births": "23694136",
244
- "popular_name": "Isabella"
245
- },
246
- {
247
- "state": "MO",
248
- "aircraft_count": "62",
249
- "airport_count": "537",
250
- "births": "6792279",
251
- "popular_name": "Isabella"
252
- },
253
- {
254
- "state": "OK",
255
- "aircraft_count": "99",
256
- "airport_count": "443",
257
- "births": "4137117",
258
- "popular_name": "Isabella"
259
- },
260
- {
261
- "state": "GA",
262
- "aircraft_count": "98",
263
- "airport_count": "440",
264
- "births": "8183223",
265
- "popular_name": "Isabella"
266
- },
267
- {
268
- "state": "CO",
269
- "aircraft_count": "98",
270
- "airport_count": "425",
271
- "births": "3424936",
272
- "popular_name": "Isabella"
273
- },
274
- {
275
- "state": "KS",
276
- "aircraft_count": "63",
277
- "airport_count": "415",
278
- "births": "3212573",
279
- "popular_name": "Isabella"
280
- },
281
- {
282
- "state": "NJ",
283
- "aircraft_count": "47",
284
- "airport_count": "378",
285
- "births": "8318769",
286
- "popular_name": "Isabella"
287
- },
288
- {
289
- "state": "AZ",
290
- "aircraft_count": "98",
291
- "airport_count": "319",
292
- "births": "3328182",
293
- "popular_name": "Isabella"
294
- },
295
- {
296
- "state": "TN",
297
- "aircraft_count": "62",
298
- "airport_count": "285",
299
- "births": "6313851",
300
- "popular_name": "Isabella"
301
- },
302
- {
303
- "state": "MA",
304
- "aircraft_count": "40",
305
- "airport_count": "225",
306
- "births": "7576933",
307
- "popular_name": "Isabella"
308
- },
309
- {
310
- "state": "KY",
311
- "aircraft_count": "26",
312
- "airport_count": "202",
313
- "births": "5352594",
314
- "popular_name": "Isabella"
315
- },
316
- {
317
- "state": "NM",
318
- "aircraft_count": "46",
319
- "airport_count": "181",
320
- "births": "1640394",
321
- "popular_name": "Isabella"
322
- },
323
- {
324
- "state": "CT",
325
- "aircraft_count": "25",
326
- "airport_count": "153",
327
- "births": "3361014",
328
- "popular_name": "Isabella"
329
- },
330
- {
331
- "state": "NV",
332
- "aircraft_count": "55",
333
- "airport_count": "128",
334
- "births": "842854",
335
- "popular_name": "Isabella"
336
- },
337
- {
338
- "state": "WV",
339
- "aircraft_count": "13",
340
- "airport_count": "116",
341
- "births": "2962442",
342
- "popular_name": "Isabella"
343
- },
344
- {
345
- "state": "WY",
346
- "aircraft_count": "15",
347
- "airport_count": "115",
348
- "births": "423000",
349
- "popular_name": "Isabella"
350
- },
351
- {
352
- "state": "HI",
353
- "aircraft_count": "10",
354
- "airport_count": "52",
355
- "births": "950538",
356
- "popular_name": "Isabella"
357
- },
358
- {
359
- "state": "RI",
360
- "aircraft_count": "5",
361
- "airport_count": "28",
362
- "births": "1148302",
363
- "popular_name": "Isabella"
364
- }
365
- ]') as p
366
- );