From b359c67b11c5d3a55c597a0d7210bca96aca4d69 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Mon, 10 Jan 2022 10:47:59 +0530 Subject: [PATCH 01/15] feat: Database Dialect support --- src/instance.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/instance.ts b/src/instance.ts index f34e39f5e..a67bfe4d5 100644 --- a/src/instance.ts +++ b/src/instance.ts @@ -888,10 +888,17 @@ class Instance extends common.GrpcServiceObject { const poolOptions = options.poolOptions; const poolCtor = options.poolCtor; + let createStatement = 'CREATE DATABASE `' + name.split('/').pop() + '`'; + if ( + databaseAdmin.spanner.admin.database.v1.DatabaseDialect.POSTGRESQL === + options.databaseDialect + ) { + createStatement = 'CREATE DATABASE "' + name.split('/').pop() + '"'; + } const reqOpts = extend( { parent: this.formattedName_, - createStatement: 'CREATE DATABASE `' + name.split('/').pop() + '`', + createStatement: createStatement, }, options ); From 9c10094ec582d63f8f62f226361841cc4c142909 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Tue, 25 Jan 2022 18:56:42 +0530 Subject: [PATCH 02/15] feat: Adding support for PG Numeric --- src/codec.ts | 43 +++++++++++++++++++++++++- src/index.ts | 31 ++++++++++++++++++- test/codec.ts | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ test/index.ts | 18 +++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) diff --git a/src/codec.ts b/src/codec.ts index 5c3ffb97a..a9211cf6a 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -216,6 +216,26 @@ export class Numeric { } } +/** + * @typedef PGNumeric + * @see Spanner.pgNumeric + */ +export class PGNumeric { + value: string; + constructor(pgValue: string) { + this.value = pgValue; + } + valueOf(): Big { + if (this.value.toLowerCase() === 'nan') { + throw new Error(`${this.value} cannot be converted to a numeric value`); + } + return new Big(this.value); + } + toJSON(): string { + return this.valueOf().toJSON(); + } +} + /** * @typedef JSONOptions * @property {boolean} [wrapNumbers=false] Indicates if the numbers should be @@ -333,6 +353,13 @@ function decode(value: Value, type: spannerClient.spanner.v1.Type): Value { break; case spannerClient.spanner.v1.TypeCode.NUMERIC: case 'NUMERIC': + if ( + type.typeAnnotation === + spannerClient.spanner.v1.TypeAnnotationCode.PG_NUMERIC + ) { + decoded = new PGNumeric(decoded); + break; + } decoded = new Numeric(decoded); break; case spannerClient.spanner.v1.TypeCode.TIMESTAMP: @@ -413,6 +440,10 @@ function encodeValue(value: Value): Value { return value.value; } + if (value instanceof PGNumeric) { + return value.value; + } + if (Buffer.isBuffer(value)) { return value.toString('base64'); } @@ -446,6 +477,7 @@ const TypeCode: { int64: 'INT64', float64: 'FLOAT64', numeric: 'NUMERIC', + pgNumeric: 'NUMERIC', timestamp: 'TIMESTAMP', date: 'DATE', string: 'STRING', @@ -524,6 +556,10 @@ function getType(value: Value): Type { return {type: 'numeric'}; } + if (value instanceof PGNumeric) { + return {type: 'pgNumeric'}; + } + if (is.boolean(value)) { return {type: 'bool'}; } @@ -639,7 +675,7 @@ function createTypeObject( friendlyType = 'unspecified'; } - if (is.string(friendlyType)) { + if (typeof friendlyType === 'string') { friendlyType = {type: friendlyType} as Type; } @@ -662,6 +698,10 @@ function createTypeObject( }; } + if (friendlyType.type === 'pgNumeric') { + type.typeAnnotation = + spannerClient.spanner.v1.TypeAnnotationCode.PG_NUMERIC; + } return type; } @@ -674,6 +714,7 @@ export const codec = { Float, Int, Numeric, + PGNumeric, convertFieldsToJson, decode, encode, diff --git a/src/index.ts b/src/index.ts index e0873b95e..1058d3ede 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,15 @@ import * as path from 'path'; import {common as p} from 'protobufjs'; import * as streamEvents from 'stream-events'; import * as through from 'through2'; -import {codec, Float, Int, Numeric, SpannerDate, Struct} from './codec'; +import { + codec, + Float, + Int, + Numeric, + PGNumeric, + SpannerDate, + Struct, +} from './codec'; import {Backup} from './backup'; import {Database} from './database'; import { @@ -185,6 +193,10 @@ class Spanner extends GrpcService { * @type {string} */ static COMMIT_TIMESTAMP = 'spanner.commit_timestamp()'; + static POSTGRESQL = + google.spanner.admin.database.v1.DatabaseDialect.POSTGRESQL; + static GOOGLE_STANDARD_SQL = + google.spanner.admin.database.v1.DatabaseDialect.GOOGLE_STANDARD_SQL; /** * Gets the configured Spanner emulator host from an environment variable. @@ -1274,6 +1286,22 @@ class Spanner extends GrpcService { return new codec.Numeric(value); } + /** + * Helper function to get a Cloud Spanner pgNumeric object. + * + * @param {string} value The pgNumeric value as a string. + * @returns {PGNumeric} + * + * @example + * ``` + * const {Spanner} = require('@google-cloud/spanner'); + * const pgNumeric = Spanner.pgNumeric("3.141592653"); + * ``` + */ + static pgNumeric(value): PGNumeric { + return new codec.PGNumeric(value); + } + /** * Helper function to get a Cloud Spanner Struct object. * @@ -1309,6 +1337,7 @@ promisifyAll(Spanner, { 'instance', 'int', 'numeric', + 'pgNumeric', 'operation', 'timestamp', ], diff --git a/test/codec.ts b/test/codec.ts index 3842bfa1a..43dec97e8 100644 --- a/test/codec.ts +++ b/test/codec.ts @@ -188,6 +188,46 @@ describe('codec', () => { }); }); + describe('PGNumeric', () => { + it('should store value as a string', () => { + const value = '8.01911'; + const pgNumeric = new codec.PGNumeric(value); + + assert.strictEqual(pgNumeric.value, '8.01911'); + }); + + it('should store NaN value as a string', () => { + const value = 'NaN'; + const pgNumeric = new codec.PGNumeric(value); + + assert.strictEqual(pgNumeric.value, 'NaN'); + }); + + it('should return as a Big', () => { + const value = '8.01911'; + const pgNumeric = new codec.PGNumeric(value); + + const expected = new Big(value); + assert.ok(pgNumeric.valueOf().eq(expected)); + }); + + it('should throw an error when trying to return NaN as a Big', () => { + const value = 'NaN'; + const pgNumeric = new codec.PGNumeric(value); + + assert.throws(() => { + pgNumeric.valueOf(); + }, new RegExp('NaN cannot be converted to a numeric value')); + }); + + it('toJSON', () => { + const value = '8.01911'; + const pgNumeric = new codec.PGNumeric(value); + + assert.strictEqual(pgNumeric.toJSON(), value); + }); + }); + describe('Struct', () => { describe('toJSON', () => { it('should covert the struct to JSON', () => { @@ -463,6 +503,18 @@ describe('codec', () => { assert.strictEqual(decoded.value, value); }); + it('should decode PG NUMERIC', () => { + const value = '8.01911'; + + const decoded = codec.decode(value, { + code: google.spanner.v1.TypeCode.NUMERIC, + typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_NUMERIC, + }); + + assert(decoded instanceof codec.PGNumeric); + assert.strictEqual(decoded.value, value); + }); + it('should decode JSON', () => { const value = '{"result":true, "count":42}'; const expected = JSON.parse(value); @@ -618,6 +670,14 @@ describe('codec', () => { assert.strictEqual(encoded, value.value); }); + it('should stringify PG NUMERIC', () => { + const value = new codec.PGNumeric('8.01911'); + + const encoded = codec.encode(value); + + assert.strictEqual(encoded, value.value); + }); + it('should encode ARRAY and inner members', () => { const value = [5]; @@ -811,6 +871,12 @@ describe('codec', () => { }, }); }); + + it('should determine if the value is a PGNumeric', () => { + assert.deepStrictEqual(codec.getType(new codec.PGNumeric('7248')), { + type: 'pgNumeric', + }); + }); }); describe('convertToListValue', () => { @@ -1019,5 +1085,22 @@ describe('codec', () => { }, }); }); + it('should set code and typeAnnotation for pgNumeric string', () => { + const type = codec.createTypeObject('pgNumeric'); + + assert.deepStrictEqual(type, { + code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.NUMERIC], + typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_NUMERIC, + }); + }); + + it('should set code and typeAnnotation for pgNumeric friendlyType object', () => { + const type = codec.createTypeObject({type: 'pgNumeric'}); + + assert.deepStrictEqual(type, { + code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.NUMERIC], + typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_NUMERIC, + }); + }); }); }); diff --git a/test/index.ts b/test/index.ts index 92a4a9e7d..c08896796 100644 --- a/test/index.ts +++ b/test/index.ts @@ -85,6 +85,7 @@ const fakePfy = extend({}, pfy, { 'instance', 'int', 'numeric', + 'pgNumeric', 'operation', 'timestamp', ]); @@ -514,6 +515,23 @@ describe('Spanner', () => { }); }); + describe('pgNumeric', () => { + it('should create a PGNumeric instance', () => { + const value = '3.145'; + const customValue = {value: '3.145'}; + + fakeCodec.PGNumeric = class { + constructor(value_) { + assert.strictEqual(value_, value); + return customValue; + } + }; + + const pgNumeric = Spanner.pgNumeric(value); + assert.strictEqual(pgNumeric, customValue); + }); + }); + describe('createInstance', () => { const NAME = 'instance-name'; let PATH; From 275e20edf18d8cc7e7e1482c29dbcf34536c12a7 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Tue, 22 Feb 2022 02:50:09 +0530 Subject: [PATCH 03/15] fix: fixing bug in PG Numeric Support --- src/codec.ts | 3 ++- src/index.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/codec.ts b/src/codec.ts index a9211cf6a..4a4755201 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -355,7 +355,8 @@ function decode(value: Value, type: spannerClient.spanner.v1.Type): Value { case 'NUMERIC': if ( type.typeAnnotation === - spannerClient.spanner.v1.TypeAnnotationCode.PG_NUMERIC + spannerClient.spanner.v1.TypeAnnotationCode.PG_NUMERIC || + type.typeAnnotation === 'PG_NUMERIC' ) { decoded = new PGNumeric(decoded); break; diff --git a/src/index.ts b/src/index.ts index 1058d3ede..e2a4bd7d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,7 +41,7 @@ import { CreateInstanceResponse, } from './instance'; import {grpc, GrpcClientOptions, CallOptions, GoogleError} from 'google-gax'; -import {google as instanceAdmin} from '../protos/protos'; +import {google, google as instanceAdmin} from '../protos/protos'; import { PagedOptions, PagedResponse, From 288e125105cd439a380a86b3e9e416c1c10f52d3 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Thu, 27 Jan 2022 19:27:56 +0530 Subject: [PATCH 04/15] feat: adding samples for PG Numeric --- samples/pg-database-create.js | 94 +++++++++++++++++++++ samples/pg-numeric-add-table.js | 72 ++++++++++++++++ samples/pg-numeric-insert-data.js | 114 ++++++++++++++++++++++++++ samples/pg-numeric-query-parameter.js | 90 ++++++++++++++++++++ samples/system-test/spanner.test.js | 79 +++++++++++++++++- 5 files changed, 447 insertions(+), 2 deletions(-) create mode 100644 samples/pg-database-create.js create mode 100644 samples/pg-numeric-add-table.js create mode 100644 samples/pg-numeric-insert-data.js create mode 100644 samples/pg-numeric-query-parameter.js diff --git a/samples/pg-database-create.js b/samples/pg-database-create.js new file mode 100644 index 000000000..6fa88f65e --- /dev/null +++ b/samples/pg-database-create.js @@ -0,0 +1,94 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Creates a PostgreSQL Database. +// usage: node createPgDatabase + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_create_database] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function createPgDatabase() { + // Gets a reference to a Cloud Spanner instance + const instance = spanner.instance(instanceId); + + // Set Dialect as PostgreSQL + const request = { + databaseDialect: Spanner.POSTGRESQL, + }; + + // Creates a PostgreSQL database. PostgreSQL create requests may not contain any additional + // DDL statements. We need to execute these separately after the database has been created. + const [database, operationCreate] = await instance.createDatabase( + databaseId, + request + ); + + console.log(`Waiting for operation on ${database.id} to complete...`); + await operationCreate.promise(); + await database.getMetadata(); + console.log( + `Created database ${databaseId} on instance ${instanceId} with dialect ${database.metadata.databaseDialect}.` + ); + + // Create a couple of tables using a separate request. We can use PostgreSQL style DDL as the + // database has been created with the PostgreSQL dialect. + const statements = [ + `CREATE TABLE Singers + (SingerId bigint NOT NULL, + FirstName varchar(1024), + LastName varchar(1024), + SingerInfo bytea, + PRIMARY KEY (SingerId) + ); + CREATE TABLE Albums + (AlbumId bigint NOT NULL, + SingerId bigint NOT NULL REFERENCES Singers (SingerId), + AlbumTitle text, + PRIMARY KEY (AlbumId) + );`, + ]; + const [operationUpdateDDL] = await database.updateSchema(statements); + await operationUpdateDDL.promise(); + console.log('Updated schema'); + } + createPgDatabase(); + // [END spanner_pg_create_database] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-numeric-add-table.js b/samples/pg-numeric-add-table.js new file mode 100644 index 000000000..70a84ceab --- /dev/null +++ b/samples/pg-numeric-add-table.js @@ -0,0 +1,72 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Adds a table with PostgreSQL Numeric column. +// usage: node addPgNumericTable + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_add_pg_numeric_table] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function addPgNumericTable() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const request = [ + `CREATE TABLE Venues + (VenueId bigint NOT NULL PRIMARY KEY, + Name varchar(1024) NOT NULL, + Revenues numeric + );`, + ]; + + // Updates schema by adding a new table. + const [operation] = await database.updateSchema(request); + + console.log(`Waiting for operation on ${databaseId} to complete...`); + + await operation.promise(); + + console.log(`Added table Revenues to database ${databaseId}.`); + } + addPgNumericTable(); + // [END spanner_add_pg_numeric_table] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-numeric-insert-data.js b/samples/pg-numeric-insert-data.js new file mode 100644 index 000000000..45b147d33 --- /dev/null +++ b/samples/pg-numeric-insert-data.js @@ -0,0 +1,114 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Inserts data to table with pg numeric column. +// usage: node insertPgNumericData + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_insert_pg_numeric_data] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function insertPgNumericData() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // Instantiate Spanner table objects. + const venuesTable = database.table('venues'); + const revenues1 = Spanner.pgNumeric('97372.3863'); + const revenues2 = Spanner.pgNumeric('7629'); + const revenues3 = Spanner.pgNumeric( + '1234567890.000012387293137871837817783828271273962412698378219372373072321997201370913293722379069869126846496978479842178917827474178248943891738912692839263826722738362982366832623281' + ); + const revenues4 = Spanner.pgNumeric('1.23e-7'); + const revenues5 = Spanner.pgNumeric('NaN'); + + const data = [ + { + venueid: '4', + name: 'Venue 4', + revenues: revenues1, + }, + { + venueid: '19', + name: 'Venue 19', + revenues: revenues2, + }, + { + venueid: '42', + name: 'Venue 42', + revenues: revenues3, + }, + { + venueid: '398', + name: 'Venue 398', + revenues: revenues4, + }, + { + venueid: '73', + name: 'Venue 73', + revenues: revenues5, + }, + { + venueid: '568', + name: 'Venue 568', + revenues: 'NaN', + }, + { + venueid: '3547', + name: 'Venue 3547', + revenues: '182730.128673921', + }, + ]; + + // Insert rows into the Venues table. + try { + await venuesTable.insert(data); + console.log('Inserted data.'); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + } + insertPgNumericData(); + // [END spanner_insert_pg_numeric_data] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-numeric-query-parameter.js b/samples/pg-numeric-query-parameter.js new file mode 100644 index 000000000..a36e40b4e --- /dev/null +++ b/samples/pg-numeric-query-parameter.js @@ -0,0 +1,90 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Queries data with a pg numeric parameter from a table with PostgreSQL Numeric column. +// usage: node queryWithPgNumericParameter + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_query_with_pg_numeric_parameter] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function queryWithPgNumericParameter() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const fieldType = { + type: 'pgNumeric', + }; + + const exampleNumeric = Spanner.pgNumeric('100000'); + + const query = { + sql: `SELECT venueid, revenues FROM venues + WHERE revenues < $1`, + params: { + p1: exampleNumeric, + }, + types: { + p1: fieldType, + }, + }; + + // Queries rows from the Venues table. + try { + const [rows] = await database.run(query); + + rows.forEach(row => { + const json = row.toJSON(); + // Check if revenue field is Null + const revenue = + json.revenues === null ? json.revenues : json.revenues.value; + console.log(`VenueId: ${json.venueid}, Revenue: ${revenue}`); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + } + queryWithPgNumericParameter(); + // [END spanner_query_with_pg_numeric_parameter] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/system-test/spanner.test.js b/samples/system-test/spanner.test.js index adeee8f38..d7c54a305 100644 --- a/samples/system-test/spanner.test.js +++ b/samples/system-test/spanner.test.js @@ -46,6 +46,7 @@ const INSTANCE_ID = const SAMPLE_INSTANCE_ID = `${PREFIX}-my-sample-instance-${CURRENT_TIME}`; const INSTANCE_ALREADY_EXISTS = !!process.env.SPANNERTEST_INSTANCE; const DATABASE_ID = `test-database-${CURRENT_TIME}`; +const PG_DATABASE_ID = `test-pg-database-${CURRENT_TIME}`; const RESTORE_DATABASE_ID = `test-database-${CURRENT_TIME}-r`; const ENCRYPTED_RESTORE_DATABASE_ID = `test-database-${CURRENT_TIME}-r-enc`; const VERSION_RETENTION_DATABASE_ID = `test-database-${CURRENT_TIME}-v`; @@ -55,8 +56,9 @@ const BACKUP_ID = `test-backup-${CURRENT_TIME}`; const COPY_BACKUP_ID = `test-copy-backup-${CURRENT_TIME}`; const ENCRYPTED_BACKUP_ID = `test-backup-${CURRENT_TIME}-enc`; const CANCELLED_BACKUP_ID = `test-backup-${CURRENT_TIME}-c`; -const LOCATION_ID = 'regional-us-west1'; -const KEY_LOCATION_ID = 'us-west1'; +const LOCATION_ID = 'regional-us-central1'; +const PG_LOCATION_ID = 'regional-us-west2'; +const KEY_LOCATION_ID = 'us-central1'; const KEY_RING_ID = 'test-key-ring-node'; const KEY_ID = 'test-key'; const DEFAULT_LEADER = 'us-central1'; @@ -222,6 +224,7 @@ describe('Spanner', () => { } else { await Promise.all([ instance.database(DATABASE_ID).delete(), + instance.database(PG_DATABASE_ID).delete(), instance.database(RESTORE_DATABASE_ID).delete(), instance.database(ENCRYPTED_RESTORE_DATABASE_ID).delete(), instance.backup(BACKUP_ID).delete(GAX_OPTIONS), @@ -1314,4 +1317,76 @@ describe('Spanner', () => { assert.include(output, 'CREATE TABLE Singers'); }); }); + + describe('postgreSQL', () => { + before(async () => { + const instance = spanner.instance(SAMPLE_INSTANCE_ID); + const [, operation] = await instance.create({ + config: PG_LOCATION_ID, + nodes: 1, + displayName: 'PostgreSQL Test', + labels: { + ['cloud_spanner_samples']: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + }); + await operation.promise(); + }); + + after(async () => { + const instance = spanner.instance(SAMPLE_INSTANCE_ID); + await instance.delete(); + }); + + // create_pg_database + it('should create an example postgreSQL database', async () => { + const output = execSync( + `node pg-database-create.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp(`Waiting for operation on ${PG_DATABASE_ID} to complete...`) + ); + assert.match( + output, + new RegExp( + `Created database ${PG_DATABASE_ID} on instance ${SAMPLE_INSTANCE_ID} with dialect POSTGRESQL.` + ) + ); + }); + + // add_pg_numeric_table + it('should add a table with PostgreSQL Numeric column', async () => { + const output = execSync( + `node pg-numeric-add-table.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp(`Waiting for operation on ${PG_DATABASE_ID} to complete...`) + ); + assert.match( + output, + new RegExp(`Added table Revenues to database ${PG_DATABASE_ID}.`) + ); + }); + + // insert_pg_numeric_data + it('should insert data to table with pg numeric column', async () => { + const output = execSync( + `node pg-numeric-insert-data.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match(output, new RegExp(`/Inserted data./`)); + }); + + // query_with_pg_numeric_parameter + it('should query data with a pg numeric parameter from a table with PostgreSQL Numeric column.', async () => { + const output = execSync( + `node pg-numeric-query-parameter.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match(output, new RegExp('/VenueId: 4, Revenue: 97372.3863/')); + assert.match(output, new RegExp('/VenueId: 19, Revenue: 7629/')); + assert.match(output, new RegExp('/VenueId: 398, Revenue: 0.000000123/')); + assert.match(output, new RegExp('/VenueId: 728, Revenue: 7629/')); + }); + }); }); From 49024ce7aac711491ab44aaf2ca40a61d77bddd2 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Tue, 8 Feb 2022 22:59:59 +0530 Subject: [PATCH 05/15] feat: Adding samples for PG --- samples/pg-case-sensitivity.js | 163 ++++++++++++++++++++++++++++ samples/pg-datatypes-casting.js | 85 +++++++++++++++ samples/pg-dml-batch.js | 105 ++++++++++++++++++ samples/pg-dml-partitioned.js | 71 ++++++++++++ samples/pg-dml-with-parameter.js | 77 +++++++++++++ samples/pg-functions.js | 73 +++++++++++++ samples/pg-interleaving.js | 80 ++++++++++++++ samples/pg-ordering-nulls.js | 135 +++++++++++++++++++++++ samples/pg-query-parameter.js | 88 +++++++++++++++ samples/pg-schema-information.js | 84 ++++++++++++++ samples/system-test/spanner.test.js | 138 ++++++++++++++++++++++- 11 files changed, 1093 insertions(+), 6 deletions(-) create mode 100644 samples/pg-case-sensitivity.js create mode 100644 samples/pg-datatypes-casting.js create mode 100644 samples/pg-dml-batch.js create mode 100644 samples/pg-dml-partitioned.js create mode 100644 samples/pg-dml-with-parameter.js create mode 100644 samples/pg-functions.js create mode 100644 samples/pg-interleaving.js create mode 100644 samples/pg-ordering-nulls.js create mode 100644 samples/pg-query-parameter.js create mode 100644 samples/pg-schema-information.js diff --git a/samples/pg-case-sensitivity.js b/samples/pg-case-sensitivity.js new file mode 100644 index 000000000..8fd5f7066 --- /dev/null +++ b/samples/pg-case-sensitivity.js @@ -0,0 +1,163 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Showcase the rules for case-sensitivity and case folding for a Spanner PostgreSQL database. +// usage: node pgCaseSensitivity + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_case_sensitivity] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgCaseSensitivity() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // DDL: Spanner PostgreSQL follows the case sensitivity rules of PostgreSQL. This means that: + // 1. Identifiers that are not double-quoted are folded to lower case. + // 2. Identifiers that are double-quoted retain their case and are case-sensitive. + // See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + // for more information. + const statements = [ + `CREATE TABLE Concerts + ( + -- ConcertId will be folded to "singerid" + ConcertId bigint NOT NULL PRIMARY KEY, + -- Location and Time are double-quoted and will therefore retain their + -- mixed case and are case-sensitive. This means that any statement that + -- references any of these columns must use double quotes. + "Location" varchar(1024) NOT NULL, + "Time" timestamptz NOT NULL + )`, + ]; + + // Updates schema by adding new tables. + const [operation] = await database.updateSchema(statements); + await operation.promise(); + console.log( + `Created table with case sensitive names in database ${databaseId} using PostgreSQL dialect.` + ); + + // Mutations: Column names in mutations are always case-insensitive, regardless whether the + // columns were double-quoted or not during creation. + const concertsTable = database.table('concerts'); + const data = [ + { + concertid: '4', + location: 'Venue 4', + time: new Date().toISOString(), + }, + ]; + try { + await concertsTable.insert(data); + console.log('Inserted data using mutations.'); + } catch (err) { + console.error('ERROR:', err); + } + + try { + const [rows] = await database.run({ + sql: 'SELECT * FROM Concerts', + }); + + console.log(`Concerts Table Data using Mutations:`); + rows.forEach(row => { + const json = row.toJSON(); + // Queries: ConcertId is automatically folded to lower case. Accessing the column by its name in + // a result set must therefore use all lower-case letters. Location and Time were double-quoted + // during creation, and retain their mixed case when returned in a result set. + console.log( + `concertid: ${json.concertid}, Location: ${json.Location}, Time: ${json.Time}` + ); + }); + } catch (err) { + console.error('ERROR:', err); + } + + // Aliases: Aliases are also identifiers, and specifying an alias in double quotes will make the alias + // retain its case. + try { + const [rows] = await database.run({ + sql: + 'SELECT concertid AS "ConcertId",' + + '"Location" AS "venue", "Time" FROM Concerts', + }); + + console.log(`Concerts Table Data using Aliases:`); + rows.forEach(row => { + const json = row.toJSON(); + // The aliases are double-quoted and therefore retains their mixed case. + console.log( + `concertid: ${json.ConcertId}, Location: ${json.venue}, Time: ${json.Time}` + ); + }); + } catch (err) { + console.error('ERROR:', err); + } + + // DML: Statements must also follow the PostgreSQL case rules. + try { + await database.runTransactionAsync(async transaction => { + await transaction.runUpdate({ + sql: `INSERT INTO Concerts (ConcertId, "Location", "Time") + VALUES ($1, $2, $3)`, + params: { + p1: 7, + p2: 'Venue 7', + p3: new Date().toISOString(), + }, + types: { + p1: {type: 'int64'}, + p2: {type: 'string'}, + p3: {type: 'timestamp'}, + }, + }); + console.log('Inserted data using DML.'); + await transaction.commit(); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + } + pgCaseSensitivity(); + // [END spanner_pg_case_sensitivity] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-datatypes-casting.js b/samples/pg-datatypes-casting.js new file mode 100644 index 000000000..c7a79c33b --- /dev/null +++ b/samples/pg-datatypes-casting.js @@ -0,0 +1,85 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Use cast operator to cast from one data type to another in a Spanner PostgreSQL database. +// usage: node pgDatatypeCasting + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_datatype_casting] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgDatatypeCasting() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // The `::` cast operator can be used to cast from one data type to another. + const query = { + sql: + "select 1::varchar as str, '2'::int as int, 3::decimal as dec, '4'::bytea as bytes, " + + "5::float as float, 'true'::bool as bool, " + + "'2021-11-03T09:35:01UTC'::timestamptz as timestamp", + }; + + try { + const [rows] = await database.run(query); + rows.forEach(row => { + const json = row.toJSON(); + console.log( + `Data types after casting \n` + + `String: ${json.str} \n` + + `Int: ${json.int} \n` + + `Decimal: ${json.dec.value} \n` + + `Bytes: ${json.bytes.valueOf()} \n` + + `Float: ${json.float} \n` + + `Bool: ${json.bool} \n` + + `Timestamp: ${Spanner.timestamp(json.timestamp)}` + ); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + await database.close(); + } + } + pgDatatypeCasting(); + // [END spanner_pg_datatype_casting] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-dml-batch.js b/samples/pg-dml-batch.js new file mode 100644 index 000000000..e0aed8b2e --- /dev/null +++ b/samples/pg-dml-batch.js @@ -0,0 +1,105 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Execute a batch of DML statements on a Spanner PostgreSQL database. +// usage: node pgBatchDml + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_batch_dml] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgBatchDml() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const insert1 = { + sql: `INSERT INTO singers (singerid, firstname, lastname) + VALUES ($1, $2, $3)`, + params: { + p1: 1, + p2: 'Alice', + p3: 'Henderson', + }, + types: { + p1: 'int64', + p2: 'string', + p3: 'string', + }, + }; + + const insert2 = { + sql: `INSERT INTO singers (singerid, firstname, lastname) + VALUES (2, 'Shruti', 'Hassan')`, + }; + + const insert3 = { + sql: `INSERT INTO singers (singerid, firstname, lastname) + VALUES ($1, $2, $3)`, + params: { + p1: 3, + p2: 'Olivia', + p3: 'Garcia', + }, + }; + + const dmlStatements = [insert1, insert2, insert3]; + + // Batches multiple DML statements into one request, which reduces the number of round trips that is needed for + // multiple DML statements. + try { + await database.runTransactionAsync(async transaction => { + const [rowCounts] = await transaction.batchUpdate(dmlStatements); + await transaction.commit(); + console.log( + `Successfully executed ${rowCounts.length} postgreSQL statements using Batch DML.` + ); + }); + } catch (err) { + console.error('ERROR:', err); + throw err; + } finally { + // Close the database when finished. + await database.close(); + } + } + pgBatchDml(); + // [END spanner_pg_batch_dml] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-dml-partitioned.js b/samples/pg-dml-partitioned.js new file mode 100644 index 000000000..3efeacb0b --- /dev/null +++ b/samples/pg-dml-partitioned.js @@ -0,0 +1,71 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Execute a Partitioned DML on a Spanner PostgreSQL database. +// usage: node pgPartitionedDml + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_partitioned_dml] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgPartitionedDml() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // Spanner PostgreSQL has the same transaction limits as normal Spanner. This includes a + // maximum of 20,000 mutations in a single read/write transaction. Large update operations can + // be executed using Partitioned DML. This is also supported on Spanner PostgreSQL. + // See https://cloud.google.com/spanner/docs/dml-partitioned for more information. + try { + const [rowCount] = await database.runPartitionedUpdate({ + sql: 'DELETE FROM Singers WHERE SingerId > 3', + }); + console.log(`Successfully deleted ${rowCount} record.`); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + } + pgPartitionedDml(); + // [END spanner_pg_partitioned_dml] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-dml-with-parameter.js b/samples/pg-dml-with-parameter.js new file mode 100644 index 000000000..10f7eaa0a --- /dev/null +++ b/samples/pg-dml-with-parameter.js @@ -0,0 +1,77 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Execute a DML statement with parameters on a Spanner PostgreSQL database. +// usage: node pgDmlWithParameters + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_dml_with_parameters] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgDmlWithParameters() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + database.runTransaction(async (err, transaction) => { + if (err) { + console.error(err); + return; + } + try { + const [rowCounts] = await transaction.runUpdate({ + sql: `INSERT INTO Singers (SingerId, FirstName, LastName) + VALUES (11, 'Timothy', 'Campbell')`, + }); + await transaction.commit(); + console.log( + `Successfully executed ${rowCounts} postgreSQL statements using DML.` + ); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + }); + } + pgDmlWithParameters(); + // [END spanner_pg_dml_with_parameters] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-functions.js b/samples/pg-functions.js new file mode 100644 index 000000000..a201fc9c7 --- /dev/null +++ b/samples/pg-functions.js @@ -0,0 +1,73 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Calls a server side function on a Spanner PostgreSQL database. +// usage: node pgFunctions + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_functions] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgFunctions() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const query = { + sql: 'SELECT to_timestamp(1284352323) AS time', + }; + + // Use the PostgreSQL `to_timestamp` function to convert a number of seconds since epoch to a timestamp. + try { + const [rows] = await database.run(query); + rows.forEach(row => { + const json = row.toJSON(); + console.log(`1284352323 seconds after epoch is ${json.time}`); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + await database.close(); + } + } + pgFunctions(); + // [END spanner_pg_functions] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-interleaving.js b/samples/pg-interleaving.js new file mode 100644 index 000000000..22ddb4fc6 --- /dev/null +++ b/samples/pg-interleaving.js @@ -0,0 +1,80 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Created interleaved table hierarchy using PostgreSQL dialect. +// usage: node pgInterleaving + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_interleaving] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgInterleaving() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const statements = [ + `CREATE TABLE Author + (AuthorId bigint NOT NULL, + FirstName varchar(1024), + LastName varchar(1024), + Rating double precision, + PRIMARY KEY (AuthorId) + ); + CREATE TABLE Book + (AuthorId bigint NOT NULL, + BookId bigint NOT NULL, + BookTitle text, + PRIMARY KEY (AuthorId, BookId) + ) INTERLEAVE IN PARENT Author ON DELETE CASCADE;`, + ]; + + // Updates schema by adding new tables. + const [operation] = await database.updateSchema(statements); + + console.log(`Waiting for operation on ${databaseId} to complete...`); + await operation.promise(); + + console.log( + `Created an interleaved table hierarchy in database ${databaseId} using PostgreSQL dialect.` + ); + } + pgInterleaving(); + // [END spanner_pg_interleaving] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-ordering-nulls.js b/samples/pg-ordering-nulls.js new file mode 100644 index 000000000..161ee3512 --- /dev/null +++ b/samples/pg-ordering-nulls.js @@ -0,0 +1,135 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Showcases how a Spanner PostgreSQL database orders null values in a query. +// usage: node pgOrderingNulls + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_ordering_nulls] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgOrderingNulls() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // Instantiate Spanner table objects. + const authorTable = database.table('author'); + + const data = [ + { + authorid: '4', + firstname: 'Alice', + }, + { + authorid: '5', + firstname: 'Bruce', + }, + { + authorid: '6', + firstname: null, + }, + ]; + + // Insert rows into the Author table. + try { + await authorTable.insert(data); + console.log('Inserted data.'); + } catch (err) { + console.error('ERROR:', err); + } + + try { + const [rows] = await database.run({ + sql: 'SELECT FirstName FROM Author ORDER BY FirstName', + }); + + console.log('\nAuthor ORDER BY FirstName'); + printAuthors(rows); + } catch (err) { + console.error('ERROR:', err); + } + + try { + const [rows] = await database.run({ + sql: 'SELECT FirstName FROM Author ORDER BY FirstName DESC', + }); + + console.log('\nAuthor ORDER BY FirstName DESC'); + printAuthors(rows); + } catch (err) { + console.error('ERROR:', err); + } + + try { + const [rows] = await database.run({ + sql: 'SELECT FirstName FROM Author ORDER BY FirstName NULLS FIRST', + }); + + console.log('\nAuthor ORDER BY FirstName NULLS FIRST'); + printAuthors(rows); + } catch (err) { + console.error('ERROR:', err); + } + + try { + const [rows] = await database.run({ + sql: 'SELECT FirstName FROM Author ORDER BY FirstName DESC NULLS LAST', + }); + + console.log('\nAuthor ORDER BY FirstName DESC NULLS LAST'); + printAuthors(rows); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + } + pgOrderingNulls(); + // [END spanner_pg_ordering_nulls] +} + +function printAuthors(rows) { + rows.forEach(row => { + const json = row.toJSON(); + console.log(`${json.firstname}`); + }); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-query-parameter.js b/samples/pg-query-parameter.js new file mode 100644 index 000000000..34d19d28b --- /dev/null +++ b/samples/pg-query-parameter.js @@ -0,0 +1,88 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Execute a query with parameters on a Spanner PostgreSQL database. +// usage: node queryWithPgParameter + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_query_with_pg_parameter] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function queryWithPgParameter() { + // Gets a reference to a Cloud Spanner instance and database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + const fieldType = { + type: 'string', + }; + + const query = { + sql: `SELECT singerid, firstname, lastname + FROM singers + WHERE firstname LIKE $1`, + params: { + p1: 'A%', + }, + types: { + p1: fieldType, + }, + }; + + // Queries rows from the Singers table. + try { + const [rows] = await database.run(query); + + rows.forEach(row => { + const json = row.toJSON(); + console.log( + `SingerId: ${json.singerid}, FirstName: ${json.firstname}, LastName: ${json.lastname}` + ); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + } + queryWithPgParameter(); + // [END spanner_query_with_pg_parameter] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/pg-schema-information.js b/samples/pg-schema-information.js new file mode 100644 index 000000000..69fbddc94 --- /dev/null +++ b/samples/pg-schema-information.js @@ -0,0 +1,84 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Query the information schema metadata in a Spanner PostgreSQL database. +// usage: node pgSchemaInformation + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_pg_schema_information] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud Spanner client library + const {Spanner} = require('@google-cloud/spanner'); + + // Instantiates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function pgSchemaInformation() { + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // Get all the user tables in the database. PostgreSQL uses the `public` schema for user + // tables. The table_catalog is equal to the database name. The `user_defined_...` columns + // are only available for PostgreSQL databases. + const query = { + sql: + 'SELECT table_catalog, table_schema, table_name, ' + + 'user_defined_type_catalog, ' + + 'user_defined_type_schema, ' + + 'user_defined_type_name ' + + 'FROM INFORMATION_SCHEMA.tables ' + + "WHERE table_schema='public'", + }; + + try { + const [rows] = await database.run(query); + rows.forEach(row => { + const json = row.toJSON(); + console.log( + `Table: ${json.table_catalog}.${json.table_schema}.${json.table_name} ` + + `(User defined type: ${json.user_defined_type_catalog}.${json.user_defined_type_schema}.${json.user_defined_type_name})` + ); + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + await database.close(); + } + } + pgSchemaInformation(); + // [END spanner_pg_schema_information] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/system-test/spanner.test.js b/samples/system-test/spanner.test.js index d7c54a305..b1a77f097 100644 --- a/samples/system-test/spanner.test.js +++ b/samples/system-test/spanner.test.js @@ -1339,7 +1339,7 @@ describe('Spanner', () => { }); // create_pg_database - it('should create an example postgreSQL database', async () => { + it('should create an example PostgreSQL database', async () => { const output = execSync( `node pg-database-create.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` ); @@ -1355,6 +1355,100 @@ describe('Spanner', () => { ); }); + // pg_interleaving + it('should create an interleaved table hierarchy using PostgreSQL dialect', async () => { + const output = execSync( + `node pg-interleaving.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp(`Waiting for operation on ${PG_DATABASE_ID} to complete...`) + ); + assert.match( + output, + new RegExp( + `Created an interleaved table hierarchy in database ${PG_DATABASE_ID} using PostgreSQL dialect.` + ) + ); + }); + + // pg_dml_with_parameter + it('should execute a DML statement with parameters on a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-dml-with-parameter.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp('Successfully executed 1 postgreSQL statements using DML') + ); + }); + + // pg_dml_batch + it('should execute a batch of DML statements on a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-dml-batch.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp( + 'Successfully executed 3 postgreSQL statements using Batch DML.' + ) + ); + }); + + // pg_dml_partitioned + it('should execute a partitioned DML on a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-dml-partitioned.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match(output, new RegExp('Successfully deleted 1 record.')); + }); + + // pg_query_with_parameters + it('should execute a query with parameters on a Spanner PostgreSQL database.', async () => { + const output = execSync( + `node pg-query-parameter.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp('SingerId: 1, FirstName: Alice, LastName: Henderson') + ); + }); + + // pg_schema_information + it('should query the information schema metadata in a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-schema-information.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp(`Table: ${PG_DATABASE_ID}.public.albums`) + ); + assert.match( + output, + new RegExp(`Table: ${PG_DATABASE_ID}.public.author`) + ); + assert.match(output, new RegExp(`Table: ${PG_DATABASE_ID}.public.book`)); + assert.match( + output, + new RegExp(`Table: ${PG_DATABASE_ID}.public.singers`) + ); + }); + + // pg_ordering_nulls + it('should order nulls as per clause in a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-ordering-nulls.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match(output, new RegExp('Author ORDER BY FirstName')); + assert.match(output, new RegExp('Author ORDER BY FirstName DESC')); + assert.match(output, new RegExp('Author ORDER BY FirstName NULLS FIRST')); + assert.match( + output, + new RegExp('Author ORDER BY FirstName DESC NULLS LAST') + ); + }); + // add_pg_numeric_table it('should add a table with PostgreSQL Numeric column', async () => { const output = execSync( @@ -1375,7 +1469,7 @@ describe('Spanner', () => { const output = execSync( `node pg-numeric-insert-data.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` ); - assert.match(output, new RegExp(`/Inserted data./`)); + assert.match(output, new RegExp('Inserted data.')); }); // query_with_pg_numeric_parameter @@ -1383,10 +1477,42 @@ describe('Spanner', () => { const output = execSync( `node pg-numeric-query-parameter.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` ); - assert.match(output, new RegExp('/VenueId: 4, Revenue: 97372.3863/')); - assert.match(output, new RegExp('/VenueId: 19, Revenue: 7629/')); - assert.match(output, new RegExp('/VenueId: 398, Revenue: 0.000000123/')); - assert.match(output, new RegExp('/VenueId: 728, Revenue: 7629/')); + assert.match(output, new RegExp('VenueId: 4, Revenue: 97372.3863')); + assert.match(output, new RegExp('VenueId: 19, Revenue: 7629')); + assert.match(output, new RegExp('VenueId: 398, Revenue: 0.000000123')); + }); + + // pg_case_sensitivity + it('should create case sensitive table and query the information in a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-case-sensitivity.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match( + output, + new RegExp( + `Created table with case sensitive names in database ${PG_DATABASE_ID} using PostgreSQL dialect.` + ) + ); + assert.match(output, new RegExp('Inserted data using mutations.')); + assert.match(output, new RegExp('Concerts Table Data using Mutations:')); + assert.match(output, new RegExp('Concerts Table Data using Aliases:')); + assert.match(output, new RegExp('Inserted data using DML.')); + }); + + // pg_datatypes_casting + it('should use cast operator to cast from one data type to another in a Spanner PostgreSQL database', async () => { + const output = execSync( + `node pg-datatypes-casting.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match(output, new RegExp('Data types after casting')); + }); + + // pg_functions + it('should call a server side function on a Spanner PostgreSQL database.', async () => { + const output = execSync( + `node pg-functions.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + ); + assert.match(output, new RegExp('1284352323 seconds after epoch is')); }); }); }); From 116287cec1cdc65d6b68c03ee9e4be9689e36de2 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Fri, 11 Feb 2022 16:52:22 +0530 Subject: [PATCH 06/15] feat: changes in samples as per comments,renaming tags, merging numeric samples --- samples/pg-case-sensitivity.js | 6 +- samples/pg-database-create.js | 4 +- samples/pg-datatypes-casting.js | 4 +- samples/pg-dml-batch.js | 4 +- samples/pg-dml-partitioned.js | 4 +- samples/pg-dml-with-parameter.js | 18 +++- samples/pg-functions.js | 4 +- samples/pg-interleaving.js | 4 +- samples/pg-numeric-add-table.js | 72 --------------- ...insert-data.js => pg-numeric-data-type.js} | 58 ++++++++++-- samples/pg-numeric-query-parameter.js | 90 ------------------- samples/pg-ordering-nulls.js | 4 +- samples/pg-query-parameter.js | 4 +- samples/pg-schema-information.js | 4 +- samples/system-test/spanner.test.js | 24 ++--- 15 files changed, 92 insertions(+), 212 deletions(-) delete mode 100644 samples/pg-numeric-add-table.js rename samples/{pg-numeric-insert-data.js => pg-numeric-data-type.js} (65%) delete mode 100644 samples/pg-numeric-query-parameter.js diff --git a/samples/pg-case-sensitivity.js b/samples/pg-case-sensitivity.js index 8fd5f7066..2ba84760b 100644 --- a/samples/pg-case-sensitivity.js +++ b/samples/pg-case-sensitivity.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_case_sensitivity] + // [START spanner_postgresql_case_sensitivity] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -51,7 +51,7 @@ function main( const statements = [ `CREATE TABLE Concerts ( - -- ConcertId will be folded to "singerid" + -- ConcertId will be folded to "concertid" ConcertId bigint NOT NULL PRIMARY KEY, -- Location and Time are double-quoted and will therefore retain their -- mixed case and are case-sensitive. This means that any statement that @@ -153,7 +153,7 @@ function main( } } pgCaseSensitivity(); - // [END spanner_pg_case_sensitivity] + // [END spanner_postgresql_case_sensitivity] } process.on('unhandledRejection', err => { diff --git a/samples/pg-database-create.js b/samples/pg-database-create.js index 6fa88f65e..d11282bf8 100644 --- a/samples/pg-database-create.js +++ b/samples/pg-database-create.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_create_database] + // [START spanner_postgresql_create_database] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -84,7 +84,7 @@ function main( console.log('Updated schema'); } createPgDatabase(); - // [END spanner_pg_create_database] + // [END spanner_postgresql_create_database] } process.on('unhandledRejection', err => { diff --git a/samples/pg-datatypes-casting.js b/samples/pg-datatypes-casting.js index c7a79c33b..2d07d7bb3 100644 --- a/samples/pg-datatypes-casting.js +++ b/samples/pg-datatypes-casting.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_datatype_casting] + // [START spanner_postgresql_cast_data_type] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -75,7 +75,7 @@ function main( } } pgDatatypeCasting(); - // [END spanner_pg_datatype_casting] + // [END spanner_postgresql_cast_data_type] } process.on('unhandledRejection', err => { diff --git a/samples/pg-dml-batch.js b/samples/pg-dml-batch.js index e0aed8b2e..2829a6301 100644 --- a/samples/pg-dml-batch.js +++ b/samples/pg-dml-batch.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_batch_dml] + // [START spanner_postgresql_batch_dml] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -95,7 +95,7 @@ function main( } } pgBatchDml(); - // [END spanner_pg_batch_dml] + // [END spanner_postgresql_batch_dml] } process.on('unhandledRejection', err => { diff --git a/samples/pg-dml-partitioned.js b/samples/pg-dml-partitioned.js index 3efeacb0b..28b5f064c 100644 --- a/samples/pg-dml-partitioned.js +++ b/samples/pg-dml-partitioned.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_partitioned_dml] + // [START spanner_postgresql_partitioned_dml] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -61,7 +61,7 @@ function main( } } pgPartitionedDml(); - // [END spanner_pg_partitioned_dml] + // [END spanner_postgresql_partitioned_dml] } process.on('unhandledRejection', err => { diff --git a/samples/pg-dml-with-parameter.js b/samples/pg-dml-with-parameter.js index 10f7eaa0a..d8d0122a8 100644 --- a/samples/pg-dml-with-parameter.js +++ b/samples/pg-dml-with-parameter.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_dml_with_parameters] + // [START spanner_postgresql_dml_with_parameters] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -51,8 +51,18 @@ function main( } try { const [rowCounts] = await transaction.runUpdate({ - sql: `INSERT INTO Singers (SingerId, FirstName, LastName) - VALUES (11, 'Timothy', 'Campbell')`, + sql: `INSERT INTO singers (singerid, firstname, lastname) + VALUES ($1, $2, $3)`, + params: { + p1: 11, + p2: 'Timothy', + p3: 'Campbell', + }, + types: { + p1: 'int64', + p2: 'string', + p3: 'string', + }, }); await transaction.commit(); console.log( @@ -67,7 +77,7 @@ function main( }); } pgDmlWithParameters(); - // [END spanner_pg_dml_with_parameters] + // [END spanner_postgresql_dml_with_parameters] } process.on('unhandledRejection', err => { diff --git a/samples/pg-functions.js b/samples/pg-functions.js index a201fc9c7..8cddfc0da 100644 --- a/samples/pg-functions.js +++ b/samples/pg-functions.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_functions] + // [START spanner_postgresql_functions] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -63,7 +63,7 @@ function main( } } pgFunctions(); - // [END spanner_pg_functions] + // [END spanner_postgresql_functions] } process.on('unhandledRejection', err => { diff --git a/samples/pg-interleaving.js b/samples/pg-interleaving.js index 22ddb4fc6..04e84ed80 100644 --- a/samples/pg-interleaving.js +++ b/samples/pg-interleaving.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_interleaving] + // [START spanner_postgresql_interleaved_table] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -70,7 +70,7 @@ function main( ); } pgInterleaving(); - // [END spanner_pg_interleaving] + // [END spanner_postgresql_interleaved_table] } process.on('unhandledRejection', err => { diff --git a/samples/pg-numeric-add-table.js b/samples/pg-numeric-add-table.js deleted file mode 100644 index 70a84ceab..000000000 --- a/samples/pg-numeric-add-table.js +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// sample-metadata: -// title: Adds a table with PostgreSQL Numeric column. -// usage: node addPgNumericTable - -'use strict'; - -function main( - instanceId = 'my-instance', - databaseId = 'my-database', - projectId = 'my-project-id' -) { - // [START spanner_add_pg_numeric_table] - /** - * TODO(developer): Uncomment these variables before running the sample. - */ - // const instanceId = 'my-instance'; - // const databaseId = 'my-database'; - // const projectId = 'my-project-id'; - - // Imports the Google Cloud Spanner client library - const {Spanner} = require('@google-cloud/spanner'); - - // Instantiates a client - const spanner = new Spanner({ - projectId: projectId, - }); - - async function addPgNumericTable() { - // Gets a reference to a Cloud Spanner instance and database. - const instance = spanner.instance(instanceId); - const database = instance.database(databaseId); - - const request = [ - `CREATE TABLE Venues - (VenueId bigint NOT NULL PRIMARY KEY, - Name varchar(1024) NOT NULL, - Revenues numeric - );`, - ]; - - // Updates schema by adding a new table. - const [operation] = await database.updateSchema(request); - - console.log(`Waiting for operation on ${databaseId} to complete...`); - - await operation.promise(); - - console.log(`Added table Revenues to database ${databaseId}.`); - } - addPgNumericTable(); - // [END spanner_add_pg_numeric_table] -} - -process.on('unhandledRejection', err => { - console.error(err.message); - process.exitCode = 1; -}); -main(...process.argv.slice(2)); diff --git a/samples/pg-numeric-insert-data.js b/samples/pg-numeric-data-type.js similarity index 65% rename from samples/pg-numeric-insert-data.js rename to samples/pg-numeric-data-type.js index 45b147d33..29947d725 100644 --- a/samples/pg-numeric-insert-data.js +++ b/samples/pg-numeric-data-type.js @@ -13,8 +13,8 @@ // limitations under the License. // sample-metadata: -// title: Inserts data to table with pg numeric column. -// usage: node insertPgNumericData +// title: Showcase how to work with the PostgreSQL NUMERIC/DECIMAL data type on a Spanner PostgreSQL database. +// usage: node pgNumericDataType 'use strict'; @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_insert_pg_numeric_data] + // [START spanner_postgresql_numeric_data_type] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -39,11 +39,25 @@ function main( projectId: projectId, }); - async function insertPgNumericData() { + async function pgNumericDataType() { // Gets a reference to a Cloud Spanner instance and database. const instance = spanner.instance(instanceId); const database = instance.database(databaseId); + const request = [ + `CREATE TABLE Venues + (VenueId bigint NOT NULL PRIMARY KEY, + Name varchar(1024) NOT NULL, + Revenues numeric + );`, + ]; + + // Updates schema by adding a new table. + const [operation] = await database.updateSchema(request); + console.log(`Waiting for operation on ${databaseId} to complete...`); + await operation.promise(); + console.log(`Added table venues to database ${databaseId}.`); + // Instantiate Spanner table objects. const venuesTable = database.table('venues'); const revenues1 = Spanner.pgNumeric('97372.3863'); @@ -98,13 +112,45 @@ function main( console.log('Inserted data.'); } catch (err) { console.error('ERROR:', err); + } + + const fieldType = { + type: 'pgNumeric', + }; + + const exampleNumeric = Spanner.pgNumeric('100000'); + + const query = { + sql: `SELECT venueid, revenues FROM venues + WHERE revenues < $1`, + params: { + p1: exampleNumeric, + }, + types: { + p1: fieldType, + }, + }; + + // Queries rows from the Venues table. + try { + const [rows] = await database.run(query); + + rows.forEach(row => { + const json = row.toJSON(); + // Check if revenue field is Null + const revenue = + json.revenues === null ? json.revenues : json.revenues.value; + console.log(`VenueId: ${json.venueid}, Revenue: ${revenue}`); + }); + } catch (err) { + console.error('ERROR:', err); } finally { // Close the database when finished. database.close(); } } - insertPgNumericData(); - // [END spanner_insert_pg_numeric_data] + pgNumericDataType(); + // [END spanner_postgresql_numeric_data_type] } process.on('unhandledRejection', err => { diff --git a/samples/pg-numeric-query-parameter.js b/samples/pg-numeric-query-parameter.js deleted file mode 100644 index a36e40b4e..000000000 --- a/samples/pg-numeric-query-parameter.js +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// sample-metadata: -// title: Queries data with a pg numeric parameter from a table with PostgreSQL Numeric column. -// usage: node queryWithPgNumericParameter - -'use strict'; - -function main( - instanceId = 'my-instance', - databaseId = 'my-database', - projectId = 'my-project-id' -) { - // [START spanner_query_with_pg_numeric_parameter] - /** - * TODO(developer): Uncomment these variables before running the sample. - */ - // const instanceId = 'my-instance'; - // const databaseId = 'my-database'; - // const projectId = 'my-project-id'; - - // Imports the Google Cloud Spanner client library - const {Spanner} = require('@google-cloud/spanner'); - - // Instantiates a client - const spanner = new Spanner({ - projectId: projectId, - }); - - async function queryWithPgNumericParameter() { - // Gets a reference to a Cloud Spanner instance and database. - const instance = spanner.instance(instanceId); - const database = instance.database(databaseId); - - const fieldType = { - type: 'pgNumeric', - }; - - const exampleNumeric = Spanner.pgNumeric('100000'); - - const query = { - sql: `SELECT venueid, revenues FROM venues - WHERE revenues < $1`, - params: { - p1: exampleNumeric, - }, - types: { - p1: fieldType, - }, - }; - - // Queries rows from the Venues table. - try { - const [rows] = await database.run(query); - - rows.forEach(row => { - const json = row.toJSON(); - // Check if revenue field is Null - const revenue = - json.revenues === null ? json.revenues : json.revenues.value; - console.log(`VenueId: ${json.venueid}, Revenue: ${revenue}`); - }); - } catch (err) { - console.error('ERROR:', err); - } finally { - // Close the database when finished. - database.close(); - } - } - queryWithPgNumericParameter(); - // [END spanner_query_with_pg_numeric_parameter] -} - -process.on('unhandledRejection', err => { - console.error(err.message); - process.exitCode = 1; -}); -main(...process.argv.slice(2)); diff --git a/samples/pg-ordering-nulls.js b/samples/pg-ordering-nulls.js index 161ee3512..acc108391 100644 --- a/samples/pg-ordering-nulls.js +++ b/samples/pg-ordering-nulls.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_ordering_nulls] + // [START spanner_postgresql_order_nulls] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -118,7 +118,7 @@ function main( } } pgOrderingNulls(); - // [END spanner_pg_ordering_nulls] + // [END spanner_postgresql_order_nulls] } function printAuthors(rows) { diff --git a/samples/pg-query-parameter.js b/samples/pg-query-parameter.js index 34d19d28b..f07708edd 100644 --- a/samples/pg-query-parameter.js +++ b/samples/pg-query-parameter.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_query_with_pg_parameter] + // [START spanner_postgresql_query_parameter] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -78,7 +78,7 @@ function main( } } queryWithPgParameter(); - // [END spanner_query_with_pg_parameter] + // [END spanner_postgresql_query_parameter] } process.on('unhandledRejection', err => { diff --git a/samples/pg-schema-information.js b/samples/pg-schema-information.js index 69fbddc94..e2392719d 100644 --- a/samples/pg-schema-information.js +++ b/samples/pg-schema-information.js @@ -23,7 +23,7 @@ function main( databaseId = 'my-database', projectId = 'my-project-id' ) { - // [START spanner_pg_schema_information] + // [START spanner_postgresql_information_schema] /** * TODO(developer): Uncomment these variables before running the sample. */ @@ -74,7 +74,7 @@ function main( } } pgSchemaInformation(); - // [END spanner_pg_schema_information] + // [END spanner_postgresql_information_schema] } process.on('unhandledRejection', err => { diff --git a/samples/system-test/spanner.test.js b/samples/system-test/spanner.test.js index b1a77f097..1cca65c23 100644 --- a/samples/system-test/spanner.test.js +++ b/samples/system-test/spanner.test.js @@ -532,7 +532,7 @@ describe('Spanner', () => { ); }); - // read_only_transaction + // read_only_transactioni it('should read an example table using transactions', async () => { const output = execSync( `${transactionCmd} readOnly ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` @@ -1449,10 +1449,10 @@ describe('Spanner', () => { ); }); - // add_pg_numeric_table - it('should add a table with PostgreSQL Numeric column', async () => { + // pg_numeric_data_type + it('should create a table, insert and query pg numeric data', async () => { const output = execSync( - `node pg-numeric-add-table.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + `node pg-numeric-data-type.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` ); assert.match( output, @@ -1460,23 +1460,9 @@ describe('Spanner', () => { ); assert.match( output, - new RegExp(`Added table Revenues to database ${PG_DATABASE_ID}.`) - ); - }); - - // insert_pg_numeric_data - it('should insert data to table with pg numeric column', async () => { - const output = execSync( - `node pg-numeric-insert-data.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` + new RegExp(`Added table venues to database ${PG_DATABASE_ID}.`) ); assert.match(output, new RegExp('Inserted data.')); - }); - - // query_with_pg_numeric_parameter - it('should query data with a pg numeric parameter from a table with PostgreSQL Numeric column.', async () => { - const output = execSync( - `node pg-numeric-query-parameter.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` - ); assert.match(output, new RegExp('VenueId: 4, Revenue: 97372.3863')); assert.match(output, new RegExp('VenueId: 19, Revenue: 7629')); assert.match(output, new RegExp('VenueId: 398, Revenue: 0.000000123')); From ad7c0f5c3d0838e33bf844228f72e180ffb75009 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Thu, 24 Mar 2022 14:47:35 +0530 Subject: [PATCH 07/15] comment changes --- samples/pg-case-sensitivity.js | 2 +- samples/pg-database-create.js | 2 +- samples/pg-datatypes-casting.js | 2 +- samples/pg-dml-batch.js | 2 +- samples/pg-dml-partitioned.js | 2 +- samples/pg-dml-with-parameter.js | 2 +- samples/pg-functions.js | 2 +- samples/pg-interleaving.js | 2 +- samples/pg-numeric-data-type.js | 2 +- samples/pg-ordering-nulls.js | 2 +- samples/pg-query-parameter.js | 2 +- samples/pg-schema-information.js | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/samples/pg-case-sensitivity.js b/samples/pg-case-sensitivity.js index 2ba84760b..0d0eae20f 100644 --- a/samples/pg-case-sensitivity.js +++ b/samples/pg-case-sensitivity.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Showcase the rules for case-sensitivity and case folding for a Spanner PostgreSQL database. -// usage: node pgCaseSensitivity +// usage: node pg-case-sensitivity.js 'use strict'; diff --git a/samples/pg-database-create.js b/samples/pg-database-create.js index d11282bf8..6fa389c9b 100644 --- a/samples/pg-database-create.js +++ b/samples/pg-database-create.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Creates a PostgreSQL Database. -// usage: node createPgDatabase +// usage: node pg-database-create.js 'use strict'; diff --git a/samples/pg-datatypes-casting.js b/samples/pg-datatypes-casting.js index 2d07d7bb3..59fcc35b0 100644 --- a/samples/pg-datatypes-casting.js +++ b/samples/pg-datatypes-casting.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Use cast operator to cast from one data type to another in a Spanner PostgreSQL database. -// usage: node pgDatatypeCasting +// usage: node pg-datatypes-casting.js 'use strict'; diff --git a/samples/pg-dml-batch.js b/samples/pg-dml-batch.js index 2829a6301..cf4a407ea 100644 --- a/samples/pg-dml-batch.js +++ b/samples/pg-dml-batch.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Execute a batch of DML statements on a Spanner PostgreSQL database. -// usage: node pgBatchDml +// usage: node pg-dml-batch.js 'use strict'; diff --git a/samples/pg-dml-partitioned.js b/samples/pg-dml-partitioned.js index 28b5f064c..33e4b3e9b 100644 --- a/samples/pg-dml-partitioned.js +++ b/samples/pg-dml-partitioned.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Execute a Partitioned DML on a Spanner PostgreSQL database. -// usage: node pgPartitionedDml +// usage: node pg-dml-partitioned.js 'use strict'; diff --git a/samples/pg-dml-with-parameter.js b/samples/pg-dml-with-parameter.js index d8d0122a8..eaf5200d7 100644 --- a/samples/pg-dml-with-parameter.js +++ b/samples/pg-dml-with-parameter.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Execute a DML statement with parameters on a Spanner PostgreSQL database. -// usage: node pgDmlWithParameters +// usage: node pg-dml-with-parameter.js 'use strict'; diff --git a/samples/pg-functions.js b/samples/pg-functions.js index 8cddfc0da..1824b3312 100644 --- a/samples/pg-functions.js +++ b/samples/pg-functions.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Calls a server side function on a Spanner PostgreSQL database. -// usage: node pgFunctions +// usage: node pg-functions.js 'use strict'; diff --git a/samples/pg-interleaving.js b/samples/pg-interleaving.js index 04e84ed80..c61a594b6 100644 --- a/samples/pg-interleaving.js +++ b/samples/pg-interleaving.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Created interleaved table hierarchy using PostgreSQL dialect. -// usage: node pgInterleaving +// usage: node pg-interleaving.js 'use strict'; diff --git a/samples/pg-numeric-data-type.js b/samples/pg-numeric-data-type.js index 29947d725..e2f458346 100644 --- a/samples/pg-numeric-data-type.js +++ b/samples/pg-numeric-data-type.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Showcase how to work with the PostgreSQL NUMERIC/DECIMAL data type on a Spanner PostgreSQL database. -// usage: node pgNumericDataType +// usage: node ppg-numeric-data-type.js 'use strict'; diff --git a/samples/pg-ordering-nulls.js b/samples/pg-ordering-nulls.js index acc108391..bb1a9b699 100644 --- a/samples/pg-ordering-nulls.js +++ b/samples/pg-ordering-nulls.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Showcases how a Spanner PostgreSQL database orders null values in a query. -// usage: node pgOrderingNulls +// usage: node pg-ordering-nulls.js 'use strict'; diff --git a/samples/pg-query-parameter.js b/samples/pg-query-parameter.js index f07708edd..a495e363a 100644 --- a/samples/pg-query-parameter.js +++ b/samples/pg-query-parameter.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Execute a query with parameters on a Spanner PostgreSQL database. -// usage: node queryWithPgParameter +// usage: node pg-query-parameter.js 'use strict'; diff --git a/samples/pg-schema-information.js b/samples/pg-schema-information.js index e2392719d..99dbd8235 100644 --- a/samples/pg-schema-information.js +++ b/samples/pg-schema-information.js @@ -14,7 +14,7 @@ // sample-metadata: // title: Query the information schema metadata in a Spanner PostgreSQL database. -// usage: node pgSchemaInformation +// usage: node pg-schema-information.js 'use strict'; From 8f9995163e06039be2ccda281dbcf63dd943712a Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Wed, 16 Mar 2022 11:21:47 +0530 Subject: [PATCH 08/15] fix: allowing number in pgNumeric --- samples/pg-numeric-data-type.js | 2 +- src/codec.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/samples/pg-numeric-data-type.js b/samples/pg-numeric-data-type.js index e2f458346..61eaaa09a 100644 --- a/samples/pg-numeric-data-type.js +++ b/samples/pg-numeric-data-type.js @@ -61,7 +61,7 @@ function main( // Instantiate Spanner table objects. const venuesTable = database.table('venues'); const revenues1 = Spanner.pgNumeric('97372.3863'); - const revenues2 = Spanner.pgNumeric('7629'); + const revenues2 = Spanner.pgNumeric(7629); const revenues3 = Spanner.pgNumeric( '1234567890.000012387293137871837817783828271273962412698378219372373072321997201370913293722379069869126846496978479842178917827474178248943891738912692839263826722738362982366832623281' ); diff --git a/src/codec.ts b/src/codec.ts index 4a4755201..034c89e10 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -18,9 +18,10 @@ import {PreciseDate} from '@google-cloud/precise-date'; import arrify = require('arrify'); import {Big} from 'big.js'; import * as is from 'is'; -import {common as p} from 'protobufjs'; +import {common as p, util} from 'protobufjs'; import {google as spannerClient} from '../protos/protos'; import {GoogleError} from 'google-gax'; +import float = util.float; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Value = any; @@ -222,8 +223,8 @@ export class Numeric { */ export class PGNumeric { value: string; - constructor(pgValue: string) { - this.value = pgValue; + constructor(pgValue: string | number) { + this.value = pgValue.toString(); } valueOf(): Big { if (this.value.toLowerCase() === 'nan') { From 2466f3f77347c7189bf961f55a64de92e811c8c0 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Thu, 24 Mar 2022 11:38:31 +0000 Subject: [PATCH 09/15] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20?= =?UTF-8?q?post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- README.md | 12 ++ samples/README.md | 216 ++++++++++++++++++++++++++++++++ samples/pg-case-sensitivity.js | 4 +- samples/pg-datatypes-casting.js | 2 +- 4 files changed, 231 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4b0cdb25f..57b8560d8 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,18 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-spanner/tre | Numeric-add-column | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/numeric-add-column.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-add-column.js,samples/README.md) | | Numeric-query-parameter | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/numeric-query-parameter.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-query-parameter.js,samples/README.md) | | Numeric-update-data | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/numeric-update-data.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/numeric-update-data.js,samples/README.md) | +| Showcase the rules for case-sensitivity and case folding for a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-case-sensitivity.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-case-sensitivity.js,samples/README.md) | +| Creates a PostgreSQL Database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-database-create.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-database-create.js,samples/README.md) | +| Use cast operator to cast from one data type to another in a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-datatypes-casting.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-datatypes-casting.js,samples/README.md) | +| Execute a batch of DML statements on a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-dml-batch.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-dml-batch.js,samples/README.md) | +| Execute a Partitioned DML on a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-dml-partitioned.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-dml-partitioned.js,samples/README.md) | +| Execute a DML statement with parameters on a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-dml-with-parameter.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-dml-with-parameter.js,samples/README.md) | +| Calls a server side function on a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-functions.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-functions.js,samples/README.md) | +| Created interleaved table hierarchy using PostgreSQL dialect. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-interleaving.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-interleaving.js,samples/README.md) | +| Showcase how to work with the PostgreSQL NUMERIC/DECIMAL data type on a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-numeric-data-type.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-numeric-data-type.js,samples/README.md) | +| Showcases how a Spanner PostgreSQL database orders null values in a query. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-ordering-nulls.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-ordering-nulls.js,samples/README.md) | +| Execute a query with parameters on a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-query-parameter.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-query-parameter.js,samples/README.md) | +| Query the information schema metadata in a Spanner PostgreSQL database. | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-schema-information.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-schema-information.js,samples/README.md) | | Queryoptions | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/queryoptions.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/queryoptions.js,samples/README.md) | | Quickstart | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/quickstart.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/quickstart.js,samples/README.md) | | Sets a request tag for a single query | [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/request-tag.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/request-tag.js,samples/README.md) | diff --git a/samples/README.md b/samples/README.md index 8e7095045..534c0a0f3 100644 --- a/samples/README.md +++ b/samples/README.md @@ -54,6 +54,18 @@ and automatic, synchronous replication for high availability. * [Numeric-add-column](#numeric-add-column) * [Numeric-query-parameter](#numeric-query-parameter) * [Numeric-update-data](#numeric-update-data) + * [Showcase the rules for case-sensitivity and case folding for a Spanner PostgreSQL database.](#showcase-the-rules-for-case-sensitivity-and-case-folding-for-a-spanner-postgresql-database.) + * [Creates a PostgreSQL Database.](#creates-a-postgresql-database.) + * [Use cast operator to cast from one data type to another in a Spanner PostgreSQL database.](#use-cast-operator-to-cast-from-one-data-type-to-another-in-a-spanner-postgresql-database.) + * [Execute a batch of DML statements on a Spanner PostgreSQL database.](#execute-a-batch-of-dml-statements-on-a-spanner-postgresql-database.) + * [Execute a Partitioned DML on a Spanner PostgreSQL database.](#execute-a-partitioned-dml-on-a-spanner-postgresql-database.) + * [Execute a DML statement with parameters on a Spanner PostgreSQL database.](#execute-a-dml-statement-with-parameters-on-a-spanner-postgresql-database.) + * [Calls a server side function on a Spanner PostgreSQL database.](#calls-a-server-side-function-on-a-spanner-postgresql-database.) + * [Created interleaved table hierarchy using PostgreSQL dialect.](#created-interleaved-table-hierarchy-using-postgresql-dialect.) + * [Showcase how to work with the PostgreSQL NUMERIC/DECIMAL data type on a Spanner PostgreSQL database.](#showcase-how-to-work-with-the-postgresql-numeric/decimal-data-type-on-a-spanner-postgresql-database.) + * [Showcases how a Spanner PostgreSQL database orders null values in a query.](#showcases-how-a-spanner-postgresql-database-orders-null-values-in-a-query.) + * [Execute a query with parameters on a Spanner PostgreSQL database.](#execute-a-query-with-parameters-on-a-spanner-postgresql-database.) + * [Query the information schema metadata in a Spanner PostgreSQL database.](#query-the-information-schema-metadata-in-a-spanner-postgresql-database.) * [Queryoptions](#queryoptions) * [Quickstart](#quickstart) * [Sets a request tag for a single query](#sets-a-request-tag-for-a-single-query) @@ -759,6 +771,210 @@ __Usage:__ +### Showcase the rules for case-sensitivity and case folding for a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-case-sensitivity.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-case-sensitivity.js,samples/README.md) + +__Usage:__ + + +`node pg-case-sensitivity.js ` + + +----- + + + + +### Creates a PostgreSQL Database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-database-create.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-database-create.js,samples/README.md) + +__Usage:__ + + +`node pg-database-create.js ` + + +----- + + + + +### Use cast operator to cast from one data type to another in a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-datatypes-casting.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-datatypes-casting.js,samples/README.md) + +__Usage:__ + + +`node pg-datatypes-casting.js ` + + +----- + + + + +### Execute a batch of DML statements on a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-dml-batch.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-dml-batch.js,samples/README.md) + +__Usage:__ + + +`node pg-dml-batch.js ` + + +----- + + + + +### Execute a Partitioned DML on a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-dml-partitioned.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-dml-partitioned.js,samples/README.md) + +__Usage:__ + + +`node pg-dml-partitioned.js ` + + +----- + + + + +### Execute a DML statement with parameters on a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-dml-with-parameter.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-dml-with-parameter.js,samples/README.md) + +__Usage:__ + + +`node pg-dml-with-parameter.js ` + + +----- + + + + +### Calls a server side function on a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-functions.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-functions.js,samples/README.md) + +__Usage:__ + + +`node pg-functions.js ` + + +----- + + + + +### Created interleaved table hierarchy using PostgreSQL dialect. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-interleaving.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-interleaving.js,samples/README.md) + +__Usage:__ + + +`node pg-interleaving.js ` + + +----- + + + + +### Showcase how to work with the PostgreSQL NUMERIC/DECIMAL data type on a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-numeric-data-type.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-numeric-data-type.js,samples/README.md) + +__Usage:__ + + +`node ppg-numeric-data-type.js ` + + +----- + + + + +### Showcases how a Spanner PostgreSQL database orders null values in a query. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-ordering-nulls.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-ordering-nulls.js,samples/README.md) + +__Usage:__ + + +`node pg-ordering-nulls.js ` + + +----- + + + + +### Execute a query with parameters on a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-query-parameter.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-query-parameter.js,samples/README.md) + +__Usage:__ + + +`node pg-query-parameter.js ` + + +----- + + + + +### Query the information schema metadata in a Spanner PostgreSQL database. + +View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-schema-information.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-spanner&page=editor&open_in_editor=samples/pg-schema-information.js,samples/README.md) + +__Usage:__ + + +`node pg-schema-information.js ` + + +----- + + + + ### Queryoptions View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/queryoptions.js). diff --git a/samples/pg-case-sensitivity.js b/samples/pg-case-sensitivity.js index 0d0eae20f..8c2376b93 100644 --- a/samples/pg-case-sensitivity.js +++ b/samples/pg-case-sensitivity.js @@ -90,7 +90,7 @@ function main( sql: 'SELECT * FROM Concerts', }); - console.log(`Concerts Table Data using Mutations:`); + console.log('Concerts Table Data using Mutations:'); rows.forEach(row => { const json = row.toJSON(); // Queries: ConcertId is automatically folded to lower case. Accessing the column by its name in @@ -113,7 +113,7 @@ function main( '"Location" AS "venue", "Time" FROM Concerts', }); - console.log(`Concerts Table Data using Aliases:`); + console.log('Concerts Table Data using Aliases:'); rows.forEach(row => { const json = row.toJSON(); // The aliases are double-quoted and therefore retains their mixed case. diff --git a/samples/pg-datatypes-casting.js b/samples/pg-datatypes-casting.js index 59fcc35b0..70c5d0900 100644 --- a/samples/pg-datatypes-casting.js +++ b/samples/pg-datatypes-casting.js @@ -57,7 +57,7 @@ function main( rows.forEach(row => { const json = row.toJSON(); console.log( - `Data types after casting \n` + + 'Data types after casting \n' + `String: ${json.str} \n` + `Int: ${json.int} \n` + `Decimal: ${json.dec.value} \n` + From 805ac10536291615051651570e4e16bbf8da1294 Mon Sep 17 00:00:00 2001 From: Astha Mohta <35952883+asthamohta@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:41:01 +0530 Subject: [PATCH 10/15] Update samples/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Knut Olav Løite --- samples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index 534c0a0f3..d68e2660d 100644 --- a/samples/README.md +++ b/samples/README.md @@ -890,7 +890,7 @@ __Usage:__ -### Created interleaved table hierarchy using PostgreSQL dialect. +### Create interleaved table hierarchy using PostgreSQL dialect. View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-interleaving.js). From 467382a4cef4e9f067d7df3485ac675af8dc58d6 Mon Sep 17 00:00:00 2001 From: Astha Mohta <35952883+asthamohta@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:41:05 +0530 Subject: [PATCH 11/15] Update samples/pg-database-create.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Knut Olav Løite --- samples/pg-database-create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/pg-database-create.js b/samples/pg-database-create.js index 6fa389c9b..c39e930c9 100644 --- a/samples/pg-database-create.js +++ b/samples/pg-database-create.js @@ -62,7 +62,7 @@ function main( `Created database ${databaseId} on instance ${instanceId} with dialect ${database.metadata.databaseDialect}.` ); - // Create a couple of tables using a separate request. We can use PostgreSQL style DDL as the + // Create a couple of tables using a separate request. We must use PostgreSQL style DDL as the // database has been created with the PostgreSQL dialect. const statements = [ `CREATE TABLE Singers From 8db1de5b68969c9498375b035a6c5671caa4e9d2 Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 25 Mar 2022 07:34:03 +0000 Subject: [PATCH 12/15] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20?= =?UTF-8?q?post-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- samples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/README.md b/samples/README.md index d68e2660d..534c0a0f3 100644 --- a/samples/README.md +++ b/samples/README.md @@ -890,7 +890,7 @@ __Usage:__ -### Create interleaved table hierarchy using PostgreSQL dialect. +### Created interleaved table hierarchy using PostgreSQL dialect. View the [source code](https://github.com/googleapis/nodejs-spanner/blob/main/samples/pg-interleaving.js). From 7eb8006a6075eabaa437a4a84a6ef5c4af28fecd Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Mon, 28 Mar 2022 12:25:20 +0530 Subject: [PATCH 13/15] fix: removing table_catalog --- samples/pg-schema-information.js | 4 ++-- samples/system-test/spanner.test.js | 17 ++++------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/samples/pg-schema-information.js b/samples/pg-schema-information.js index 99dbd8235..954b5153f 100644 --- a/samples/pg-schema-information.js +++ b/samples/pg-schema-information.js @@ -49,7 +49,7 @@ function main( // are only available for PostgreSQL databases. const query = { sql: - 'SELECT table_catalog, table_schema, table_name, ' + + 'SELECT table_schema, table_name, ' + 'user_defined_type_catalog, ' + 'user_defined_type_schema, ' + 'user_defined_type_name ' + @@ -62,7 +62,7 @@ function main( rows.forEach(row => { const json = row.toJSON(); console.log( - `Table: ${json.table_catalog}.${json.table_schema}.${json.table_name} ` + + `Table: ${json.table_schema}.${json.table_name} ` + `(User defined type: ${json.user_defined_type_catalog}.${json.user_defined_type_schema}.${json.user_defined_type_name})` ); }); diff --git a/samples/system-test/spanner.test.js b/samples/system-test/spanner.test.js index 1cca65c23..540651e0f 100644 --- a/samples/system-test/spanner.test.js +++ b/samples/system-test/spanner.test.js @@ -1420,19 +1420,10 @@ describe('Spanner', () => { const output = execSync( `node pg-schema-information.js ${SAMPLE_INSTANCE_ID} ${PG_DATABASE_ID} ${PROJECT_ID}` ); - assert.match( - output, - new RegExp(`Table: ${PG_DATABASE_ID}.public.albums`) - ); - assert.match( - output, - new RegExp(`Table: ${PG_DATABASE_ID}.public.author`) - ); - assert.match(output, new RegExp(`Table: ${PG_DATABASE_ID}.public.book`)); - assert.match( - output, - new RegExp(`Table: ${PG_DATABASE_ID}.public.singers`) - ); + assert.match(output, new RegExp('Table: public.albums')); + assert.match(output, new RegExp('Table: public.author')); + assert.match(output, new RegExp('Table: public.book')); + assert.match(output, new RegExp('Table: public.singers')); }); // pg_ordering_nulls From 4f971844280698af02cef9652cec54e751c2ab1f Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Tue, 29 Mar 2022 11:23:53 +0530 Subject: [PATCH 14/15] removing float --- src/codec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/codec.ts b/src/codec.ts index 034c89e10..a92ff38cd 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -21,7 +21,6 @@ import * as is from 'is'; import {common as p, util} from 'protobufjs'; import {google as spannerClient} from '../protos/protos'; import {GoogleError} from 'google-gax'; -import float = util.float; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Value = any; From dc060b41412448f88868b442e3ced7514a045adb Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Tue, 29 Mar 2022 11:29:09 +0530 Subject: [PATCH 15/15] removing util --- src/codec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codec.ts b/src/codec.ts index a92ff38cd..58c1a56ae 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -18,7 +18,7 @@ import {PreciseDate} from '@google-cloud/precise-date'; import arrify = require('arrify'); import {Big} from 'big.js'; import * as is from 'is'; -import {common as p, util} from 'protobufjs'; +import {common as p} from 'protobufjs'; import {google as spannerClient} from '../protos/protos'; import {GoogleError} from 'google-gax';