Skip to content
This repository was archived by the owner on Feb 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fa11961
fix(docs): publish new version of docs with getTables documented
steffnay Jul 17, 2019
37489fd
update
steffnay Jul 30, 2019
690f652
pull upstream master
steffnay Aug 8, 2019
cdb10c7
update
steffnay Sep 5, 2019
8137b2a
merge upstream
steffnay Nov 2, 2019
172e47f
Merge branch 'master' of https://github.com/googleapis/nodejs-bigquery
steffnay Dec 4, 2019
7f1e88b
Merge branch 'master' of https://github.com/googleapis/nodejs-bigquery
steffnay Dec 9, 2019
7c7a582
Merge branch 'master' of https://github.com/googleapis/nodejs-bigquery
steffnay Dec 22, 2019
d6d38bf
draft
steffnay Dec 26, 2019
4bebe15
feat(namedParams): adds ability to provide optional named parameter t…
steffnay Dec 26, 2019
091803c
feat(types): adds optional provided types for positional params
steffnay Dec 26, 2019
9795176
linted
steffnay Dec 26, 2019
0f2894d
refactor(params): refactors positional params and tests
steffnay Dec 27, 2019
54fa1f8
chore: remove unnecessary test comments
steffnay Dec 27, 2019
1566bdb
refactor(getTypes): removes redundant providedType logic
steffnay Dec 27, 2019
2a10c07
Merge branch 'master' into param-types
steffnay Dec 27, 2019
9482ca3
Merge branch 'master' into param-types
Dec 30, 2019
62d4a77
refactor(params): adds tests and removes repeated null type logic
steffnay Jan 3, 2020
abd93b7
refactor: renames getType functions, adds throw if invalid types prov…
steffnay Jan 21, 2020
ff06130
Merge branch 'master' of https://github.com/googleapis/nodejs-bigquer…
steffnay Jan 21, 2020
6c97362
Merge branch 'param-types' of https://github.com/steffnay/nodejs-bigq…
steffnay Jan 21, 2020
e08f577
adds tests
steffnay Jan 23, 2020
56f750e
Merge branch 'master' into param-types
steffnay Jan 23, 2020
e34edca
Merge branch 'master' into param-types
Jan 27, 2020
0a6bc3c
test: refactors tests
steffnay Jan 27, 2020
fd332cd
Merge branch 'param-types' of https://github.com/steffnay/nodejs-bigq…
steffnay Jan 27, 2020
460b002
refactor: updates param types and tests
steffnay Jan 27, 2020
bac3935
fixes test and updates providedType
steffnay Jan 28, 2020
558ef38
adds interface for providedTypes and logic for nested structs
steffnay Jan 30, 2020
7c650f5
updates providedType type
steffnay Jan 30, 2020
1f160a2
Merge branch 'master' into param-types
steffnay Jan 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 158 additions & 20 deletions src/bigquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export type Query = JobRequest<bigquery.IJobConfigurationQuery> & {
// tslint:disable-next-line no-any
params?: any[] | {[param: string]: any};
dryRun?: boolean;
types?: string[] | string[][] | {[type: string]: string[]};
defaultDataset?: Dataset;
job?: Job;
maxResults?: number;
Expand Down Expand Up @@ -155,6 +156,12 @@ export interface BigQueryDatetimeOptions {
fractional?: string | number;
}

export type ProvidedTypeArray = Array<ProvidedTypeStruct | string | []>;

export interface ProvidedTypeStruct {
[key: string]: string | ProvidedTypeArray | ProvidedTypeStruct;
}

export type QueryParameter = bigquery.IQueryParameter;

export interface BigQueryOptions extends common.GoogleAuthOptions {
Expand Down Expand Up @@ -731,6 +738,66 @@ export class BigQuery extends common.Service {
return BigQuery.geography(value);
}

/**
* Return a value's provided type.
*
* @private
*
* @throws {error} If the type provided is invalid.
*
* @see [Data Type]{@link https://cloud.google.com/bigquery/data-types}
*
* @param {*} providedType The type.
* @returns {string} The valid type provided.
*/
static getTypeDescriptorFromProvidedType_(
providedType: string | ProvidedTypeStruct | ProvidedTypeArray
): ValueType {
// The list of types can be found in src/types.d.ts
const VALID_TYPES = [
'DATE',
'DATETIME',
'TIME',
'TIMESTAMP',
Comment thread
steffnay marked this conversation as resolved.
'BYTES',
'NUMERIC',
'BOOL',
'INT64',
'FLOAT64',
'STRING',
'GEOGRAPHY',
'ARRAY',
'STRUCT',
];

if (is.array(providedType)) {
providedType = providedType as Array<ProvidedTypeStruct | string | []>;
return {
type: 'ARRAY',
arrayType: BigQuery.getTypeDescriptorFromProvidedType_(providedType[0]),
};
} else if (is.object(providedType)) {
return {
type: 'STRUCT',
structTypes: Object.keys(providedType).map(prop => {
return {
name: prop,
type: BigQuery.getTypeDescriptorFromProvidedType_(
(providedType as ProvidedTypeStruct)[prop]
),
};
}),
};
}

providedType = (providedType as string).toUpperCase();
if (!VALID_TYPES.includes(providedType)) {
throw new Error(`Invalid type provided: "${providedType}"`);
}

return {type: providedType.toUpperCase()};
}

/**
* Detect a value's type.
*
Expand All @@ -743,10 +810,16 @@ export class BigQuery extends common.Service {
* @param {*} value The value.
* @returns {string} The type detected from the value.
*/
// tslint:disable-next-line no-any
static getType_(value: any): ValueType {
static getTypeDescriptorFromValue_(
// tslint:disable-next-line: no-any
value: any
): ValueType {
let typeName;

if (value === null) {
throw new Error('Type must be provided for null values.');
}

if (value instanceof BigQueryDate) {
typeName = 'DATE';
} else if (value instanceof BigQueryDatetime) {
Expand All @@ -760,9 +833,12 @@ export class BigQuery extends common.Service {
} else if (value instanceof Big) {
typeName = 'NUMERIC';
} else if (is.array(value)) {
if (value.length === 0) {
throw new Error('Type must be provided for empty array.');
}
return {
type: 'ARRAY',
arrayType: BigQuery.getType_(value[0]),
arrayType: BigQuery.getTypeDescriptorFromValue_(value[0]),
};
} else if (is.boolean(value)) {
typeName = 'BOOL';
Expand All @@ -774,7 +850,7 @@ export class BigQuery extends common.Service {
structTypes: Object.keys(value).map(prop => {
return {
name: prop,
type: BigQuery.getType_(value[prop]),
type: BigQuery.getTypeDescriptorFromValue_(value[prop]),
};
}),
};
Expand Down Expand Up @@ -806,33 +882,52 @@ export class BigQuery extends common.Service {
* @param {*} value The value.
* @returns {object} A properly-formed `queryParameter` object.
*/
// tslint:disable-next-line no-any
static valueToQueryParameter_(value: any) {
static valueToQueryParameter_(
// tslint:disable-next-line: no-any
value: any,
providedType?: string | ProvidedTypeStruct | ProvidedTypeArray
) {
if (is.date(value)) {
value = BigQuery.timestamp(value as Date);
}

const parameterType = BigQuery.getType_(value);
let parameterType: bigquery.IQueryParameterType;
if (providedType) {
parameterType = BigQuery.getTypeDescriptorFromProvidedType_(providedType);
} else {
parameterType = BigQuery.getTypeDescriptorFromValue_(value);
}
const queryParameter: QueryParameter = {parameterType, parameterValue: {}};

const typeName = queryParameter!.parameterType!.type!;

if (typeName === 'ARRAY') {
queryParameter.parameterValue!.arrayValues = (value as Array<{}>).map(
itemValue => {
const value = getValue(itemValue, parameterType.arrayType!);
if (is.object(value) || is.array(value)) {
return BigQuery.valueToQueryParameter_(value).parameterValue!;
if (is.array(providedType)) {
providedType = providedType as [];
return BigQuery.valueToQueryParameter_(value, providedType[0])
.parameterValue!;
} else {
return BigQuery.valueToQueryParameter_(value).parameterValue!;
}
}
return {value} as bigquery.IQueryParameterValue;
}
);
} else if (typeName === 'STRUCT') {
queryParameter.parameterValue!.structValues = Object.keys(value).reduce(
(structValues, prop) => {
const nestedQueryParameter = BigQuery.valueToQueryParameter_(
value[prop]
);
let nestedQueryParameter;
if (providedType) {
nestedQueryParameter = BigQuery.valueToQueryParameter_(
value[prop],
(providedType as ProvidedTypeStruct)[prop]
);
} else {
nestedQueryParameter = BigQuery.valueToQueryParameter_(value[prop]);
}
// tslint:disable-next-line no-any
(structValues as any)[prop] = nestedQueryParameter.parameterValue;
return structValues;
Expand Down Expand Up @@ -1055,18 +1150,61 @@ export class BigQuery extends common.Service {
query.queryParameters = [];

// tslint:disable-next-line forin
for (const namedParamater in query.params) {
const value = query.params[namedParamater];
const queryParameter = BigQuery.valueToQueryParameter_(value);
queryParameter.name = namedParamater;
for (const namedParameter in query.params) {
const value = query.params[namedParameter];
let queryParameter;

if (query.types) {
if (!is.object(query.types)) {
throw new Error(
'Provided types must match the value type passed to `params`'
);
}

if (query.types[namedParameter]) {
queryParameter = BigQuery.valueToQueryParameter_(
value,
query.types[namedParameter]
);
} else {
throw new Error(
`Type not provided for parameter: ${namedParameter}`
);
}
} else {
queryParameter = BigQuery.valueToQueryParameter_(value);
}

queryParameter.name = namedParameter;
query.queryParameters.push(queryParameter);
}
} else {
query.queryParameters = query.params.map(
BigQuery.valueToQueryParameter_
);
}
query.queryParameters = [];

if (query.types) {
if (!is.array(query.types)) {
throw new Error(
'Provided types must match the value type passed to `params`'
);
}

if (query.params.length !== query.types.length) {
throw new Error('Incorrect number of parameter types provided.');
}
query.params.forEach((value: {}, i: number) => {
const queryParameter = BigQuery.valueToQueryParameter_(
value,
query.types[i]
);
query.queryParameters.push(queryParameter);
});
} else {
query.params.forEach((value: {}) => {
const queryParameter = BigQuery.valueToQueryParameter_(value);
query.queryParameters.push(queryParameter);
});
}
}
delete query.params;
}

Expand Down
20 changes: 20 additions & 0 deletions system-test/bigquery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,15 @@ describe('BigQuery', () => {
);
});

it('should work with empty arrays', async () => {
const [rows] = await bigquery.query({
query: 'SELECT * FROM UNNEST (?)',
params: [[]],
types: [['INT64']],
});
assert.strictEqual(rows.length, 0);
});

it('should work with structs', done => {
bigquery.query(
{
Expand Down Expand Up @@ -1344,6 +1353,17 @@ describe('BigQuery', () => {
);
});

it('should work with empty arrays', async () => {
const [rows] = await bigquery.query({
query: 'SELECT * FROM UNNEST (@nums)',
params: {
nums: [],
},
types: {nums: ['INT64']},
});
assert.strictEqual(rows.length, 0);
});

it('should work with structs', done => {
bigquery.query(
{
Expand Down
Loading