Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cd284b2

Browse files
author
Max Schaefer
committed
JavaScript: Add support for Google Cloud Spanner.
1 parent c064b1f commit cd284b2

5 files changed

Lines changed: 150 additions & 0 deletions

File tree

change-notes/1.19/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following features:
1010
- file system access, for example through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby)
11+
- the [Google Cloud Spanner](https://cloud.google.com/spanner) database
1112

1213
* The type inference now handles nested imports (that is, imports not appearing at the toplevel). This may yield fewer false-positive results on projects that use this non-standard language feature.
1314

javascript/ql/src/semmle/javascript/frameworks/SQL.qll

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,105 @@ private module Sequelize {
387387
}
388388
}
389389
}
390+
391+
/**
392+
* Provides classes modelling the Google Cloud Spanner library.
393+
*/
394+
private module Spanner {
395+
/**
396+
* Gets a node that refers to the `Spanner` class
397+
*/
398+
DataFlow::SourceNode spanner() {
399+
// older versions
400+
result = DataFlow::moduleImport("@google-cloud/spanner")
401+
or
402+
// newer versions
403+
result = DataFlow::moduleMember("@google-cloud/spanner", "Spanner")
404+
}
405+
406+
/**
407+
* Gets a node that refers to an instance of the `Database` class.
408+
*/
409+
DataFlow::SourceNode database() {
410+
result = spanner().getAnInvocation().getAMethodCall("instance").getAMethodCall("database")
411+
}
412+
413+
/**
414+
* Gets a node that refers to an instance of the `v1.SpannerClient` class.
415+
*/
416+
DataFlow::SourceNode v1SpannerClient() {
417+
result = spanner().getAPropertyRead("v1").getAPropertyRead("SpannerClient").getAnInstantiation()
418+
}
419+
420+
/**
421+
* Gets a node that refers to a transaction object.
422+
*/
423+
DataFlow::SourceNode transaction() {
424+
result = database().getAMethodCall("runTransaction").getCallback(0).getParameter(1)
425+
}
426+
427+
/**
428+
* A call to a Spanner method that executes a SQL query.
429+
*/
430+
abstract class SqlExecution extends DatabaseAccess, DataFlow::InvokeNode {
431+
/**
432+
* Gets the position of the query argument; default is zero, which can be overridden
433+
* by subclasses.
434+
*/
435+
int getQueryArgumentPosition() {
436+
result = 0
437+
}
438+
439+
override DataFlow::Node getAQueryArgument() {
440+
result = getArgument(getQueryArgumentPosition()) or
441+
result = getOptionArgument(getQueryArgumentPosition(), "sql")
442+
}
443+
}
444+
445+
/**
446+
* A call to `Database.run` or `Database.runStream`.
447+
*/
448+
class DatabaseRunCall extends SqlExecution {
449+
DatabaseRunCall() {
450+
exists (string run | run = "run" or run = "runPartitionedUpdate" or run = "runStream" |
451+
this = database().getAMethodCall(run)
452+
)
453+
}
454+
}
455+
456+
/**
457+
* A call to `Transaction.run` or `Database.runStream`.
458+
*/
459+
class TransactionRunCall extends SqlExecution {
460+
TransactionRunCall() {
461+
exists (string run | run = "run" or run = "runStream" or run = "runUpdate" |
462+
this = transaction().getAMethodCall(run)
463+
)
464+
}
465+
}
466+
467+
/**
468+
* A call to `v1.SpannerClient.executeSql` or `v1.SpannerClient.executeStreamingSql`.
469+
*/
470+
class ExecuteSqlCall extends SqlExecution {
471+
ExecuteSqlCall() {
472+
exists (string exec | exec = "executeSql" or exec = "executeStreamingSql" |
473+
this = v1SpannerClient().getAMethodCall(exec)
474+
)
475+
}
476+
477+
override DataFlow::Node getAQueryArgument() {
478+
// `executeSql` and `executeStreamingSql` do not accept query strings directly
479+
result = getOptionArgument(0, "sql")
480+
}
481+
}
482+
483+
/**
484+
* An expression that is interpreted as a SQL string.
485+
*/
486+
class QueryString extends SQL::SqlString {
487+
QueryString() {
488+
this = any(SqlExecution se).getAQueryArgument().asExpr()
489+
}
490+
}
491+
}

javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,24 @@
1414
| postgres3.js:15:16:15:40 | 'SELECT ... s name' |
1515
| sequelize2.js:10:17:10:118 | 'SELECT ... Y name' |
1616
| sequelize.js:8:17:8:118 | 'SELECT ... Y name' |
17+
| spanner2.js:5:26:5:35 | "SQL code" |
18+
| spanner2.js:7:35:7:44 | "SQL code" |
19+
| spanner.js:6:8:6:17 | "SQL code" |
20+
| spanner.js:7:8:7:26 | { sql: "SQL code" } |
21+
| spanner.js:7:15:7:24 | "SQL code" |
22+
| spanner.js:8:25:8:34 | "SQL code" |
23+
| spanner.js:9:25:9:43 | { sql: "SQL code" } |
24+
| spanner.js:9:32:9:41 | "SQL code" |
25+
| spanner.js:10:14:10:23 | "SQL code" |
26+
| spanner.js:11:14:11:31 | { sql: "SQL code"} |
27+
| spanner.js:11:21:11:30 | "SQL code" |
28+
| spanner.js:14:10:14:19 | "SQL code" |
29+
| spanner.js:15:10:15:28 | { sql: "SQL code" } |
30+
| spanner.js:15:17:15:26 | "SQL code" |
31+
| spanner.js:16:16:16:25 | "SQL code" |
32+
| spanner.js:17:16:17:34 | { sql: "SQL code" } |
33+
| spanner.js:17:23:17:32 | "SQL code" |
34+
| spanner.js:18:16:18:25 | "SQL code" |
35+
| spanner.js:19:16:19:34 | { sql: "SQL code" } |
36+
| spanner.js:19:23:19:32 | "SQL code" |
1737
| sqlite.js:7:8:7:45 | "UPDATE ... id = ?" |
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { Spanner } = require("@google-cloud/spanner");
2+
const spanner = new Spanner();
3+
const instance = spanner.instance('inst');
4+
const db = instance.database('db');
5+
6+
db.run("SQL code", (err, rows) => {});
7+
db.run({ sql: "SQL code" }, (err, rows) => {});
8+
db.runPartitionedUpdate("SQL code", (err, rows) => {});
9+
db.runPartitionedUpdate({ sql: "SQL code" }, (err, rows) => {});
10+
db.runStream("SQL code");
11+
db.runStream({ sql: "SQL code"});
12+
13+
db.runTransaction((err, tx) => {
14+
tx.run("SQL code");
15+
tx.run({ sql: "SQL code" });
16+
tx.runStream("SQL code");
17+
tx.runStream({ sql: "SQL code" });
18+
tx.runUpdate("SQL code");
19+
tx.runUpdate({ sql: "SQL code" });
20+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const spanner = require("@google-cloud/spanner");
2+
const client = new spanner.v1.SpannerClient({});
3+
4+
client.executeSql("not SQL code", (err, rows) => {});
5+
client.executeSql({ sql: "SQL code" }, (err, rows) => {});
6+
client.executeStreamingSql("not SQL code", (err, rows) => {});
7+
client.executeStreamingSql({ sql: "SQL code" }, (err, rows) => {});

0 commit comments

Comments
 (0)