-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2ba74f9
Showing
11 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
root = true | ||
|
||
[Makefile] | ||
indent_style = tab | ||
|
||
[*.js] | ||
indent_style = tab | ||
|
||
[*.json] | ||
indent_style = tab |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/node_modules/ | ||
/*.tgz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/*.tgz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
## 1.0.0 (Apr 27, 2019) | ||
- Things could sqlate quickly. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Sqlate.js | ||
Copyright (C) 2019– Andri Möll <[email protected]> | ||
|
||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. | ||
|
||
Additional permission under the GNU Affero GPL version 3 section 7: | ||
If you modify this Program, or any covered work, by linking or combining it with other code, such other code is not for that reason alone subject to any of the requirements of the GNU Affero GPL version 3. | ||
|
||
In summary: | ||
- You can use this program for no cost. | ||
- You can use this program for both personal and commercial reasons. | ||
- You do not have to share your own program's code which uses this program. | ||
- You have to share modifications (e.g bug-fixes) you've made to this program. | ||
|
||
For the full copy of the GNU Affero General Public License see: | ||
http://www.gnu.org/licenses. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
NODE = node | ||
NODE_OPTS = --use-strict | ||
MOCHA = ./node_modules/.bin/_mocha | ||
TEST = test/**/*_test.js | ||
|
||
love: | ||
@echo "Feel like makin' love." | ||
|
||
test: | ||
@$(NODE) $(NODE_OPTS) $(MOCHA) -R dot $(TEST) | ||
|
||
spec: | ||
@$(NODE) $(NODE_OPTS) $(MOCHA) -R spec $(TEST) | ||
|
||
autotest: | ||
@$(NODE) $(NODE_OPTS) $(MOCHA) -R dot --watch $(TEST) | ||
|
||
autospec: | ||
@$(NODE) $(NODE_OPTS) $(MOCHA) -R spec --watch $(TEST) | ||
|
||
pack: | ||
@file=$$(npm pack); echo "$$file"; tar tf "$$file" | ||
|
||
publish: | ||
npm publish | ||
|
||
tag: | ||
git tag "v$$($(NODE) -e 'console.log(require("./package").version)')" | ||
|
||
clean: | ||
-$(RM) *.tgz | ||
npm prune --production | ||
|
||
.PHONY: love | ||
.PHONY: test spec autotest autospec | ||
.PHONY: pack publish tag | ||
.PHONY: clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
Sqlate.js | ||
========= | ||
[![NPM version][npm-badge]](https://www.npmjs.com/package/sqlate) | ||
|
||
Sqlate.js is a tiny [tagged template string][template-string] function library for JavaScript that **permits you to write SQL in a template string and get a `Sql` instance out with parameter placeholders**. You can then pass the SQL and parameters safely to [Mapbox's SQLite3][node-sqlite3], [Brian Carlson's PostgreSQL][node-postgresql] or other Node.js database libraries. | ||
|
||
[npm-badge]: https://img.shields.io/npm/v/sqlate.svg | ||
[template-string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals | ||
[node-sqlite3]: https://github.com/mapbox/node-sqlite3 | ||
[node-postgresql]: https://node-postgres.com | ||
|
||
|
||
Installing | ||
---------- | ||
```sh | ||
npm install sqlate | ||
``` | ||
|
||
Sqlate.js follows [semantic versioning](http://semver.org), so feel free to depend on its major version with something like `>= 1.0.0 < 2` (a.k.a `^1.0.0`). | ||
|
||
|
||
Using | ||
----- | ||
```javascript | ||
var Sql = require("sqlate").Sql | ||
var sql = require("sqlate") | ||
|
||
var ids = [1, 2, 3] | ||
var age = 42 | ||
var query = sql`SELECT * FROM models WHERE id IN (${ids}) AND age > ${age}` | ||
query instanceof Sql // => true | ||
``` | ||
|
||
The `query` variable will be set to an instance of `Sql`. This way you can differentiate safe SQL from plain strings. | ||
|
||
When you stringify the above query via `String(query)`, you'll get the SQL with values transformed to placeholders: | ||
|
||
```sql | ||
SELECT * FROM models WHERE id IN (?, ?, ?) AND age > ? | ||
``` | ||
|
||
To get the values, get the `parameters` property from the `query`. | ||
|
||
Regular values get interpolated as placeholders (question marks) and arrays get interpolated as a comma-separated list of question marks. This allows using JavaScript arrays both for SQL tuples and [(PostgreSQL) arrays](https://www.postgresql.org/docs/9.6/functions-array.html): | ||
|
||
```javascript | ||
var nameAndAge = ["John", 42] | ||
var query = sql`SELECT * FROM models WHERE (name, age) = (${ids})` | ||
|
||
var tags = ["convertible", "v8"] | ||
var query = sql`SELECT * FROM cars WHERE tags @> ARRAY[${ids}]` | ||
``` | ||
|
||
When you need to get nested tuples, like when creating an insert statement, use `sql.tuple` on each array element. See [below](#creating-insert-statements) for an example. | ||
|
||
### Composing SQL | ||
You can freely compose different pieces of SQL safely by passing one `Sql` instance to another: | ||
|
||
```javascript | ||
var id = 42 | ||
var name = "John" | ||
var idClause = sql`id = ${id}` | ||
var nameClause = sql`name = ${name}` | ||
db.query(sql`SELECT * FROM models WHERE ${idClause} AND ${nameClause}`) | ||
``` | ||
|
||
This will generate the following query: | ||
|
||
```sql | ||
SELECT * FROM models WHERE id = ? AND name ? | ||
``` | ||
|
||
### Creating Insert Statements | ||
Sqlate.js also has helpers to quote table and column names. These come in handy for insert statements: | ||
|
||
```javascript | ||
var table = sql.table("models") | ||
var columns = ["name", "age"].map(sql.column) | ||
var values = [["John", 42], ["Mike", 13]].map(sql.tuple) | ||
db.query(sql`INSERT INTO ${table} ${columns} VALUES ${values}`) | ||
``` | ||
|
||
This will generate the following query: | ||
|
||
```sql | ||
INSERT INTO "models" ("name", "age") VALUES (?, ?), (?, ?) | ||
``` | ||
|
||
The two helpers, `sql.table` and `sql.column`, have no differences other than their names. While it's safe to pass untrusted data as values, **watch out for using untrusted data as table and column names**. Sqlate.js quotes them as per the SQL 1999 standard (using two double-quotes `""` for embedded quotes) if you use `sql.column`, but just to be safe, use a whitelist. | ||
|
||
### Using with Mapbox's SQLite3 | ||
If you'd like to use Sqlate.js with [Mapbox's SQLite3][node-sqlite3] library, here's an example of how you'd do so: | ||
|
||
```javascript | ||
var Sqlite3 = require("sqlite3") | ||
var db = new Sqlite3.Database(":memory:") | ||
db.serialize() | ||
|
||
var ids = [1, 2, 3] | ||
var age = 42 | ||
var query = sql`SELECT * FROM models WHERE id IN (${ids}) AND age > ${age}` | ||
db.all(String(query), query.parameters) | ||
``` | ||
|
||
For a complete [Table Data Gateway][table-data-gateway] library for SQLite that works with Sqlate.js, see [Heaven.js on SQLite](https://github.com/moll/node-heaven-sqlite). | ||
|
||
[table-data-gateway]: https://en.wikipedia.org/wiki/Table_data_gateway | ||
|
||
### Using with Brian Carlson's PostgreSQL | ||
If you'd like to use Sqlate.js with [Brian Carlson's PostgreSQL][node-postgresql] library, here's an example of how you'd do so: | ||
|
||
```javascript | ||
var PgClient = require("pg") | ||
var db = new PgClient({host: "localhost", database: "models"}) | ||
db.connect() | ||
|
||
var ids = [1, 2, 3] | ||
var age = 42 | ||
var query = sql`SELECT * FROM models WHERE id IN (${ids}) AND age > ${age}` | ||
db.query(String(query), query.parameters) | ||
``` | ||
|
||
Because Sqlate.js's `Sql` object also has property aliases for the PostgreSQL's library's [query config object](https://node-postgres.com/features/queries), you can also pass the query directly: | ||
|
||
```javascript | ||
var ids = [1, 2, 3] | ||
var age = 42 | ||
db.query(sql`SELECT * FROM models WHERE id IN (${ids}) AND age > ${age}`) | ||
``` | ||
|
||
### Query Helpers | ||
Rather than create a query and unpack it to SQL and parameters at call sites manually, I recommend you create a two helper functions — `search` and `read` — for accessing your database: | ||
|
||
```javascript | ||
var Sql = require("sqlate").Sql | ||
var db = connect() // Using your favorite database library here. | ||
|
||
// Returns an promise of an array of rows. | ||
function search(query) { | ||
if (!(query instanceof Sql)) throw new TypeError("Invalid Query: " + query) | ||
return db.query(String(query), query.parameters) | ||
} | ||
|
||
// Returns a promise of a single row. | ||
function read(query) { | ||
return search(query).then(function(rows) { return rows[0] }) | ||
} | ||
``` | ||
|
||
This way you have a [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) interface that you can safely pass SQL to without worrying you'll accidentally cause an SQL injection: | ||
|
||
```javascript | ||
var sql = require("sqlate") | ||
var id = 42 | ||
read(sql`SELECT * FROM models WHERE id = ${id} LIMIT 1`) | ||
``` | ||
|
||
|
||
License | ||
------- | ||
Sqlate.js is released under a *Lesser GNU Affero General Public License*, which in summary means: | ||
|
||
- You **can** use this program for **no cost**. | ||
- You **can** use this program for **both personal and commercial reasons**. | ||
- You **do not have to share your own program's code** which uses this program. | ||
- You **have to share modifications** (e.g. bug-fixes) you've made to this program. | ||
|
||
For more convoluted language, see the `LICENSE` file. | ||
|
||
|
||
About | ||
----- | ||
**[Andri Möll][moll]** typed this and the code. | ||
[Monday Calendar][monday] supported the engineering work. | ||
|
||
If you find Sqlate.js needs improving, please don't hesitate to type to me now at [[email protected]][email] or [create an issue online][issues]. | ||
|
||
[email]: mailto:[email protected] | ||
[issues]: https://github.com/moll/js-sqlate/issues | ||
[moll]: https://m811.com | ||
[monday]: https://mondayapp.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "sqlate", | ||
"version": "1.0.0", | ||
"description": "Tiny tagged template string function for safe SQL. Supports SQlite3, PostgreSQL and others.", | ||
"keywords": ["sql", "sqlite", "sqlite3", "postgresql"], | ||
"homepage": "https://github.com/moll/js-sqlate", | ||
"bugs": "https://github.com/moll/js-sqlate/issues", | ||
|
||
"author": { | ||
"name": "Andri Möll", | ||
"email": "[email protected]", | ||
"url": "https://m811.com" | ||
}, | ||
|
||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/moll/js-sqlate.git" | ||
}, | ||
|
||
"licenses": [{ | ||
"type": "LAGPL", | ||
"url": "https://github.com/moll/js-sqlate/blob/master/LICENSE" | ||
}], | ||
|
||
"main": "sqlate.js", | ||
"scripts": {"test": "make test"}, | ||
|
||
"devDependencies": { | ||
"mocha": ">= 2 < 4", | ||
"must": ">= 0.13.0 < 0.14" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
var isArray = Array.isArray | ||
var slice = Function.call.bind(Array.prototype.slice) | ||
var flatten = Function.apply.bind(Array.prototype.concat, Array.prototype) | ||
var EMPTY_ARR = Array.prototype | ||
var TYPE_ERR = "SQL should be a string: " | ||
exports = module.exports = template | ||
exports.Sql = Sql | ||
exports.new = raw | ||
exports.table = quoteSql | ||
exports.column = quoteSql | ||
exports.tuple = tupleSql | ||
|
||
function Sql(sql, params) { | ||
if (typeof sql != "string") throw new TypeError(TYPE_ERR + sql) | ||
|
||
this.sql = sql | ||
this.parameters = params || EMPTY_ARR | ||
} | ||
|
||
Sql.prototype.toString = function() { | ||
return this.sql | ||
} | ||
|
||
// The "text" and "values" aliases are for Brian Carlson's PostgreSQL library | ||
// and its query-object style: https://node-postgres.com/features/queries | ||
Object.defineProperty(Sql.prototype, "text", { | ||
get: function() { return this.sql }, configurable: true | ||
}) | ||
|
||
Object.defineProperty(Sql.prototype, "values", { | ||
get: function() { return this.parameters }, configurable: true | ||
}) | ||
|
||
function template(sqls) { | ||
var params = slice(arguments, 1) | ||
|
||
var sql = sqls.reduce(function(left, right, i) { | ||
return left + toPlaceholder(params[i - 1]) + right | ||
}) | ||
|
||
return new Sql(sql, flatten(params.map(toParameter))) | ||
} | ||
|
||
function toPlaceholder(value) { | ||
// Binding a single level for now. Could be done recursively in the future. | ||
if (isArray(value)) return value.map(bind).join(", ") | ||
return bind(value) | ||
|
||
function bind(value) { return value instanceof Sql ? value.sql : "?" } | ||
} | ||
|
||
function toParameter(value) { | ||
return isArray(value) ? flatten(value.map(toValues)) : toValues(value) | ||
} | ||
|
||
function tupleSql(tuple) { | ||
if (!isArray(tuple)) throw new TypeError("Tuple must be an array: " + tuple) | ||
return new Sql("(" + toPlaceholder(tuple) + ")", flatten(tuple.map(toValues))) | ||
} | ||
|
||
function raw(sql, params) { return new Sql(sql, params) } | ||
function quoteSql(name) { return new Sql(quote(name)) } | ||
function quote(name) { return '"' + name.replace(/"/g, '""') + '"'} | ||
function toValues(val) { return val instanceof Sql ? val.parameters : [val] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--recursive | ||
--check-leaks | ||
--require must/register |
Oops, something went wrong.