@malloydata/db-bigquery 0.0.24-dev230224214106 → 0.0.24-dev230301165401

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.
@@ -51,119 +51,119 @@ const bigquery_1 = require("@google-cloud/bigquery");
51
51
  const util = __importStar(require("util"));
52
52
  const fs = __importStar(require("fs"));
53
53
  const url_1 = require("url");
54
- describe("db:BigQuery", () => {
54
+ describe('db:BigQuery', () => {
55
55
  let bq;
56
56
  let runtime;
57
57
  beforeAll(() => {
58
- bq = new bigquery_connection_1.BigQueryConnection("test");
58
+ bq = new bigquery_connection_1.BigQueryConnection('test');
59
59
  const files = {
60
- "readURL": async (url) => {
60
+ readURL: async (url) => {
61
61
  const filePath = (0, url_1.fileURLToPath)(url);
62
- return await util.promisify(fs.readFile)(filePath, "utf8");
63
- }
62
+ return await util.promisify(fs.readFile)(filePath, 'utf8');
63
+ },
64
64
  };
65
65
  runtime = new malloy.Runtime(files, bq);
66
66
  });
67
- it("runs a SQL query", async () => {
68
- const res = await bq.runSQL(`SELECT 1 as t`);
69
- expect(res.rows[0]["t"]).toBe(1);
67
+ it('runs a SQL query', async () => {
68
+ const res = await bq.runSQL('SELECT 1 as t');
69
+ expect(res.rows[0]['t']).toBe(1);
70
70
  });
71
- it("costs a SQL query", async () => {
72
- const res = await bq.costQuery(`SELECT * FROM malloy-data.faa.airports`);
71
+ it('costs a SQL query', async () => {
72
+ const res = await bq.costQuery('SELECT * FROM malloy-data.faa.airports');
73
73
  expect(res).toBe(3029200);
74
74
  });
75
- it("gets table schema", async () => {
76
- const res = await bq.getTableFieldSchema(`malloy-data.faa.carriers`);
75
+ it('gets table schema', async () => {
76
+ const res = await bq.getTableFieldSchema('malloy-data.faa.carriers');
77
77
  expect(res.schema).toStrictEqual({
78
- "fields": [
79
- { "name": "code", "type": "STRING" },
80
- { "name": "name", "type": "STRING" },
81
- { "name": "nickname", "type": "STRING" }
82
- ]
78
+ fields: [
79
+ { name: 'code', type: 'STRING' },
80
+ { name: 'name', type: 'STRING' },
81
+ { name: 'nickname', type: 'STRING' },
82
+ ],
83
83
  });
84
84
  });
85
- it.todo("gets table structdefs");
86
- it("runs a Malloy query", async () => {
85
+ it.todo('gets table structdefs');
86
+ it('runs a Malloy query', async () => {
87
87
  const sql = await runtime
88
88
  .loadModel("explore: carriers is table('malloy-data.faa.carriers') { measure: carrier_count is count() }")
89
- .loadQuery("query: carriers -> { aggregate: carrier_count }")
89
+ .loadQuery('query: carriers -> { aggregate: carrier_count }')
90
90
  .getSQL();
91
91
  const res = await bq.runSQL(sql);
92
- expect(res.rows[0]["carrier_count"]).toBe(21);
92
+ expect(res.rows[0]['carrier_count']).toBe(21);
93
93
  });
94
- it("streams a Malloy query for download", async () => {
94
+ it('streams a Malloy query for download', async () => {
95
95
  const sql = await runtime
96
96
  .loadModel("explore: carriers is table('malloy-data.faa.carriers') { measure: carrier_count is count() }")
97
- .loadQuery("query: carriers -> { group_by: name }")
97
+ .loadQuery('query: carriers -> { group_by: name }')
98
98
  .getSQL();
99
99
  const res = await bq.downloadMalloyQuery(sql);
100
- return new Promise((resolve) => {
100
+ return new Promise(resolve => {
101
101
  let count = 0;
102
- res.on("data", () => (count += 1));
103
- res.on("end", () => {
102
+ res.on('data', () => (count += 1));
103
+ res.on('end', () => {
104
104
  expect(count).toBe(21);
105
105
  resolve(true);
106
106
  });
107
107
  });
108
108
  });
109
- it("manifests a temporary table", async () => {
110
- const fullTempTableName = await bq.manifestTemporaryTable("SELECT 1 as t");
111
- const splitTableName = fullTempTableName.split(".");
109
+ it('manifests a temporary table', async () => {
110
+ const fullTempTableName = await bq.manifestTemporaryTable('SELECT 1 as t');
111
+ const splitTableName = fullTempTableName.split('.');
112
112
  const sdk = new bigquery_1.BigQuery();
113
113
  const dataset = sdk.dataset(splitTableName[1]);
114
114
  const table = dataset.table(splitTableName[2]);
115
115
  const exists = await table.exists();
116
116
  expect(exists).toBeTruthy();
117
117
  });
118
- describe.skip("manifests permanent table", () => {
119
- const datasetName = "test_malloy_test_dataset";
120
- const tableName = "test_malloy_test_table";
118
+ describe.skip('manifests permanent table', () => {
119
+ const datasetName = 'test_malloy_test_dataset';
120
+ const tableName = 'test_malloy_test_table';
121
121
  const sdk = new bigquery_1.BigQuery();
122
122
  // delete entire dataset before each test and once tests are complete
123
123
  const deleteTestDataset = async () => {
124
124
  const dataset = sdk.dataset(datasetName);
125
125
  if ((await dataset.exists())[0]) {
126
126
  await dataset.delete({
127
- "force": true
127
+ force: true,
128
128
  });
129
129
  }
130
130
  };
131
131
  beforeEach(deleteTestDataset);
132
132
  afterAll(deleteTestDataset);
133
- it("throws if dataset does not exist and createDataset=false", async () => {
133
+ it('throws if dataset does not exist and createDataset=false', async () => {
134
134
  await expect(async () => {
135
- await bq.manifestPermanentTable("SELECT 1 as t", datasetName, tableName, false, false);
135
+ await bq.manifestPermanentTable('SELECT 1 as t', datasetName, tableName, false, false);
136
136
  }).rejects.toThrowError(`Dataset ${datasetName} does not exist`);
137
137
  });
138
- it("creates dataset if createDataset=true", async () => {
138
+ it('creates dataset if createDataset=true', async () => {
139
139
  // note - dataset does not exist b/c of beforeEach()
140
- await bq.manifestPermanentTable("SELECT 1 as t", datasetName, tableName, false, true);
140
+ await bq.manifestPermanentTable('SELECT 1 as t', datasetName, tableName, false, true);
141
141
  const dataset = sdk.dataset(datasetName);
142
142
  const [exists] = await dataset.exists();
143
143
  expect(exists).toBeTruthy();
144
144
  });
145
- it("throws if table exist and overwriteExistingTable=false", async () => {
145
+ it('throws if table exist and overwriteExistingTable=false', async () => {
146
146
  const newDatasetResponse = await sdk.createDataset(datasetName);
147
147
  const dataset = newDatasetResponse[0];
148
- const tableMeta = { "name": tableName };
148
+ const tableMeta = { name: tableName };
149
149
  await dataset.createTable(tableName, tableMeta);
150
150
  await expect(async () => {
151
- await bq.manifestPermanentTable("SELECT 1 as t", datasetName, tableName, false, true);
151
+ await bq.manifestPermanentTable('SELECT 1 as t', datasetName, tableName, false, true);
152
152
  }).rejects.toThrowError(`Table ${tableName} already exists`);
153
153
  });
154
- it("manifests a table", async () => {
155
- const jobId = await bq.manifestPermanentTable("SELECT 1 as t", datasetName, tableName, false, true);
154
+ it('manifests a table', async () => {
155
+ const jobId = await bq.manifestPermanentTable('SELECT 1 as t', datasetName, tableName, false, true);
156
156
  // wait for job to complete
157
157
  const [job] = await sdk.job(jobId).get();
158
158
  let [metaData] = await job.getMetadata();
159
- while (metaData.status.state !== "DONE") {
160
- await new Promise((resolve) => setTimeout(resolve, 1000));
159
+ while (metaData.status.state !== 'DONE') {
160
+ await new Promise(resolve => setTimeout(resolve, 1000));
161
161
  [metaData] = await job.getMetadata();
162
162
  }
163
163
  // query the new table
164
164
  const [queryJob] = await sdk.createQueryJob(`SELECT * FROM ${datasetName}.${tableName}`);
165
165
  const [results] = await queryJob.getQueryResults();
166
- expect(results[0]).toStrictEqual({ "t": 1 });
166
+ expect(results[0]).toStrictEqual({ t: 1 });
167
167
  });
168
168
  });
169
169
  });
@@ -1,7 +1,7 @@
1
- import { RowMetadata } from "@google-cloud/bigquery";
2
- import bigquery from "@google-cloud/bigquery/build/src/types";
3
- import { ResourceStream } from "@google-cloud/paginator";
4
- import { Connection, FieldTypeDef, MalloyQueryData, PersistSQLResults, PooledConnection, QueryData, QueryDataRow, SQLBlock, StreamingConnection, StructDef } from "@malloydata/malloy";
1
+ import { RowMetadata } from '@google-cloud/bigquery';
2
+ import bigquery from '@google-cloud/bigquery/build/src/types';
3
+ import { ResourceStream } from '@google-cloud/paginator';
4
+ import { Connection, FieldTypeDef, MalloyQueryData, PersistSQLResults, PooledConnection, QueryData, QueryDataRow, SQLBlock, StreamingConnection, StructDef } from '@malloydata/malloy';
5
5
  export interface BigQueryManagerOptions {
6
6
  credentials?: {
7
7
  clientId: string;
@@ -23,7 +23,8 @@ interface BigQueryConnectionConfiguration {
23
23
  }
24
24
  interface SchemaInfo {
25
25
  schema: bigquery.ITableFieldSchema;
26
- needsPartitionPsuedoColumn: boolean;
26
+ needsTableSuffixPseudoColumn: boolean;
27
+ needsPartitionPseudoColumn: boolean;
27
28
  }
28
29
  declare type QueryOptionsReader = Partial<BigQueryQueryOptions> | (() => Partial<BigQueryQueryOptions>);
29
30
  export declare class BigQueryAuthenticationError extends Error {
@@ -57,7 +57,7 @@ const malloy_1 = require("@malloydata/malloy");
57
57
  class BigQueryAuthenticationError extends Error {
58
58
  constructor(message) {
59
59
  super(message);
60
- this.name = "BigQueryAuthenticationError";
60
+ this.name = 'BigQueryAuthenticationError';
61
61
  }
62
62
  }
63
63
  exports.BigQueryAuthenticationError = BigQueryAuthenticationError;
@@ -66,7 +66,7 @@ const maybeRewriteError = (e) => {
66
66
  // the refresh token is invalid
67
67
  // ApiErrors happen if token is revoked (for example, client.revokeToken(creds.access_token!))
68
68
  if (e instanceof Error) {
69
- if ((e instanceof gaxios_1.GaxiosError && e.code === "400") ||
69
+ if ((e instanceof gaxios_1.GaxiosError && e.code === '400') ||
70
70
  (e instanceof googleCommon.ApiError && e.code === 401)) {
71
71
  return new BigQueryAuthenticationError(e.message);
72
72
  }
@@ -93,18 +93,18 @@ class BigQueryConnection {
93
93
  this.schemaCache = new Map();
94
94
  this.sqlSchemaCache = new Map();
95
95
  this.bqToMalloyTypes = {
96
- "DATE": { "type": "date" },
97
- "STRING": { "type": "string" },
98
- "INTEGER": { "type": "number", "numberType": "integer" },
99
- "INT64": { "type": "number", "numberType": "integer" },
100
- "FLOAT": { "type": "number", "numberType": "float" },
101
- "FLOAT64": { "type": "number", "numberType": "float" },
102
- "NUMERIC": { "type": "number", "numberType": "float" },
103
- "BIGNUMERIC": { "type": "number", "numberType": "float" },
104
- "TIMESTAMP": { "type": "timestamp" },
105
- "BOOLEAN": { "type": "boolean" },
106
- "BOOL": { "type": "boolean" },
107
- "JSON": { "type": "json" }
96
+ DATE: { type: 'date' },
97
+ STRING: { type: 'string' },
98
+ INTEGER: { type: 'number', numberType: 'integer' },
99
+ INT64: { type: 'number', numberType: 'integer' },
100
+ FLOAT: { type: 'number', numberType: 'float' },
101
+ FLOAT64: { type: 'number', numberType: 'float' },
102
+ NUMERIC: { type: 'number', numberType: 'float' },
103
+ BIGNUMERIC: { type: 'number', numberType: 'float' },
104
+ TIMESTAMP: { type: 'timestamp' },
105
+ BOOLEAN: { type: 'boolean' },
106
+ BOOL: { type: 'boolean' },
107
+ JSON: { type: 'json' },
108
108
  // TODO (https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#tablefieldschema):
109
109
  // BYTES
110
110
  // DATETIME
@@ -113,8 +113,8 @@ class BigQueryConnection {
113
113
  };
114
114
  this.name = name;
115
115
  this.bigQuery = new bigquery_1.BigQuery({
116
- "userAgent": `Malloy/${malloy_1.Malloy.version}`,
117
- "keyFilename": config.serviceAccountKeyPath
116
+ userAgent: `Malloy/${malloy_1.Malloy.version}`,
117
+ keyFilename: config.serviceAccountKeyPath,
118
118
  });
119
119
  // record project ID because for unclear reasons we have to modify the project ID on the SDK when
120
120
  // we want to use the tables API
@@ -122,10 +122,10 @@ class BigQueryConnection {
122
122
  this.defaultProject = config.defaultProject || this.bigQuery.projectId;
123
123
  this.queryOptions = queryOptions;
124
124
  this.config = config;
125
- this.location = config.location || "US";
125
+ this.location = config.location || 'US';
126
126
  }
127
127
  get dialectName() {
128
- return "standardsql";
128
+ return 'standardsql';
129
129
  }
130
130
  readQueryOptions() {
131
131
  const options = BigQueryConnection.DEFAULT_QUERY_OPTIONS;
@@ -156,19 +156,19 @@ class BigQueryConnection {
156
156
  const pageSize = (_a = options.rowLimit) !== null && _a !== void 0 ? _a : defaultOptions.rowLimit;
157
157
  try {
158
158
  const queryResultsOptions = {
159
- "maxResults": pageSize,
160
- "startIndex": rowIndex.toString()
159
+ maxResults: pageSize,
160
+ startIndex: rowIndex.toString(),
161
161
  };
162
162
  const jobResult = await this.createBigQueryJobAndGetResults(sqlCommand, undefined, queryResultsOptions);
163
163
  const totalRows = +(((_b = jobResult[2]) === null || _b === void 0 ? void 0 : _b.totalRows)
164
164
  ? jobResult[2].totalRows
165
- : "0");
165
+ : '0');
166
166
  // TODO need to probably surface the cause of the schema not present error
167
167
  if (((_c = jobResult[2]) === null || _c === void 0 ? void 0 : _c.schema) === undefined) {
168
- throw new Error("Schema not present");
168
+ throw new Error('Schema not present');
169
169
  }
170
170
  // TODO even though we have 10 minute timeout limit, we still should confirm that resulting metadata has "jobComplete: true"
171
- const data = { "rows": jobResult[0], totalRows };
171
+ const data = { rows: jobResult[0], totalRows };
172
172
  const schema = (_d = jobResult[2]) === null || _d === void 0 ? void 0 : _d.schema;
173
173
  return { data, schema };
174
174
  }
@@ -181,22 +181,22 @@ class BigQueryConnection {
181
181
  return data;
182
182
  }
183
183
  async runSQLBlockAndFetchResultSchema(sqlBlock, options) {
184
- const { data, "schema": schemaRaw } = await this._runSQL(sqlBlock.selectStr, options);
184
+ const { data, schema: schemaRaw } = await this._runSQL(sqlBlock.selectStr, options);
185
185
  const schema = this.structDefFromSQLSchema(sqlBlock, schemaRaw);
186
186
  return { data, schema };
187
187
  }
188
188
  async downloadMalloyQuery(sqlCommand) {
189
189
  const job = await this.createBigQueryJob({
190
- "query": sqlCommand
190
+ query: sqlCommand,
191
191
  });
192
192
  return job.getQueryResultsStream();
193
193
  }
194
194
  async dryRunSQLQuery(sqlCommand) {
195
195
  try {
196
196
  const [result] = await this.bigQuery.createQueryJob({
197
- "location": this.location,
198
- "query": sqlCommand,
199
- "dryRun": true
197
+ location: this.location,
198
+ query: sqlCommand,
199
+ dryRun: true,
200
200
  });
201
201
  return result;
202
202
  }
@@ -215,8 +215,8 @@ class BigQueryConnection {
215
215
  }
216
216
  async getTableFieldSchema(tableURL) {
217
217
  var _a, _b;
218
- const { "tablePath": tableName } = (0, malloy_1.parseTableURI)(tableURL);
219
- const segments = tableName.split(".");
218
+ const { tablePath: tableName } = (0, malloy_1.parseTableURI)(tableURL);
219
+ const segments = tableName.split('.');
220
220
  // paths can have two or three segments
221
221
  // if there are only two segments, assume the dataset is "local" to the current billing project
222
222
  let projectId, datasetNamePart, tableNamePart;
@@ -236,14 +236,16 @@ class BigQueryConnection {
236
236
  // this is better than creating a new BQ instance every time we need to get a table schema.
237
237
  if (projectId)
238
238
  this.bigQuery.projectId = projectId;
239
+ const needTableSuffixPseudoColumn = tableNamePart && tableNamePart[tableNamePart.length - 1] === '*';
239
240
  const table = this.bigQuery.dataset(datasetNamePart).table(tableNamePart);
240
241
  const metadataPromise = table.getMetadata();
241
242
  this.bigQuery.projectId = this.projectId;
242
243
  const [metadata] = await metadataPromise;
243
244
  return {
244
- "schema": metadata.schema,
245
- "needsPartitionPsuedoColumn": ((_a = metadata.timePartitioning) === null || _a === void 0 ? void 0 : _a.type) !== undefined &&
246
- ((_b = metadata.timePartitioning) === null || _b === void 0 ? void 0 : _b.field) === undefined
245
+ schema: metadata.schema,
246
+ needsTableSuffixPseudoColumn: needTableSuffixPseudoColumn,
247
+ needsPartitionPseudoColumn: ((_a = metadata.timePartitioning) === null || _a === void 0 ? void 0 : _a.type) !== undefined &&
248
+ ((_b = metadata.timePartitioning) === null || _b === void 0 ? void 0 : _b.field) === undefined,
247
249
  };
248
250
  }
249
251
  catch (e) {
@@ -255,7 +257,7 @@ class BigQueryConnection {
255
257
  return result[0];
256
258
  }
257
259
  async test() {
258
- await this.dryRunSQLQuery("SELECT 1");
260
+ await this.dryRunSQLQuery('SELECT 1');
259
261
  }
260
262
  /*
261
263
  * Do we need to care about multiple overlapping calls to this function? No.
@@ -276,7 +278,7 @@ class BigQueryConnection {
276
278
  *
277
279
  */
278
280
  async manifestTemporaryTable(sqlCommand) {
279
- const hash = crypto.createHash("md5").update(sqlCommand).digest("hex");
281
+ const hash = crypto.createHash('md5').update(sqlCommand).digest('hex');
280
282
  const tempTableName = this.temporaryTables.get(hash);
281
283
  if (tempTableName !== undefined) {
282
284
  return tempTableName;
@@ -284,16 +286,16 @@ class BigQueryConnection {
284
286
  else {
285
287
  try {
286
288
  const [job] = await this.bigQuery.createQueryJob({
287
- "location": this.location,
288
- "query": sqlCommand
289
+ location: this.location,
290
+ query: sqlCommand,
289
291
  });
290
292
  let [metaData] = await job.getMetadata();
291
293
  // wait for job to complete, because we need the table name
292
294
  // TODO just because a job is "DONE" doesn't mean it ended correctly, should probably also confirm
293
295
  // status is successful & that table was created
294
296
  // TODO this needs better error handling and a timeout so that issues dont result in infinite looping
295
- while (metaData.status.state !== "DONE") {
296
- await new Promise((resolve) => setTimeout(resolve, 1000));
297
+ while (metaData.status.state !== 'DONE') {
298
+ await new Promise(resolve => setTimeout(resolve, 1000));
297
299
  [metaData] = await job.getMetadata();
298
300
  }
299
301
  // save table name
@@ -306,7 +308,7 @@ class BigQueryConnection {
306
308
  return fullTableName;
307
309
  }
308
310
  else {
309
- throw new Error("bigquery.job.getMetadata() - metadata should have configuration but does not");
311
+ throw new Error('bigquery.job.getMetadata() - metadata should have configuration but does not');
310
312
  }
311
313
  }
312
314
  catch (e) {
@@ -332,9 +334,9 @@ class BigQueryConnection {
332
334
  throw new Error(`Table ${tableName} already exists`);
333
335
  }
334
336
  const [job] = await this.bigQuery.createQueryJob({
335
- "query": sqlCommand,
336
- "location": this.location,
337
- "destination": table
337
+ query: sqlCommand,
338
+ location: this.location,
339
+ destination: table,
338
340
  });
339
341
  // if creating this job didn't throw, there's an ID.
340
342
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -348,44 +350,42 @@ class BigQueryConnection {
348
350
  const type = field.type;
349
351
  const name = field.name;
350
352
  // check for an array
351
- if (field.mode === "REPEATED" && !["STRUCT", "RECORD"].includes(type)) {
353
+ if (field.mode === 'REPEATED' && !['STRUCT', 'RECORD'].includes(type)) {
352
354
  const malloyType = this.bqToMalloyTypes[type];
353
355
  if (malloyType) {
354
356
  const innerStructDef = {
355
- "type": "struct",
357
+ type: 'struct',
356
358
  name,
357
- "dialect": this.dialectName,
358
- "structSource": { "type": "nested" },
359
- "structRelationship": {
360
- "type": "nested",
361
- "field": name,
362
- "isArray": true
359
+ dialect: this.dialectName,
360
+ structSource: { type: 'nested' },
361
+ structRelationship: {
362
+ type: 'nested',
363
+ field: name,
364
+ isArray: true,
363
365
  },
364
- "fields": [{ ...malloyType, "name": "value" }]
366
+ fields: [{ ...malloyType, name: 'value' }],
365
367
  };
366
368
  structDef.fields.push(innerStructDef);
367
369
  }
368
370
  }
369
- else if (["STRUCT", "RECORD"].includes(type)) {
371
+ else if (['STRUCT', 'RECORD'].includes(type)) {
370
372
  const innerStructDef = {
371
- "type": "struct",
373
+ type: 'struct',
372
374
  name,
373
- "dialect": this.dialectName,
374
- "structSource": field.mode === "REPEATED"
375
- ? { "type": "nested" }
376
- : { "type": "inline" },
377
- "structRelationship": field.mode === "REPEATED"
378
- ? { "type": "nested", "field": name, "isArray": false }
379
- : { "type": "inline" },
380
- "fields": []
375
+ dialect: this.dialectName,
376
+ structSource: field.mode === 'REPEATED' ? { type: 'nested' } : { type: 'inline' },
377
+ structRelationship: field.mode === 'REPEATED'
378
+ ? { type: 'nested', field: name, isArray: false }
379
+ : { type: 'inline' },
380
+ fields: [],
381
381
  };
382
382
  this.addFieldsToStructDef(innerStructDef, field);
383
383
  structDef.fields.push(innerStructDef);
384
384
  }
385
385
  else {
386
386
  const malloyType = this.bqToMalloyTypes[type] || {
387
- "type": "unsupported",
388
- "rawType": type.toLowerCase()
387
+ type: 'unsupported',
388
+ rawType: type.toLowerCase(),
389
389
  };
390
390
  structDef.fields.push({ name, ...malloyType });
391
391
  }
@@ -393,7 +393,7 @@ class BigQueryConnection {
393
393
  }
394
394
  tableURLtoTablePath(tableURL) {
395
395
  const { tablePath } = (0, malloy_1.parseTableURI)(tableURL);
396
- if (tablePath.split(".").length === 2) {
396
+ if (tablePath.split('.').length === 2) {
397
397
  return `${this.defaultProject}.${tablePath}`;
398
398
  }
399
399
  else {
@@ -402,43 +402,49 @@ class BigQueryConnection {
402
402
  }
403
403
  structDefFromTableSchema(tableURL, schemaInfo) {
404
404
  const structDef = {
405
- "type": "struct",
406
- "name": tableURL,
407
- "dialect": this.dialectName,
408
- "structSource": {
409
- "type": "table",
410
- "tablePath": this.tableURLtoTablePath(tableURL)
405
+ type: 'struct',
406
+ name: tableURL,
407
+ dialect: this.dialectName,
408
+ structSource: {
409
+ type: 'table',
410
+ tablePath: this.tableURLtoTablePath(tableURL),
411
411
  },
412
- "structRelationship": {
413
- "type": "basetable",
414
- "connectionName": this.name
412
+ structRelationship: {
413
+ type: 'basetable',
414
+ connectionName: this.name,
415
415
  },
416
- "fields": []
416
+ fields: [],
417
417
  };
418
418
  this.addFieldsToStructDef(structDef, schemaInfo.schema);
419
- if (schemaInfo.needsPartitionPsuedoColumn) {
419
+ if (schemaInfo.needsTableSuffixPseudoColumn) {
420
420
  structDef.fields.push({
421
- "type": "timestamp",
422
- "name": "_PARTITIONTIME"
421
+ type: 'string',
422
+ name: '_TABLE_SUFFIX',
423
+ });
424
+ }
425
+ if (schemaInfo.needsPartitionPseudoColumn) {
426
+ structDef.fields.push({
427
+ type: 'timestamp',
428
+ name: '_PARTITIONTIME',
423
429
  });
424
430
  }
425
431
  return structDef;
426
432
  }
427
433
  structDefFromSQLSchema(sqlBlock, tableFieldSchema) {
428
434
  const structDef = {
429
- "type": "struct",
430
- "name": sqlBlock.name,
431
- "dialect": this.dialectName,
432
- "structSource": {
433
- "type": "sql",
434
- "method": "subquery",
435
- sqlBlock
435
+ type: 'struct',
436
+ name: sqlBlock.name,
437
+ dialect: this.dialectName,
438
+ structSource: {
439
+ type: 'sql',
440
+ method: 'subquery',
441
+ sqlBlock,
436
442
  },
437
- "structRelationship": {
438
- "type": "basetable",
439
- "connectionName": this.name
443
+ structRelationship: {
444
+ type: 'basetable',
445
+ connectionName: this.name,
440
446
  },
441
- "fields": []
447
+ fields: [],
442
448
  };
443
449
  this.addFieldsToStructDef(structDef, tableFieldSchema);
444
450
  return structDef;
@@ -452,12 +458,12 @@ class BigQueryConnection {
452
458
  try {
453
459
  const tableFieldSchema = await this.getTableFieldSchema(tableURL);
454
460
  inCache = {
455
- "schema": this.structDefFromTableSchema(tableURL, tableFieldSchema)
461
+ schema: this.structDefFromTableSchema(tableURL, tableFieldSchema),
456
462
  };
457
463
  this.schemaCache.set(tableURL, inCache);
458
464
  }
459
465
  catch (error) {
460
- inCache = { "error": error.message };
466
+ inCache = { error: error.message };
461
467
  }
462
468
  }
463
469
  if (inCache.schema !== undefined) {
@@ -479,9 +485,9 @@ class BigQueryConnection {
479
485
  for (let retries = 0; retries < 3; retries++) {
480
486
  try {
481
487
  const [job] = await this.bigQuery.createQueryJob({
482
- "location": this.location,
483
- "query": sqlRef.selectStr,
484
- "dryRun": true
488
+ location: this.location,
489
+ query: sqlRef.selectStr,
490
+ dryRun: true,
485
491
  });
486
492
  return job.metadata.statistics.query.schema;
487
493
  }
@@ -498,11 +504,11 @@ class BigQueryConnection {
498
504
  try {
499
505
  const tableFieldSchema = await this.getSQLBlockSchema(sqlRef);
500
506
  inCache = {
501
- "structDef": this.structDefFromSQLSchema(sqlRef, tableFieldSchema)
507
+ structDef: this.structDefFromSQLSchema(sqlRef, tableFieldSchema),
502
508
  };
503
509
  }
504
510
  catch (error) {
505
- inCache = { "error": error.message };
511
+ inCache = { error: error.message };
506
512
  }
507
513
  this.sqlSchemaCache.set(key, inCache);
508
514
  }
@@ -514,8 +520,8 @@ class BigQueryConnection {
514
520
  async createBigQueryJobAndGetResults(sqlCommand, createQueryJobOptions, getQueryResultsOptions) {
515
521
  try {
516
522
  const job = await this.createBigQueryJob({
517
- "query": sqlCommand,
518
- ...createQueryJobOptions
523
+ query: sqlCommand,
524
+ ...createQueryJobOptions,
519
525
  });
520
526
  // TODO we should check if this is still required?
521
527
  // We do a simple retry-loop here, as a temporary fix for a transient
@@ -527,8 +533,8 @@ class BigQueryConnection {
527
533
  for (let retries = 0; retries < 3; retries++) {
528
534
  try {
529
535
  return await job.getQueryResults({
530
- "timeoutMs": 1000 * 60 * 2,
531
- ...getQueryResultsOptions
536
+ timeoutMs: 1000 * 60 * 2,
537
+ ...getQueryResultsOptions,
532
538
  });
533
539
  }
534
540
  catch (fetchError) {
@@ -543,10 +549,10 @@ class BigQueryConnection {
543
549
  }
544
550
  async createBigQueryJob(createQueryJobOptions) {
545
551
  const [job] = await this.bigQuery.createQueryJob({
546
- "location": this.location,
547
- "maximumBytesBilled": this.config.maximumBytesBilled || MAXIMUM_BYTES_BILLED,
548
- "jobTimeoutMs": Number(this.config.timeoutMs) || TIMEOUT_MS,
549
- ...createQueryJobOptions
552
+ location: this.location,
553
+ maximumBytesBilled: this.config.maximumBytesBilled || MAXIMUM_BYTES_BILLED,
554
+ jobTimeoutMs: Number(this.config.timeoutMs) || TIMEOUT_MS,
555
+ ...createQueryJobOptions,
550
556
  });
551
557
  return job;
552
558
  }
@@ -563,9 +569,9 @@ class BigQueryConnection {
563
569
  }
564
570
  bigQuery
565
571
  .createQueryStream(sqlCommand)
566
- .on("error", onError)
567
- .on("data", handleData)
568
- .on("end", onEnd);
572
+ .on('error', onError)
573
+ .on('data', handleData)
574
+ .on('end', onEnd);
569
575
  }
570
576
  return (0, malloy_1.toAsyncGenerator)(streamBigQuery);
571
577
  }
@@ -575,6 +581,6 @@ class BigQueryConnection {
575
581
  }
576
582
  exports.BigQueryConnection = BigQueryConnection;
577
583
  BigQueryConnection.DEFAULT_QUERY_OPTIONS = {
578
- "rowLimit": 10
584
+ rowLimit: 10,
579
585
  };
580
586
  //# sourceMappingURL=bigquery_connection.js.map
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { BigQueryConnection } from "./bigquery_connection";
1
+ export { BigQueryConnection } from './bigquery_connection';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/db-bigquery",
3
- "version": "0.0.24-dev230224214106",
3
+ "version": "0.0.24-dev230301165401",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,6 +9,9 @@
9
9
  "type": "git",
10
10
  "url": "https://github.com/malloydata/malloy"
11
11
  },
12
+ "engines": {
13
+ "node": ">=10"
14
+ },
12
15
  "scripts": {
13
16
  "lint": "eslint '**/*.ts{,x}'",
14
17
  "lint-fix": "eslint '**/*.ts{,x}' --fix",
@@ -21,7 +24,8 @@
21
24
  "dependencies": {
22
25
  "@google-cloud/bigquery": "^5.5.0",
23
26
  "@google-cloud/common": "^3.6.0",
24
- "@malloydata/malloy": "^0.0.24-dev230224214106",
27
+ "@google-cloud/paginator": "^4.0.1",
28
+ "@malloydata/malloy": "^0.0.24-dev230301165401",
25
29
  "gaxios": "^4.2.0"
26
30
  }
27
31
  }