diff --git a/spanner/database-create-with-default-leader.js b/spanner/database-create-with-default-leader.js new file mode 100644 index 0000000000..8f70e16b64 --- /dev/null +++ b/spanner/database-create-with-default-leader.js @@ -0,0 +1,97 @@ +/** + * Copyright 2024 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 new database with a specific default leader +// usage: node database-create-with-default-leader.js + +'use strict'; + +function main(instanceId, databaseId, defaultLeader, projectId) { + // [START spanner_create_database_with_default_leader] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + // const defaultLeader = 'my-default-leader'; example: 'asia-northeast1' + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + // Gets a reference to a Cloud Spanner Database Admin Client object + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function createDatabaseWithDefaultLeader() { + // Create a new database with an extra statement which will alter the + // database after creation to set the default leader. + console.log( + `Creating database ${databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + )}.` + ); + const createSingersTableStatement = ` + CREATE TABLE Singers ( + SingerId INT64 NOT NULL, + FirstName STRING(1024), + LastName STRING(1024), + SingerInfo BYTES(MAX) + ) PRIMARY KEY (SingerId)`; + const createAlbumsStatement = ` + CREATE TABLE Albums ( + SingerId INT64 NOT NULL, + AlbumId INT64 NOT NULL, + AlbumTitle STRING(MAX) + ) PRIMARY KEY (SingerId, AlbumId), + INTERLEAVE IN PARENT Singers ON DELETE CASCADE`; + + // Default leader is one of the possible values in the leaderOptions field of the + // instance config of the instance where the database is created. + const setDefaultLeaderStatement = ` + ALTER DATABASE \`${databaseId}\` + SET OPTIONS (default_leader = '${defaultLeader}')`; + + const [operation] = await databaseAdminClient.createDatabase({ + createStatement: 'CREATE DATABASE `' + databaseId + '`', + extraStatements: [ + createSingersTableStatement, + createAlbumsStatement, + setDefaultLeaderStatement, + ], + parent: databaseAdminClient.instancePath(projectId, instanceId), + }); + + console.log(`Waiting for creation of ${databaseId} to complete...`); + await operation.promise(); + console.log( + `Created database ${databaseId} with default leader ${defaultLeader}.` + ); + } + createDatabaseWithDefaultLeader(); + // [END spanner_create_database_with_default_leader] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/database-get-ddl.js b/spanner/database-get-ddl.js new file mode 100644 index 0000000000..2b2bd96083 --- /dev/null +++ b/spanner/database-get-ddl.js @@ -0,0 +1,69 @@ +/** + * Copyright 2024 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: Gets the schema definition of an existing database +// usage: node database-get-ddl.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_get_database_ddl] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function getDatabaseDdl() { + // Get the schema definition of the database. + const [ddlStatements] = await databaseAdminClient.getDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + }); + + console.log( + `Retrieved database DDL for ${databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + )}:` + ); + ddlStatements.statements.forEach(element => { + console.log(element); + }); + } + getDatabaseDdl(); + // [END spanner_get_database_ddl] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/database-get-default-leader.js b/spanner/database-get-default-leader.js new file mode 100644 index 0000000000..fe11365a98 --- /dev/null +++ b/spanner/database-get-default-leader.js @@ -0,0 +1,69 @@ +/** + * Copyright 2021 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: Gets the default leader option of an existing database +// usage: node database-get-default-leader.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_query_information_schema_database_options] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + // Gets a reference to a Cloud Spanner instance and a database. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + async function getDatabaseDdl() { + // Get the default leader option for the database. + const [rows] = await database.run({ + sql: ` + SELECT s.OPTION_NAME, s.OPTION_VALUE + FROM INFORMATION_SCHEMA.DATABASE_OPTIONS s + WHERE s.OPTION_NAME = 'default_leader'`, + json: true, + }); + if (rows.length > 0) { + const option = rows[0]; + console.log( + `The ${option.OPTION_NAME} for ${databaseId} is ${option.OPTION_VALUE}` + ); + } else { + console.log( + `Database ${databaseId} does not have a value for option 'default_leader'` + ); + } + } + getDatabaseDdl(); + // [END spanner_query_information_schema_database_options] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/database-update-default-leader.js b/spanner/database-update-default-leader.js new file mode 100644 index 0000000000..7a5c928a0a --- /dev/null +++ b/spanner/database-update-default-leader.js @@ -0,0 +1,75 @@ +/** + * Copyright 2024 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: Updates the default leader of an existing database +// usage: node database-update-default-leader.js + +'use strict'; + +function main(instanceId, databaseId, defaultLeader, projectId) { + // [START spanner_update_database_with_default_leader] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + // const defaultLeader = 'my-default-leader'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function updateDatabaseWithDefaultLeader() { + console.log( + `Updating database ${databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + )}.` + ); + const setDefaultLeaderStatement = ` + ALTER DATABASE \`${databaseId}\` + SET OPTIONS (default_leader = '${defaultLeader}')`; + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: [setDefaultLeaderStatement], + }); + + console.log(`Waiting for updating of ${databaseId} to complete...`); + await operation.promise(); + console.log( + `Updated database ${databaseId} with default leader ${defaultLeader}.` + ); + } + updateDatabaseWithDefaultLeader(); + // [END spanner_update_database_with_default_leader] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/get-instance-config.js b/spanner/get-instance-config.js new file mode 100644 index 0000000000..01a4d49521 --- /dev/null +++ b/spanner/get-instance-config.js @@ -0,0 +1,61 @@ +/** + * Copyright 2024 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: Gets the instance config metadata for the configuration nam6 +// usage: node get-instance-config.js + +'use strict'; + +function main(projectId) { + // [START spanner_get_instance_config] + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + async function getInstanceConfig() { + // Get the instance config for the multi-region North America 6 (NAM6). + // See https://cloud.google.com/spanner/docs/instance-configurations#configuration for a list of all available + // configurations. + const [instanceConfig] = await instanceAdminClient.getInstanceConfig({ + name: instanceAdminClient.instanceConfigPath(projectId, 'nam6'), + }); + console.log( + `Available leader options for instance config ${instanceConfig.name} ('${ + instanceConfig.displayName + }'): + ${instanceConfig.leaderOptions.join()}` + ); + } + getInstanceConfig(); + // [END spanner_get_instance_config] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/instance-config-create.js b/spanner/instance-config-create.js new file mode 100644 index 0000000000..95686b0d76 --- /dev/null +++ b/spanner/instance-config-create.js @@ -0,0 +1,101 @@ +/** + * Copyright 2024 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 user-managed instance configuration. +// usage: node instance-config-create + +'use strict'; + +function main( + instanceConfigId = 'custom-my-instance-config', + baseInstanceConfigId = 'my-base-instance-config', + projectId = 'my-project-id' +) { + // [START spanner_create_instance_config] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const instanceConfigId = 'custom-my-instance-config-id' + // const baseInstanceConfigId = 'my-base-instance-config-id'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + // Creates a new instance config + async function createInstanceConfig() { + const [baseInstanceConfig] = await instanceAdminClient.getInstanceConfig({ + name: instanceAdminClient.instanceConfigPath( + projectId, + baseInstanceConfigId + ), + }); + try { + console.log( + `Creating instance config ${instanceAdminClient.instanceConfigPath( + projectId, + instanceConfigId + )}.` + ); + const [operation] = await instanceAdminClient.createInstanceConfig({ + instanceConfigId: instanceConfigId, + parent: instanceAdminClient.projectPath(projectId), + instanceConfig: { + name: instanceAdminClient.instanceConfigPath( + projectId, + instanceConfigId + ), + baseConfig: instanceAdminClient.instanceConfigPath( + projectId, + baseInstanceConfigId + ), + displayName: instanceConfigId, + replicas: baseInstanceConfig.replicas.concat( + baseInstanceConfig.optionalReplicas[0] + ), + }, + }); + console.log( + `Waiting for create operation for ${instanceConfigId} to complete...` + ); + await operation.promise(); + console.log(`Created instance config ${instanceConfigId}.`); + } catch (err) { + console.error( + 'ERROR: Creating instance config ', + instanceConfigId, + ' failed with error message ', + err + ); + } + } + createInstanceConfig(); + // [END spanner_create_instance_config] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/instance-config-delete.js b/spanner/instance-config-delete.js new file mode 100644 index 0000000000..b5de388b09 --- /dev/null +++ b/spanner/instance-config-delete.js @@ -0,0 +1,73 @@ +/** + * Copyright 2024 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: Deletes a user-managed instance configuration. +// usage: node instance-config-delete + +'use strict'; + +function main( + instanceConfigId = 'custom-my-instance-config', + projectId = 'my-project-id' +) { + // [START spanner_delete_instance_config] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const instanceConfigId = 'custom-my-instance-config-id'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + async function deleteInstanceConfig() { + // Deletes an instance config. + + try { + // Delete the instance config. + console.log(`Deleting ${instanceConfigId}...\n`); + await instanceAdminClient.deleteInstanceConfig({ + name: instanceAdminClient.instanceConfigPath( + projectId, + instanceConfigId + ), + }); + console.log(`Deleted instance config ${instanceConfigId}.\n`); + } catch (err) { + console.error( + 'ERROR: Deleting instance config ', + instanceConfigId, + ' failed with error message ', + err + ); + } + } + deleteInstanceConfig(); + // [END spanner_delete_instance_config] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/instance-config-get-operations.js b/spanner/instance-config-get-operations.js new file mode 100644 index 0000000000..4737ff40e7 --- /dev/null +++ b/spanner/instance-config-get-operations.js @@ -0,0 +1,81 @@ +/** + * Copyright 2024 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: Lists the instance configuration operations. +// usage: node instance-config-get-operations + +'use strict'; + +function main(projectId = 'my-project-id') { + // [START spanner_list_instance_config_operations] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + async function getInstanceConfigOperations() { + // Lists the instance config operations. + try { + console.log( + `Getting list of instance config operations on project ${projectId}...\n` + ); + const [instanceConfigOperations] = + await instanceAdminClient.listInstanceConfigOperations({ + parent: instanceAdminClient.projectPath(projectId), + // This filter ensures that only operations with metadata type CreateInstanceConfigMetadata + filter: + '(metadata.@type=type.googleapis.com/google.spanner.admin.instance.v1.CreateInstanceConfigMetadata)', + }); + console.log( + `Available instance config operations for project ${projectId}:` + ); + instanceConfigOperations.forEach(instanceConfigOperation => { + const metadata = instanceConfigOperation.metadata; + const instanceConfig = + protos.google.spanner.admin.instance.v1.CreateInstanceConfigMetadata.decode( + instanceConfigOperation.metadata.value + ).instanceConfig; + console.log( + `Instance config operation for ${instanceConfig.name} of type` + + ` ${metadata.type_url} has status ${ + instanceConfigOperation.done ? 'done' : 'running' + }.` + ); + }); + } catch (err) { + console.error('ERROR:', err); + } + } + getInstanceConfigOperations(); + // [END spanner_list_instance_config_operations] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/instance-config-update.js b/spanner/instance-config-update.js new file mode 100644 index 0000000000..59b8bae46e --- /dev/null +++ b/spanner/instance-config-update.js @@ -0,0 +1,93 @@ +/** + * Copyright 2024 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: Updates a user-managed instance configuration. +// usage: node instance-config-update + +'use strict'; + +function main( + instanceConfigId = 'custom-my-instance-config', + projectId = 'my-project-id' +) { + // [START spanner_update_instance_config] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const instanceConfigId = 'custom-my-instance-config-id'; + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner, protos} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + async function updateInstanceConfig() { + // Updates an instance config + try { + console.log( + `Updating instance config ${instanceAdminClient.instanceConfigPath( + projectId, + instanceConfigId + )}.` + ); + const [operation] = await instanceAdminClient.updateInstanceConfig({ + instanceConfig: { + name: instanceAdminClient.instanceConfigPath( + projectId, + instanceConfigId + ), + displayName: 'updated custom instance config', + labels: { + updated: 'true', + created: Math.round(Date.now() / 1000).toString(), // current time + }, + }, + // Field mask specifying fields that should get updated in InstanceConfig + // Only display_name and labels can be updated + updateMask: (protos.google.protobuf.FieldMask = { + paths: ['display_name', 'labels'], + }), + }); + console.log( + `Waiting for update operation for ${instanceConfigId} to complete...` + ); + await operation.promise(); + console.log(`Updated instance config ${instanceConfigId}.`); + } catch (err) { + console.error( + 'ERROR: Updating instance config ', + instanceConfigId, + ' failed with error message ', + err + ); + } + } + updateInstanceConfig(); + // [END spanner_update_instance_config] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/instance-partition-create.js b/spanner/instance-partition-create.js new file mode 100644 index 0000000000..b7536fe93c --- /dev/null +++ b/spanner/instance-partition-create.js @@ -0,0 +1,84 @@ +/** + * Copyright 2024 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 new instance partition +// usage: node instance-partition-create.js + +'use strict'; + +function main( + instanceId = 'my-instance', + instancePartitionId = 'my-instance-partition', + projectId = 'my-project-id' +) { + async function createInstancePartition() { + // [START spanner_create_instance_partition] + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const instancePartitionId = 'my-instance-partition'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + // Get the instance admin client + const instanceAdminClient = spanner.getInstanceAdminClient(); + + // Creates a new instance partition + try { + console.log( + `Creating instance partition ${instanceAdminClient.instancePartitionPath( + projectId, + instanceId, + instancePartitionId + )}.` + ); + const [operation] = await instanceAdminClient.createInstancePartition({ + instancePartitionId: instancePartitionId, + parent: instanceAdminClient.instancePath(projectId, instanceId), + instancePartition: { + config: instanceAdminClient.instanceConfigPath(projectId, 'nam3'), + nodeCount: 1, + displayName: 'Test instance partition', + }, + }); + + console.log( + `Waiting for operation on ${instancePartitionId} to complete...` + ); + await operation.promise(); + + console.log(`Created instance partition ${instancePartitionId}.`); + } catch (err) { + console.error('ERROR:', err); + } + // [END spanner_create_instance_partition] + } + createInstancePartition(); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/list-databases.js b/spanner/list-databases.js new file mode 100644 index 0000000000..e24dbae6fa --- /dev/null +++ b/spanner/list-databases.js @@ -0,0 +1,60 @@ +/** + * Copyright 2024 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: Lists all databases on the selected instance +// usage: node list-databases.js + +'use strict'; + +function main(instanceId, projectId) { + // [START spanner_list_databases] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function listDatabases() { + // Lists all databases on the instance. + const [databases] = await databaseAdminClient.listDatabases({ + parent: databaseAdminClient.instancePath(projectId, instanceId), + }); + console.log(`Databases for projects/${projectId}/instances/${instanceId}:`); + databases.forEach(database => { + const defaultLeader = database.defaultLeader + ? `(default leader = ${database.defaultLeader})` + : ''; + console.log(`\t${database.name} ${defaultLeader}`); + }); + } + listDatabases(); + // [END spanner_list_databases] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/list-instance-configs.js b/spanner/list-instance-configs.js new file mode 100644 index 0000000000..27017f38eb --- /dev/null +++ b/spanner/list-instance-configs.js @@ -0,0 +1,63 @@ +/** + * Copyright 2024 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: Lists all the available instance configs for the selected project. +// usage: node list-instance-configs.js + +'use strict'; + +function main(projectId) { + // [START spanner_list_instance_configs] + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'my-project-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const instanceAdminClient = spanner.getInstanceAdminClient(); + + async function listInstanceConfigs() { + // Lists all available instance configurations in the project. + // See https://cloud.google.com/spanner/docs/instance-configurations#configuration for a list of all available + // configurations. + const [instanceConfigs] = await instanceAdminClient.listInstanceConfigs({ + parent: instanceAdminClient.projectPath(projectId), + }); + console.log(`Available instance configs for project ${projectId}:`); + instanceConfigs.forEach(instanceConfig => { + console.log( + `Available leader options for instance config ${ + instanceConfig.name + } ('${instanceConfig.displayName}'): + ${instanceConfig.leaderOptions.join()}` + ); + }); + } + listInstanceConfigs(); + // [END spanner_list_instance_configs] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/max-commit-delay.js b/spanner/max-commit-delay.js new file mode 100644 index 0000000000..c3b13b1efa --- /dev/null +++ b/spanner/max-commit-delay.js @@ -0,0 +1,83 @@ +// Copyright 2024 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: Executes request with max commit delay +// usage: node max-commit-delay.js + +'use strict'; + +function main( + instanceId = 'my-instance', + databaseId = 'my-database', + projectId = 'my-project-id' +) { + // [START spanner_set_max_commit_delay] + const {Spanner, protos} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Creates a client. + const spanner = new Spanner({ + projectId: projectId, + }); + + async function setMaxCommitDelay() { + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + database.runTransaction(async (err, transaction) => { + if (err) { + console.error(err); + return; + } + try { + const [rowCount] = await transaction.runUpdate({ + sql: 'INSERT Singers (SingerId, FirstName, LastName) VALUES (111, @firstName, @lastName)', + params: { + firstName: 'Virginia', + lastName: 'Watson', + }, + }); + + console.log( + `Successfully inserted ${rowCount} record into the Singers table.` + ); + + await transaction.commit({ + maxCommitDelay: protos.google.protobuf.Duration({ + seconds: 0, // 0 seconds + nanos: 100000000, // 100 milliseconds + }), + }); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + database.close(); + } + }); + } + setMaxCommitDelay(); + // [END spanner_set_max_commit_delay] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/observability-traces.js b/spanner/observability-traces.js new file mode 100644 index 0000000000..766c041adc --- /dev/null +++ b/spanner/observability-traces.js @@ -0,0 +1,99 @@ +/*! + * Copyright 2025 Google LLC. All Rights Reserved. + * + * 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: Observability (Tracing) with OpenTelemetry +// usage: node observability-traces.js + +'use strict'; + +async function main( + projectId = 'my-project-id', + instanceId = 'my-instance-id', + databaseId = 'my-project-id' +) { + // [START spanner_opentelemetry_traces_cloudtrace_usage] + + const {NodeTracerProvider} = require('@opentelemetry/sdk-trace-node'); + const { + TraceExporter, + } = require('@google-cloud/opentelemetry-cloud-trace-exporter'); + const { + BatchSpanProcessor, + TraceIdRatioBasedSampler, + } = require('@opentelemetry/sdk-trace-base'); + const {Spanner} = require('@google-cloud/spanner'); + + const traceExporter = new TraceExporter({projectId: projectId}); + + // Create a provider with a custom sampler + const provider = new NodeTracerProvider({ + sampler: new TraceIdRatioBasedSampler(1.0), // Sample 100% of traces + spanProcessors: [new BatchSpanProcessor(traceExporter)], + }); + + // Uncomment following line to register tracerProvider globally or pass it in Spanner object + // provider.register(); + + // Set global propagator to propogate the trace context for end to end tracing. + const {propagation} = require('@opentelemetry/api'); + const {W3CTraceContextPropagator} = require('@opentelemetry/core'); + propagation.setGlobalPropagator(new W3CTraceContextPropagator()); + + const spanner = new Spanner({ + projectId: projectId, + observabilityOptions: { + tracerProvider: provider, + // Enable extended tracing to allow your SQL statements to be annotated. + enableExtendedTracing: true, + // Enable end to end tracing. + enableEndToEndTracing: true, + }, + }); + + // [END spanner_opentelemetry_traces_cloudtrace_usage] + + // Acquire the database handle. + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + try { + const query = { + sql: 'SELECT 1', + }; + const [rows] = await database.run(query); + console.log(`Query: ${rows.length} found.`); + rows.forEach(row => console.log(row)); + } finally { + spanner.close(); + } + + await provider.forceFlush(); + + // This sleep gives ample time for the trace + // spans to be exported to Google Cloud Trace. + await new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 8800); + }); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/quickstart.js b/spanner/quickstart.js new file mode 100644 index 0000000000..5bf30f81ed --- /dev/null +++ b/spanner/quickstart.js @@ -0,0 +1,46 @@ +// Copyright 2016 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. + +'use strict'; + +async function quickstart( + projectId = 'YOUR-PROJECT-ID', // Your Google Cloud Platform project ID + instanceId = 'my-instance', // Your Cloud Spanner instance ID + databaseId = 'my-database' // Your Cloud Spanner database ID +) { + // [START spanner_quickstart] + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // Creates a client + const spanner = new Spanner({projectId}); + + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + // The query to execute + const query = { + sql: 'SELECT 1', + }; + + // Execute a simple SQL statement + const [rows] = await database.run(query); + console.log(`Query: ${rows.length} found.`); + rows.forEach(row => console.log(row)); + // [END spanner_quickstart] +} + +const args = process.argv.slice(2); +quickstart(...args).catch(console.error); diff --git a/spanner/sequence-alter.js b/spanner/sequence-alter.js new file mode 100644 index 0000000000..b2dde1ea61 --- /dev/null +++ b/spanner/sequence-alter.js @@ -0,0 +1,109 @@ +// Copyright 2024 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: Alters a sequence in a GoogleSQL database. +// usage: node sequence-alter.js + +'use strict'; + +async function main(instanceId, databaseId, projectId) { + // [START spanner_alter_sequence] + + // Imports the Google Cloud client library. + const {Spanner} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function alterSequence(instanceId, databaseId) { + // Gets a reference to a Cloud Spanner Database Admin Client object + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + const request = [ + 'ALTER SEQUENCE Seq SET OPTIONS (skip_range_min = 1000, skip_range_max = 5000000)', + ]; + + try { + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: request, + }); + + console.log('Waiting for operation to complete...'); + await operation.promise(); + + console.log( + 'Altered Seq sequence to skip an inclusive range between 1000 and 5000000.' + ); + } catch (err) { + console.error('ERROR:', err); + } + + // 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 [rows, stats] = await transaction.run({ + sql: "INSERT INTO Customers (CustomerName) VALUES ('Lea'), ('Catalina'), ('Smith') THEN RETURN CustomerId", + }); + + rows.forEach(row => { + console.log( + `Inserted customer record with CustomerId: ${ + row.toJSON({wrapNumbers: true}).CustomerId.value + }` + ); + }); + + const rowCount = Math.floor(stats[stats.rowCount]); + console.log(`Number of customer records inserted is: ${rowCount}`); + + await transaction.commit(); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + await database.close(); + } + }); + } + await alterSequence(instanceId, databaseId); + // [END spanner_alter_sequence] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/sequence-create.js b/spanner/sequence-create.js new file mode 100644 index 0000000000..dd4c11424a --- /dev/null +++ b/spanner/sequence-create.js @@ -0,0 +1,110 @@ +// Copyright 2024 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 sequence in GoogleSQL database. +// usage: node sequence-create.js + +'use strict'; + +async function main(instanceId, databaseId, projectId) { + // [START spanner_create_sequence] + // Imports the Google Cloud client library. + const {Spanner} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function createSequence(instanceId, databaseId) { + // Gets a reference to a Cloud Spanner Database Admin Client object + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + const request = [ + "CREATE SEQUENCE Seq OPTIONS (sequence_kind = 'bit_reversed_positive')", + 'CREATE TABLE Customers (CustomerId INT64 DEFAULT (GET_NEXT_SEQUENCE_VALUE(Sequence Seq)), CustomerName STRING(1024)) PRIMARY KEY (CustomerId)', + ]; + + // Creates a new table with sequence + try { + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: request, + }); + + console.log('Waiting for operation to complete...'); + await operation.promise(); + + console.log( + 'Created Seq sequence and Customers table, where the key column CustomerId uses the sequence as a default value.' + ); + } catch (err) { + console.error('ERROR:', err); + } + + // 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 [rows, stats] = await transaction.run({ + sql: "INSERT INTO Customers (CustomerName) VALUES ('Alice'), ('David'), ('Marc') THEN RETURN CustomerId", + }); + + rows.forEach(row => { + console.log( + `Inserted customer record with CustomerId: ${ + row.toJSON({wrapNumbers: true}).CustomerId.value + }` + ); + }); + + const rowCount = Math.floor(stats[stats.rowCount]); + console.log(`Number of customer records inserted is: ${rowCount}`); + + await transaction.commit(); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the database when finished. + await database.close(); + } + }); + } + await createSequence(instanceId, databaseId); + // [END spanner_create_sequence] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/sequence-drop.js b/spanner/sequence-drop.js new file mode 100644 index 0000000000..4693cfd5f7 --- /dev/null +++ b/spanner/sequence-drop.js @@ -0,0 +1,80 @@ +// Copyright 2024 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: Drops a sequence in GoogleSQL database. +// usage: node sequence-drop.js + +'use strict'; + +async function main(instanceId, databaseId, projectId) { + // [START spanner_drop_sequence] + // Imports the Google Cloud client library. + const {Spanner} = require('@google-cloud/spanner'); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance'; + // const databaseId = 'my-database'; + + // Creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + async function dropSequence(instanceId, databaseId) { + // Gets a reference to a Cloud Spanner Database Admin Client object + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + const request = [ + 'ALTER TABLE Customers ALTER COLUMN CustomerId DROP DEFAULT', + 'DROP SEQUENCE Seq', + ]; + + // Drop sequence from DDL + try { + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: request, + }); + + console.log('Waiting for operation to complete...'); + await operation.promise(); + + console.log( + 'Altered Customers table to drop DEFAULT from CustomerId column and dropped the Seq sequence.' + ); + } catch (err) { + console.error('ERROR:', err); + } finally { + // Close the spanner client when finished. + // The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient. + spanner.close(); + } + } + await dropSequence(instanceId, databaseId); + // [END spanner_drop_sequence] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/system-test/spanner.test.js b/spanner/system-test/spanner.test.js index b31bb249ea..a7b5dc5144 100644 --- a/spanner/system-test/spanner.test.js +++ b/spanner/system-test/spanner.test.js @@ -585,7 +585,7 @@ describe('Autogenerated Admin Clients', () => { }); }); - describe.skip('quickstart', () => { + describe('quickstart', () => { // Running the quickstart test in here since there's already a spanner // instance and database set up at this point. it('should query a table', async () => { @@ -1715,7 +1715,7 @@ describe('Autogenerated Admin Clients', () => { }); // custom_timeout_and_retry - it.skip('should insert with custom timeout and retry settings', async () => { + it('should insert with custom timeout and retry settings', async () => { const output = execSync( `${dmlCmd} insertWithCustomTimeoutAndRetrySettings ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1723,7 +1723,7 @@ describe('Autogenerated Admin Clients', () => { }); // get_commit_stats - it.skip('should update rows in Albums example table and return CommitStats', async () => { + it('should update rows in Albums example table and return CommitStats', async () => { const output = execSync( `${crudCmd} getCommitStats ${INSTANCE_ID} ${DATABASE_ID} ${PROJECT_ID}` ); @@ -1731,7 +1731,7 @@ describe('Autogenerated Admin Clients', () => { }); // create_database_with_version_retention_period - it.skip('should create a database with a version retention period', async () => { + it('should create a database with a version retention period', async () => { const output = execSync( `${schemaCmd} createDatabaseWithVersionRetentionPeriod "${INSTANCE_ID}" "${VERSION_RETENTION_DATABASE_ID}" ${PROJECT_ID}` ); @@ -1751,7 +1751,7 @@ describe('Autogenerated Admin Clients', () => { assert.include(output, 'Earliest version time:'); }); - it.skip('should create a table with foreign key delete cascade', async () => { + it('should create a table with foreign key delete cascade', async () => { const output = execSync( `${createTableWithForeignKeyDeleteCascadeCommand} "${INSTANCE_ID}" "${DATABASE_ID}" ${PROJECT_ID}` ); @@ -1767,7 +1767,7 @@ describe('Autogenerated Admin Clients', () => { ); }); - it.skip('should alter a table with foreign key delete cascade', async () => { + it('should alter a table with foreign key delete cascade', async () => { const output = execSync( `${alterTableWithForeignKeyDeleteCascadeCommand} "${INSTANCE_ID}" "${DATABASE_ID}" ${PROJECT_ID}` ); @@ -1781,7 +1781,7 @@ describe('Autogenerated Admin Clients', () => { ); }); - it.skip('should drop a foreign key constraint delete cascade', async () => { + it('should drop a foreign key constraint delete cascade', async () => { const output = execSync( `${dropForeignKeyConstraintDeleteCascaseCommand} "${INSTANCE_ID}" "${DATABASE_ID}" ${PROJECT_ID}` ); @@ -1797,7 +1797,7 @@ describe('Autogenerated Admin Clients', () => { ); }); - describe.skip('observability', () => { + describe('observability', () => { it('traces', () => { const output = execSync( `${traceObservabilityCommand} ${PROJECT_ID} ${INSTANCE_ID} ${DATABASE_ID}` @@ -1806,7 +1806,7 @@ describe('Autogenerated Admin Clients', () => { }); }); - describe.skip('leader options', () => { + describe('leader options', () => { before(async () => { const instance = spanner.instance(SAMPLE_INSTANCE_ID); const [, operation] = await instance.create({ @@ -2001,7 +2001,7 @@ describe('Autogenerated Admin Clients', () => { }); }); - describe.skip('sequence', () => { + describe('sequence', () => { before(async () => { const instance = spanner.instance(INSTANCE_ID); const database = instance.database(SEQUENCE_DATABASE_ID); @@ -2062,7 +2062,7 @@ describe('Autogenerated Admin Clients', () => { }); }); - describe.skip('instance partition', () => { + describe('instance partition', () => { before(async () => { const instance = spanner.instance(SAMPLE_INSTANCE_ID); const [, operation] = await instance.create({ diff --git a/spanner/table-alter-with-foreign-key-delete-cascade.js b/spanner/table-alter-with-foreign-key-delete-cascade.js new file mode 100644 index 0000000000..f3bf4e466e --- /dev/null +++ b/spanner/table-alter-with-foreign-key-delete-cascade.js @@ -0,0 +1,70 @@ +// Copyright 2024 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: Alters a table with foreign key delete cascade action +// usage: node table-alter-with-foreign-key-delete-cascade.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_alter_table_with_foreign_key_delete_cascade] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function alterTableWithForeignKeyDeleteCascade() { + const request = [ + `ALTER TABLE ShoppingCarts + ADD CONSTRAINT FKShoppingCartsCustomerName + FOREIGN KEY (CustomerName) + REFERENCES Customers(CustomerName) + ON DELETE CASCADE`, + ]; + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: request, + }); + + console.log(`Waiting for operation on ${databaseId} to complete...`); + await operation.promise(); + + console.log('Altered ShoppingCarts table with FKShoppingCartsCustomerName'); + } + alterTableWithForeignKeyDeleteCascade(); + // [END spanner_alter_table_with_foreign_key_delete_cascade] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/table-create-with-foreign-key-delete-cascade.js b/spanner/table-create-with-foreign-key-delete-cascade.js new file mode 100644 index 0000000000..b0e9b215ea --- /dev/null +++ b/spanner/table-create-with-foreign-key-delete-cascade.js @@ -0,0 +1,78 @@ +// Copyright 2024 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 table with foreign key delete cascade action +// usage: node table-create-with-foreign-key-delete-cascade.js.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_create_table_with_foreign_key_delete_cascade] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function createTableWithForeignKeyDeleteCascade() { + const request = [ + `CREATE TABLE Customers ( + CustomerId INT64, + CustomerName STRING(62) NOT NULL + ) PRIMARY KEY (CustomerId)`, + `CREATE TABLE ShoppingCarts ( + CartId INT64 NOT NULL, + CustomerId INT64 NOT NULL, + CustomerName STRING(62) NOT NULL, + CONSTRAINT FKShoppingCartsCustomerId FOREIGN KEY (CustomerId) + REFERENCES Customers (CustomerId) ON DELETE CASCADE, + ) PRIMARY KEY (CartId)`, + ]; + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: request, + }); + + console.log(`Waiting for operation on ${databaseId} to complete...`); + await operation.promise(); + + console.log( + 'Created Customers and ShoppingCarts table with FKShoppingCartsCustomerId' + ); + } + createTableWithForeignKeyDeleteCascade(); + // [END spanner_create_table_with_foreign_key_delete_cascade] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/spanner/table-drop-foreign-key-constraint-delete-cascade.js b/spanner/table-drop-foreign-key-constraint-delete-cascade.js new file mode 100644 index 0000000000..ac9fc56990 --- /dev/null +++ b/spanner/table-drop-foreign-key-constraint-delete-cascade.js @@ -0,0 +1,70 @@ +// Copyright 2024 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: Drops a foreign key constraint with delete cascade action +// usage: node table-drop-foreign-key-constraint-delete-cascade.js + +'use strict'; + +function main(instanceId, databaseId, projectId) { + // [START spanner_drop_foreign_key_constraint_delete_cascade] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = 'my-project-id'; + // const instanceId = 'my-instance-id'; + // const databaseId = 'my-database-id'; + + // Imports the Google Cloud client library + const {Spanner} = require('@google-cloud/spanner'); + + // creates a client + const spanner = new Spanner({ + projectId: projectId, + }); + + const databaseAdminClient = spanner.getDatabaseAdminClient(); + + async function dropForeignKeyConstraintDeleteCascade() { + const request = [ + `ALTER TABLE ShoppingCarts + DROP CONSTRAINT FKShoppingCartsCustomerName`, + ]; + + const [operation] = await databaseAdminClient.updateDatabaseDdl({ + database: databaseAdminClient.databasePath( + projectId, + instanceId, + databaseId + ), + statements: request, + }); + + console.log(`Waiting for operation on ${databaseId} to complete...`); + await operation.promise(); + + console.log( + 'Altered ShoppingCarts table to drop FKShoppingCartsCustomerName' + ); + } + dropForeignKeyConstraintDeleteCascade(); + // [END spanner_drop_foreign_key_constraint_delete_cascade] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2));