@malloydata/db-snowflake 0.0.126-dev240305182920

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,205 @@
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
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ var desc = Object.getOwnPropertyDescriptor(m, k);
27
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
28
+ desc = { enumerable: true, get: function() { return m[k]; } };
29
+ }
30
+ Object.defineProperty(o, k2, desc);
31
+ }) : (function(o, m, k, k2) {
32
+ if (k2 === undefined) k2 = k;
33
+ o[k2] = m[k];
34
+ }));
35
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
36
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
37
+ }) : function(o, v) {
38
+ o["default"] = v;
39
+ });
40
+ var __importStar = (this && this.__importStar) || function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ var __importDefault = (this && this.__importDefault) || function (mod) {
48
+ return (mod && mod.__esModule) ? mod : { "default": mod };
49
+ };
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.SnowflakeExecutor = void 0;
52
+ const snowflake_sdk_1 = __importDefault(require("snowflake-sdk"));
53
+ const toml = __importStar(require("toml"));
54
+ const fs = __importStar(require("fs"));
55
+ const path = __importStar(require("path"));
56
+ const malloy_1 = require("@malloydata/malloy");
57
+ // function columnNameToLowerCase(row: QueryDataRow): QueryDataRow {
58
+ // const ret: QueryDataRow = {};
59
+ // for (const key in row) {
60
+ // ret[key.toLowerCase()] = row[key];
61
+ // }
62
+ // return ret;
63
+ // }
64
+ class SnowflakeExecutor {
65
+ constructor(connOptions, poolOptions) {
66
+ this.pool_ = snowflake_sdk_1.default.createPool(connOptions, {
67
+ ...SnowflakeExecutor.defaultPoolOptions_,
68
+ ...(poolOptions !== null && poolOptions !== void 0 ? poolOptions : {}),
69
+ });
70
+ }
71
+ static getConnectionOptionsFromEnv() {
72
+ const account = process.env['SNOWFLAKE_ACCOUNT'];
73
+ if (account) {
74
+ const username = process.env['SNOWFLAKE_USER'];
75
+ const password = process.env['SNOWFLAKE_PASSWORD'];
76
+ const warehouse = process.env['SNOWFLAKE_WAREHOUSE'];
77
+ const database = process.env['SNOWFLAKE_DATABASE'];
78
+ const schema = process.env['SNOWFLAKE_SCHEMA'];
79
+ return {
80
+ account,
81
+ username,
82
+ password,
83
+ warehouse,
84
+ database,
85
+ schema,
86
+ };
87
+ }
88
+ return undefined;
89
+ }
90
+ static getConnectionOptionsFromToml(options) {
91
+ var _a, _b;
92
+ let location = options === null || options === void 0 ? void 0 : options.config_file_path;
93
+ if (location === undefined) {
94
+ const homeDir = process.env['HOME'] || process.env['USERPROFILE'];
95
+ if (homeDir === undefined) {
96
+ throw new Error('could not find a path to connections.toml');
97
+ }
98
+ location = path.join(homeDir, '.snowflake', 'connections.toml');
99
+ }
100
+ if (!fs.existsSync(location)) {
101
+ throw new Error(`provided snowflake connection config file: ${location} does not exist`);
102
+ }
103
+ const tomlData = fs.readFileSync(location, 'utf-8');
104
+ const connections = toml.parse(tomlData);
105
+ const tomlConnectionName = (_a = options === null || options === void 0 ? void 0 : options.connection_name) !== null && _a !== void 0 ? _a : 'default';
106
+ const connection = connections[tomlConnectionName];
107
+ if (connection === undefined) {
108
+ throw new Error(`provided snowflake connection name: ${tomlConnectionName} does not exist at ${location}`);
109
+ }
110
+ // sometimes the connection file uses "user" instead of "username"
111
+ // because the python api expects 'user'
112
+ connection['username'] = (_b = connection['username']) !== null && _b !== void 0 ? _b : connection['user'];
113
+ if (!connection || !connection.account || !connection.username) {
114
+ throw new Error(`provided snowflake connection config file at ${location} is not valid`);
115
+ }
116
+ return {
117
+ // some basic options we configure by default but can be overriden
118
+ ...SnowflakeExecutor.defaultConnectionOptions,
119
+ account: connection.account,
120
+ username: connection.username,
121
+ ...connection,
122
+ };
123
+ }
124
+ async done() {
125
+ await this.pool_.drain().then(() => {
126
+ this.pool_.clear();
127
+ });
128
+ }
129
+ async _execute(sqlText, conn) {
130
+ return new Promise((resolve, reject) => {
131
+ const _statment = conn.execute({
132
+ sqlText,
133
+ complete: (err, _stmt, rows) => {
134
+ if (err) {
135
+ reject(err);
136
+ }
137
+ else if (rows) {
138
+ resolve(rows);
139
+ }
140
+ },
141
+ });
142
+ });
143
+ }
144
+ async _setSessionParams(conn) {
145
+ // set some default session parameters
146
+ // this is quite imporant for snowflake because malloy tends to add quotes to all database identifiers
147
+ // and snowflake is case sensitive by with quotes but matches against all caps identifiers without quotes
148
+ // await this._execute(
149
+ // 'ALTER SESSION SET QUOTED_IDENTIFIERS_IGNORE_CASE = true;',
150
+ // conn
151
+ // );
152
+ // set utc as the default timezone which is the malloy convention
153
+ await this._execute("ALTER SESSION SET TIMEZONE = 'UTC';", conn);
154
+ // ensure week starts on Sunday which is the malloy convention
155
+ await this._execute('ALTER SESSION SET WEEK_START = 7;', conn);
156
+ }
157
+ async batch(sqlText) {
158
+ return await this.pool_.use(async (conn) => {
159
+ await this._setSessionParams(conn);
160
+ return await this._execute(sqlText, conn);
161
+ });
162
+ }
163
+ async stream(sqlText, options) {
164
+ const pool = this.pool_;
165
+ return await pool.acquire().then(async (conn) => {
166
+ await this._setSessionParams(conn);
167
+ const stmt = conn.execute({
168
+ sqlText,
169
+ streamResult: true,
170
+ });
171
+ const stream = stmt.streamRows();
172
+ function streamSnowflake(onError, onData, onEnd) {
173
+ function handleEnd() {
174
+ onEnd();
175
+ pool.release(conn);
176
+ }
177
+ let index = 0;
178
+ function handleData(row) {
179
+ onData(row);
180
+ index += 1;
181
+ if ((options === null || options === void 0 ? void 0 : options.rowLimit) !== undefined && index >= options.rowLimit) {
182
+ onEnd();
183
+ }
184
+ }
185
+ stream.on('error', onError);
186
+ stream.on('data', handleData);
187
+ stream.on('end', handleEnd);
188
+ }
189
+ return Promise.resolve((0, malloy_1.toAsyncGenerator)(streamSnowflake));
190
+ });
191
+ }
192
+ }
193
+ exports.SnowflakeExecutor = SnowflakeExecutor;
194
+ SnowflakeExecutor.defaultPoolOptions_ = {
195
+ min: 1,
196
+ max: 1,
197
+ // ensure we validate a connection before giving it to a client
198
+ testOnBorrow: true,
199
+ testOnReturn: true,
200
+ };
201
+ SnowflakeExecutor.defaultConnectionOptions = {
202
+ clientSessionKeepAlive: true, // default = false
203
+ clientSessionKeepAliveHeartbeatFrequency: 900, // default = 3600
204
+ };
205
+ //# sourceMappingURL=snowflake_executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snowflake_executor.js","sourceRoot":"","sources":["../src/snowflake_executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
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
+ const snowflake_executor_1 = require("./snowflake_executor");
26
+ const envDatabases = process.env['MALLOY_DATABASES'] || process.env['MALLOY_DATABASE'];
27
+ let describe = globalThis.describe;
28
+ if (envDatabases && !envDatabases.includes('snowflake')) {
29
+ describe = describe.skip;
30
+ }
31
+ class SnowflakeExecutorTestSetup {
32
+ constructor(executor) {
33
+ this.executor = executor;
34
+ this.executor_ = executor;
35
+ }
36
+ async runBatch(sqlText) {
37
+ let ret = [];
38
+ await (async () => {
39
+ const rows = await this.executor_.batch(sqlText);
40
+ return rows;
41
+ })().then((rows) => {
42
+ ret = rows;
43
+ });
44
+ return ret;
45
+ }
46
+ async runStreaming(sqlText, queryOptions) {
47
+ const rows = [];
48
+ await (async () => {
49
+ for await (const row of await this.executor_.stream(sqlText, queryOptions)) {
50
+ rows.push(row);
51
+ }
52
+ })();
53
+ return rows;
54
+ }
55
+ async done() {
56
+ await this.executor_.done();
57
+ }
58
+ }
59
+ describe('db:SnowflakeExecutor', () => {
60
+ let db;
61
+ let query;
62
+ beforeAll(() => {
63
+ const connOptions = snowflake_executor_1.SnowflakeExecutor.getConnectionOptionsFromEnv() ||
64
+ snowflake_executor_1.SnowflakeExecutor.getConnectionOptionsFromToml();
65
+ const executor = new snowflake_executor_1.SnowflakeExecutor(connOptions);
66
+ db = new SnowflakeExecutorTestSetup(executor);
67
+ query = `
68
+ select
69
+ *
70
+ from
71
+ (
72
+ values
73
+ (1, 'one'),
74
+ (2, 'two'),
75
+ (3, 'three'),
76
+ (4, 'four'),
77
+ (5, 'five')
78
+ );
79
+ `;
80
+ });
81
+ afterAll(async () => {
82
+ await db.done();
83
+ });
84
+ it('verifies batch execute', async () => {
85
+ const rows = await db.runBatch(query);
86
+ expect(rows.length).toBe(5);
87
+ });
88
+ it('verifies stream iterable', async () => {
89
+ const rows = await db.runStreaming(query, { rowLimit: 2 });
90
+ expect(rows.length).toBe(2);
91
+ });
92
+ });
93
+ //# sourceMappingURL=snowflake_executor.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snowflake_executor.spec.js","sourceRoot":"","sources":["../src/snowflake_executor.spec.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;AAEH,6DAAuD;AAGvD,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAEpE,IAAI,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC;AACnC,IAAI,YAAY,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;IACxD,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;AAC3B,CAAC;AAED,MAAM,0BAA0B;IAE9B,YAAoB,QAA2B;QAA3B,aAAQ,GAAR,QAAQ,CAAmB;QAC7C,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAe;QAC5B,IAAI,GAAG,GAAc,EAAE,CAAC;QACxB,MAAM,CAAC,KAAK,IAAI,EAAE;YAChB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAe,EAAE,EAAE;YAC5B,GAAG,GAAG,IAAI,CAAC;QACb,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAe,EAAE,YAA4B;QAC9D,MAAM,IAAI,GAAc,EAAE,CAAC;QAC3B,MAAM,CAAC,KAAK,IAAI,EAAE;YAChB,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CACjD,OAAO,EACP,YAAY,CACb,EAAE,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;CACF;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,IAAI,EAA8B,CAAC;IACnC,IAAI,KAAa,CAAC;IAElB,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GACf,sCAAiB,CAAC,2BAA2B,EAAE;YAC/C,sCAAiB,CAAC,4BAA4B,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,sCAAiB,CAAC,WAAW,CAAC,CAAC;QACpD,EAAE,GAAG,IAAI,0BAA0B,CAAC,QAAQ,CAAC,CAAC;QAC9C,KAAK,GAAG;;;;;;;;;;;;KAYP,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,EAAC,QAAQ,EAAE,CAAC,EAAC,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@malloydata/db-snowflake",
3
+ "version": "0.0.126-dev240305182920",
4
+ "license": "MIT",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/malloydata/malloy"
10
+ },
11
+ "engines": {
12
+ "node": ">=18"
13
+ },
14
+ "scripts": {
15
+ "lint": "eslint '**/*.ts{,x}'",
16
+ "lint-fix": "eslint '**/*.ts{,x}' --fix",
17
+ "test": "jest --config=../../jest.config.js",
18
+ "build": "tsc --build",
19
+ "clean": "tsc --build --clean",
20
+ "malloyc": "ts-node ../../scripts/malloy-to-json",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "dependencies": {
24
+ "@malloydata/malloy": "^0.0.126-dev240305182920",
25
+ "@types/snowflake-sdk": "^1.6.16",
26
+ "generic-pool": "^3.9.0",
27
+ "snowflake-sdk": "^1.9.0",
28
+ "toml": "^3.0.0"
29
+ }
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2023 Google LLC
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining
5
+ * a copy of this software and associated documentation files
6
+ * (the "Software"), to deal in the Software without restriction,
7
+ * including without limitation the rights to use, copy, modify, merge,
8
+ * publish, distribute, sublicense, and/or sell copies of the Software,
9
+ * and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be
13
+ * included in all copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+
24
+ export {SnowflakeConnection} from './snowflake_connection';
@@ -0,0 +1,113 @@
1
+ /*
2
+ * Copyright 2023 Google LLC
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining
5
+ * a copy of this software and associated documentation files
6
+ * (the "Software"), to deal in the Software without restriction,
7
+ * including without limitation the rights to use, copy, modify, merge,
8
+ * publish, distribute, sublicense, and/or sell copies of the Software,
9
+ * and to permit persons to whom the Software is furnished to do so,
10
+ * subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be
13
+ * included in all copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+
24
+ import * as malloy from '@malloydata/malloy';
25
+ import {SnowflakeConnection} from './snowflake_connection';
26
+ import {fileURLToPath} from 'url';
27
+ import * as util from 'util';
28
+ import * as fs from 'fs';
29
+ import {SnowflakeExecutor} from './snowflake_executor';
30
+
31
+ const envDatabases =
32
+ process.env['MALLOY_DATABASES'] || process.env['MALLOY_DATABASE'];
33
+
34
+ let describe = globalThis.describe;
35
+ if (envDatabases && !envDatabases.includes('snowflake')) {
36
+ describe = describe.skip;
37
+ }
38
+
39
+ describe('db:Snowflake', () => {
40
+ let conn: SnowflakeConnection;
41
+ let runtime: malloy.Runtime;
42
+
43
+ beforeAll(() => {
44
+ const connOptions =
45
+ SnowflakeExecutor.getConnectionOptionsFromEnv() ||
46
+ SnowflakeExecutor.getConnectionOptionsFromToml();
47
+ conn = new SnowflakeConnection('snowflake', {
48
+ connOptions: connOptions,
49
+ queryOptions: {rowLimit: 1000},
50
+ });
51
+ const files = {
52
+ readURL: async (url: URL) => {
53
+ const filePath = fileURLToPath(url);
54
+ return await util.promisify(fs.readFile)(filePath, 'utf8');
55
+ },
56
+ };
57
+ runtime = new malloy.Runtime(files, conn);
58
+ });
59
+
60
+ afterAll(async () => {
61
+ await conn.close();
62
+ });
63
+
64
+ it('runs a SQL query', async () => {
65
+ const res = await conn.runSQL('SELECT 1 as t');
66
+ expect(res.rows[0]['t']).toBe(1);
67
+ });
68
+
69
+ it('runs a Malloy query', async () => {
70
+ const sql = await runtime
71
+ .loadModel("source: aircraft is snowflake.table('malloytest.aircraft')")
72
+ .loadQuery(
73
+ `run: aircraft -> {
74
+ where: state != null
75
+ aggregate: cnt is count()
76
+ group_by: state}`
77
+ )
78
+ .getSQL();
79
+ const res = await conn.runSQL(sql);
80
+ expect(res.totalRows).toBe(55);
81
+ let total = 0;
82
+ for (const row of res.rows) {
83
+ total += +(row['cnt'] ?? 0);
84
+ }
85
+ expect(total).toBe(3540);
86
+
87
+ // if we request for a smaller rowLimit we should get fewer rows
88
+ const res_limited = await conn.runSQL(sql, {rowLimit: 10});
89
+ expect(res_limited.totalRows).toBe(10);
90
+ });
91
+
92
+ it('runs a Malloy query on an sql source', async () => {
93
+ const sql = await runtime
94
+ .loadModel(
95
+ "source: aircraft is snowflake.sql('SELECT * FROM malloytest.AIRCRAFT')"
96
+ )
97
+ .loadQuery('run: aircraft -> { aggregate: cnt is count() }')
98
+ .getSQL();
99
+ const res = await conn.runSQL(sql);
100
+ expect(res.rows.length).toBe(1);
101
+ expect(res.rows[0]['cnt']).toBe(3599);
102
+ });
103
+
104
+ it('runs a Malloy function with overrides', async () => {
105
+ const sql = await runtime
106
+ .loadModel("source: aircraft is snowflake.table('malloytest.aircraft')")
107
+ .loadQuery('run: aircraft -> { select: rand is rand(), limit: 1}')
108
+ .getSQL();
109
+ const res = await conn.runSQL(sql);
110
+ expect(res.rows[0]['rand']).toBeGreaterThanOrEqual(0);
111
+ expect(res.rows[0]['rand']).toBeLessThanOrEqual(1);
112
+ });
113
+ });