Skip to content

Commit ed87c9f

Browse files
authored
feat(spanner): migrate batch 1 core samples and tests (#4273)
* feat(spanner): first batch of samples including create database and update * fix(spanner): address PR review comments for INT64 keys and concurrency limit * fix(spanner): disable database drop protection during test cleanup
1 parent 24d8a8a commit ed87c9f

11 files changed

Lines changed: 3897 additions & 0 deletions

spanner/crud.js

Lines changed: 395 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
// Copyright 2017 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
// sample-metadata:
18+
// title: CRUD
19+
20+
async function updateData(instanceId, databaseId, projectId) {
21+
// [START spanner_update_data]
22+
// Imports the Google Cloud client library
23+
const {Spanner} = require('@google-cloud/spanner');
24+
25+
/**
26+
* TODO(developer): Uncomment the following lines before running the sample.
27+
*/
28+
// const projectId = 'my-project-id';
29+
// const instanceId = 'my-instance';
30+
// const databaseId = 'my-database';
31+
32+
// Creates a client
33+
const spanner = new Spanner({
34+
projectId: projectId,
35+
});
36+
37+
// Gets a reference to a Cloud Spanner instance and database
38+
const instance = spanner.instance(instanceId);
39+
const database = instance.database(databaseId);
40+
41+
// Update a row in the Albums table
42+
// Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so they
43+
// must be converted to strings before being inserted as INT64s
44+
const albumsTable = database.table('Albums');
45+
46+
try {
47+
await albumsTable.update([
48+
{SingerId: '1', AlbumId: '1', MarketingBudget: '100000'},
49+
{SingerId: '2', AlbumId: '2', MarketingBudget: '500000'},
50+
]);
51+
console.log('Updated data.');
52+
} catch (err) {
53+
console.error('ERROR:', err);
54+
} finally {
55+
// Close the database when finished.
56+
await database.close();
57+
}
58+
// [END spanner_update_data]
59+
}
60+
61+
async function insertData(instanceId, databaseId, projectId) {
62+
// [START spanner_insert_data]
63+
// Imports the Google Cloud client library
64+
const {Spanner} = require('@google-cloud/spanner');
65+
66+
/**
67+
* TODO(developer): Uncomment the following lines before running the sample.
68+
*/
69+
// const projectId = 'my-project-id';
70+
// const instanceId = 'my-instance';
71+
// const databaseId = 'my-database';
72+
73+
// Creates a client
74+
const spanner = new Spanner({
75+
projectId: projectId,
76+
});
77+
78+
// Gets a reference to a Cloud Spanner instance and database
79+
const instance = spanner.instance(instanceId);
80+
const database = instance.database(databaseId);
81+
82+
// Instantiate Spanner table objects
83+
const singersTable = database.table('Singers');
84+
const albumsTable = database.table('Albums');
85+
86+
// Inserts rows into the Singers table
87+
// Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so
88+
// they must be converted to strings before being inserted as INT64s
89+
try {
90+
await singersTable.insert([
91+
{SingerId: '1', FirstName: 'Marc', LastName: 'Richards'},
92+
{SingerId: '2', FirstName: 'Catalina', LastName: 'Smith'},
93+
{SingerId: '3', FirstName: 'Alice', LastName: 'Trentor'},
94+
{SingerId: '4', FirstName: 'Lea', LastName: 'Martin'},
95+
{SingerId: '5', FirstName: 'David', LastName: 'Lomond'},
96+
]);
97+
98+
await albumsTable.insert([
99+
{SingerId: '1', AlbumId: '1', AlbumTitle: 'Total Junk'},
100+
{SingerId: '1', AlbumId: '2', AlbumTitle: 'Go, Go, Go'},
101+
{SingerId: '2', AlbumId: '1', AlbumTitle: 'Green'},
102+
{SingerId: '2', AlbumId: '2', AlbumTitle: 'Forever Hold your Peace'},
103+
{SingerId: '2', AlbumId: '3', AlbumTitle: 'Terrified'},
104+
]);
105+
106+
console.log('Inserted data.');
107+
} catch (err) {
108+
console.error('ERROR:', err);
109+
} finally {
110+
await database.close();
111+
}
112+
// [END spanner_insert_data]
113+
}
114+
115+
async function deleteData(instanceId, databaseId, projectId) {
116+
// [START spanner_delete_data]
117+
// Imports the Google Cloud client library
118+
const {Spanner} = require('@google-cloud/spanner');
119+
120+
/**
121+
* TODO(developer): Uncomment the following lines before running the sample.
122+
*/
123+
// const projectId = 'my-project-id';
124+
// const instanceId = 'my-instance';
125+
// const databaseId = 'my-database';
126+
127+
// Creates a client
128+
const spanner = new Spanner({
129+
projectId: projectId,
130+
});
131+
132+
// Gets a reference to a Cloud Spanner instance and database
133+
const instance = spanner.instance(instanceId);
134+
const database = instance.database(databaseId);
135+
136+
// Instantiate Spanner table object
137+
const albumsTable = database.table('Albums');
138+
139+
// Deletes individual rows from the Albums table.
140+
try {
141+
const keys = [
142+
['2', '1'],
143+
['2', '3'],
144+
];
145+
await albumsTable.deleteRows(keys);
146+
console.log('Deleted individual rows in Albums.');
147+
} catch (err) {
148+
console.error('ERROR:', err);
149+
}
150+
151+
// Delete a range of rows where the column key is >=3 and <5
152+
database.runTransaction(async (err, transaction) => {
153+
if (err) {
154+
console.error(err);
155+
return;
156+
}
157+
try {
158+
const [rowCount] = await transaction.runUpdate({
159+
sql: 'DELETE FROM Singers WHERE SingerId >= 3 AND SingerId < 5',
160+
});
161+
console.log(`${rowCount} records deleted from Singers.`);
162+
} catch (err) {
163+
console.error('ERROR:', err);
164+
}
165+
166+
// Deletes remaining rows from the Singers table and the Albums table,
167+
// because Albums table is defined with ON DELETE CASCADE.
168+
try {
169+
// The WHERE clause is required for DELETE statements to prevent
170+
// accidentally deleting all rows in a table.
171+
// https://cloud.google.com/spanner/docs/dml-syntax#where_clause
172+
const [rowCount] = await transaction.runUpdate({
173+
sql: 'DELETE FROM Singers WHERE true',
174+
});
175+
console.log(`${rowCount} records deleted from Singers.`);
176+
await transaction.commit();
177+
} catch (err) {
178+
console.error('ERROR:', err);
179+
} finally {
180+
// Close the database when finished.
181+
await database.close();
182+
}
183+
});
184+
// [END spanner_delete_data]
185+
}
186+
187+
async function queryData(instanceId, databaseId, projectId) {
188+
// [START spanner_query_data]
189+
// Imports the Google Cloud client library
190+
const {Spanner} = require('@google-cloud/spanner');
191+
192+
/**
193+
* TODO(developer): Uncomment the following lines before running the sample.
194+
*/
195+
// const projectId = 'my-project-id';
196+
// const instanceId = 'my-instance';
197+
// const databaseId = 'my-database';
198+
199+
// Creates a client
200+
const spanner = new Spanner({
201+
projectId: projectId,
202+
});
203+
204+
// Gets a reference to a Cloud Spanner instance and database
205+
const instance = spanner.instance(instanceId);
206+
const database = instance.database(databaseId);
207+
208+
const query = {
209+
sql: 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums',
210+
};
211+
212+
// Queries rows from the Albums table
213+
try {
214+
const [rows] = await database.run(query);
215+
216+
rows.forEach(row => {
217+
const json = row.toJSON();
218+
console.log(
219+
`SingerId: ${json.SingerId}, AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`
220+
);
221+
});
222+
} catch (err) {
223+
console.error('ERROR:', err);
224+
} finally {
225+
// Close the database when finished.
226+
await database.close();
227+
}
228+
// [END spanner_query_data]
229+
}
230+
231+
async function readData(instanceId, databaseId, projectId) {
232+
// [START spanner_read_data]
233+
// Imports the Google Cloud client library
234+
const {Spanner} = require('@google-cloud/spanner');
235+
236+
/**
237+
* TODO(developer): Uncomment the following lines before running the sample.
238+
*/
239+
// const projectId = 'my-project-id';
240+
// const instanceId = 'my-instance';
241+
// const databaseId = 'my-database';
242+
243+
// Creates a client
244+
const spanner = new Spanner({
245+
projectId: projectId,
246+
});
247+
248+
// Gets a reference to a Cloud Spanner instance and database
249+
const instance = spanner.instance(instanceId);
250+
const database = instance.database(databaseId);
251+
252+
// Reads rows from the Albums table
253+
const albumsTable = database.table('Albums');
254+
255+
const query = {
256+
columns: ['SingerId', 'AlbumId', 'AlbumTitle'],
257+
keySet: {
258+
all: true,
259+
},
260+
};
261+
262+
try {
263+
const [rows] = await albumsTable.read(query);
264+
265+
rows.forEach(row => {
266+
const json = row.toJSON();
267+
console.log(
268+
`SingerId: ${json.SingerId}, AlbumId: ${json.AlbumId}, AlbumTitle: ${json.AlbumTitle}`
269+
);
270+
});
271+
} catch (err) {
272+
console.error('ERROR:', err);
273+
} finally {
274+
// Close the database when finished.
275+
await database.close();
276+
}
277+
// [END spanner_read_data]
278+
}
279+
280+
async function readStaleData(instanceId, databaseId, projectId) {
281+
// [START spanner_read_stale_data]
282+
// Imports the Google Cloud client library
283+
const {Spanner} = require('@google-cloud/spanner');
284+
285+
/**
286+
* TODO(developer): Uncomment the following lines before running the sample.
287+
*/
288+
// const projectId = 'my-project-id';
289+
// const instanceId = 'my-instance';
290+
// const databaseId = 'my-database';
291+
292+
// Creates a client
293+
const spanner = new Spanner({
294+
projectId: projectId,
295+
});
296+
297+
// Gets a reference to a Cloud Spanner instance and database
298+
const instance = spanner.instance(instanceId);
299+
const database = instance.database(databaseId);
300+
301+
// Reads rows from the Albums table
302+
const albumsTable = database.table('Albums');
303+
304+
const query = {
305+
columns: ['SingerId', 'AlbumId', 'AlbumTitle', 'MarketingBudget'],
306+
keySet: {
307+
all: true,
308+
},
309+
};
310+
311+
const options = {
312+
// Guarantees that all writes committed more than 15000 milliseconds ago are visible
313+
exactStaleness: 15000,
314+
};
315+
316+
try {
317+
const [rows] = await albumsTable.read(query, options);
318+
319+
rows.forEach(row => {
320+
const json = row.toJSON();
321+
const id = json.SingerId;
322+
const album = json.AlbumId;
323+
const title = json.AlbumTitle;
324+
const budget = json.MarketingBudget ? json.MarketingBudget : '';
325+
console.log(
326+
`SingerId: ${id}, AlbumId: ${album}, AlbumTitle: ${title}, MarketingBudget: ${budget}`
327+
);
328+
});
329+
} catch (err) {
330+
console.error('ERROR:', err);
331+
} finally {
332+
// Close the database when finished.
333+
await database.close();
334+
}
335+
// [END spanner_read_stale_data]
336+
}
337+
338+
const {getCommitStats} = require('./get-commit-stats');
339+
340+
require('yargs')
341+
.demand(1)
342+
.command(
343+
'update <instanceName> <databaseName> <projectId>',
344+
'Modifies existing rows of data in an example Cloud Spanner table.',
345+
{},
346+
opts => updateData(opts.instanceName, opts.databaseName, opts.projectId)
347+
)
348+
.command(
349+
'query <instanceName> <databaseName> <projectId>',
350+
'Executes a read-only SQL query against an example Cloud Spanner table.',
351+
{},
352+
opts => queryData(opts.instanceName, opts.databaseName, opts.projectId)
353+
)
354+
.command(
355+
'insert <instanceName> <databaseName> <projectId>',
356+
'Inserts new rows of data into an example Cloud Spanner table.',
357+
{},
358+
opts => insertData(opts.instanceName, opts.databaseName, opts.projectId)
359+
)
360+
.command(
361+
'delete <instanceName> <databaseName> <projectId>',
362+
'Deletes rows from an example Cloud Spanner table.',
363+
{},
364+
opts => deleteData(opts.instanceName, opts.databaseName, opts.projectId)
365+
)
366+
.command(
367+
'read <instanceName> <databaseName> <projectId>',
368+
'Reads data in an example Cloud Spanner table.',
369+
{},
370+
opts => readData(opts.instanceName, opts.databaseName, opts.projectId)
371+
)
372+
.command(
373+
'read-stale <instanceName> <databaseName> <projectId>',
374+
'Reads stale data in an example Cloud Spanner table.',
375+
{},
376+
opts => readStaleData(opts.instanceName, opts.databaseName, opts.projectId)
377+
)
378+
.command(
379+
'getCommitStats <instanceName> <databaseName> <projectId>',
380+
'Updates rows in example Cloud Spanner table and reads CommitStats.',
381+
{},
382+
opts => getCommitStats(opts.instanceName, opts.databaseName, opts.projectId)
383+
)
384+
.example('node $0 update "my-instance" "my-database" "my-project-id"')
385+
.example('node $0 query "my-instance" "my-database" "my-project-id"')
386+
.example('node $0 insert "my-instance" "my-database" "my-project-id"')
387+
.example('node $0 delete "my-instance" "my-database" "my-project-id"')
388+
.example('node $0 read "my-instance" "my-database" "my-project-id"')
389+
.example('node $0 read-stale "my-instance" "my-database" "my-project-id"')
390+
.example('node $0 getCommitStats "my-instance" "my-database" "my-project-id"')
391+
.wrap(120)
392+
.recommendCommands()
393+
.epilogue('For more information, see https://cloud.google.com/spanner/docs')
394+
.strict()
395+
.help().argv;

0 commit comments

Comments
 (0)