From f9c1eb436dd02952c5e76be97377e3ca8894cf07 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 10:52:06 +0400 Subject: [PATCH 01/33] fix: delete config.json and update config.default.json --- .gitignore | 1 - config.default.json | 16 ++++-- config.json | 131 -------------------------------------------- 3 files changed, 10 insertions(+), 138 deletions(-) delete mode 100644 config.json diff --git a/.gitignore b/.gitignore index 5f86aa9b..a01ea1a1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ release ssl/ # Config files -config.json test/config.json sftp-config.json diff --git a/config.default.json b/config.default.json index 01fcf138..aa43fd64 100644 --- a/config.default.json +++ b/config.default.json @@ -45,15 +45,11 @@ "enabled": true, "list": [ { - "ip": "51.15.221.205", - "port": 36666 - }, - { - "ip": "51.15.88.53", + "ip": "5.161.68.61", "port": 36666 }, { - "ip": "5.161.68.61", + "ip": "149.102.157.15", "port": 36666 }, { @@ -67,6 +63,14 @@ { "ip": "138.201.152.191", "port": 36666 + }, + { + "ip": "184.94.215.92", + "port": 45555 + }, + { + "ip": "207.244.243.23", + "port": 45555 } ], "access": { diff --git a/config.json b/config.json deleted file mode 100644 index aa43fd64..00000000 --- a/config.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "port": 36666, - "address": "0.0.0.0", - "fileLogLevel": "warn", - "logFileName": "logs/adamant.log", - "consoleLogLevel": "info", - "trustProxy": false, - "topAccounts": false, - "cacheEnabled": false, - "db": { - "host": "localhost", - "port": 5432, - "database": "adamant_main", - "user": "adamant", - "password": "password", - "poolSize": 95, - "poolIdleTimeout": 30000, - "reapIntervalMillis": 1000, - "logEvents": [ - "error" - ] - }, - "redis": { - "url": "redis://127.0.0.1:6379/0", - "password": null - }, - "api": { - "enabled": true, - "access": { - "public": false, - "whiteList": [ - "127.0.0.1" - ] - }, - "options": { - "limits": { - "max": 0, - "delayMs": 0, - "delayAfter": 0, - "windowMs": 60000 - } - } - }, - "peers": { - "enabled": true, - "list": [ - { - "ip": "5.161.68.61", - "port": 36666 - }, - { - "ip": "149.102.157.15", - "port": 36666 - }, - { - "ip": "78.47.205.206", - "port": 36666 - }, - { - "ip": "107.161.26.184", - "port": 36666 - }, - { - "ip": "138.201.152.191", - "port": 36666 - }, - { - "ip": "184.94.215.92", - "port": 45555 - }, - { - "ip": "207.244.243.23", - "port": 45555 - } - ], - "access": { - "blackList": [] - }, - "options": { - "limits": { - "max": 0, - "delayMs": 0, - "delayAfter": 0, - "windowMs": 60000 - }, - "timeout": 5000 - } - }, - "broadcasts": { - "broadcastInterval": 1500, - "broadcastLimit": 20, - "parallelLimit": 20, - "releaseLimit": 25, - "relayLimit": 4 - }, - "transactions": { - "maxTxsPerQueue": 1000 - }, - "forging": { - "force": false, - "secret": [], - "access": { - "whiteList": [ - "127.0.0.1" - ] - } - }, - "loading": { - "verifyOnLoading": false, - "loadPerIteration": 5000 - }, - "ssl": { - "enabled": false, - "options": { - "port": 443, - "address": "0.0.0.0", - "key": "./ssl/adamant.key", - "cert": "./ssl/adamant.crt" - } - }, - "dapp": { - "masterrequired": true, - "masterpassword": "", - "autoexec": [] - }, - "wsClient": { - "portWS": 36668, - "enabled": true - }, - "nethash": "bd330166898377fb28743ceef5e43a5d9d0a3efd9b3451fb7bc53530bb0a6d64" -} From c0857f836876d6998325c30906e3bd232339b4ac Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 10:52:43 +0400 Subject: [PATCH 02/33] chore: add config.json to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a01ea1a1..5f86aa9b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ release ssl/ # Config files +config.json test/config.json sftp-config.json From 11cdcda0151c56331f64201a2c3b8d49008a4b00 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 10:56:49 +0400 Subject: [PATCH 03/33] chore: update package.json --- README.md | 2 +- package.json | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f96021a..75d96727 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ npm run test:single test/api/accounts.js ## License -Copyright © 2020-2022 ADAMANT Foundation +Copyright © 2020-2023 ADAMANT Foundation Copyright © 2017-2020 ADAMANT TECH LABS LP diff --git a/package.json b/package.json index c3762326..0a6edf3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "adamant", - "version": "0.8.2", + "version": "0.8.3", + "description": "ADAMANT Blockchain Node", "private": true, "scripts": { "start": "node app.js", @@ -33,7 +34,7 @@ "cryptocurrency", "dpos" ], - "author": "ADAMANT Tech Labs , Lisk Foundation , lightcurve GmbH ", + "author": "ADAMANT Foundation , ADAMANT Tech Labs , Lisk Foundation , lightcurve GmbH ", "license": "GPL-3.0", "dependencies": { "async": "=3.2.4", @@ -104,7 +105,7 @@ } }, "config": { - "minVersion": ">=0.4.0" + "minVersion": ">=0.6.0" }, "repository": { "type": "git", From 40070d03cbe242f49c7c9ce7803b502d9ce3fcba Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 11:07:58 +0400 Subject: [PATCH 04/33] chore: update Readme --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 75d96727..f28996a9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # ADAMANT -ADAMANT is a **decentralized blockchain messaging platform**. Applications use ADAMANT as an anonymous and encrypted relay and storage to enable messaging features. As examples, see [Messenger app](https://github.com/Adamant-im/adamant-im), [Blockchain 2FA](https://github.com/Adamant-im/adamant-2fa) and [Cryptocurrency Exchanger](https://github.com/Adamant-im/adamant-exchangebot) implementations. +ADAMANT is a **decentralized blockchain messaging platform**. Applications use ADAMANT as an anonymous and encrypted relay and storage to enable messaging features. For examples, see the [Messenger app](https://github.com/Adamant-im/adamant-im), [Blockchain 2FA](https://github.com/Adamant-im/adamant-2fa), and [Cryptocurrency Exchanger](https://github.com/Adamant-im/adamant-exchangebot) implementations. -For more information refer to ADAMANT website: . +For more information, refer to the ADAMANT website: . ![ADAMANT nodes](./img/adm-nodes.jpeg) @@ -17,17 +17,17 @@ Additional information: Comprehensive [API specification](https://github.com/Adamant-im/adamant/wiki) is available. -The manual describes API endpoints to manage accounts, transactions, chats, and a key-value storage (KVS). Additionally, the manual suggests valuable information on creating new accounts and encrypting and decrypting messages. +The manual describes API endpoints to manage accounts, transactions, chats, and key-value storage (KVS). Additionally, the manual suggests valuable information on creating new accounts and encrypting and decrypting messages. ## Set up -[How to run ADAMANT node (instructions for users)](https://medium.com/adamant-im/how-to-run-your-adamant-node-on-ubuntu-990e391e8fcc). You can skip them if you are experienced Linux user. +[How to run ADAMANT node (instructions for users)](https://medium.com/adamant-im/how-to-run-your-adamant-node-on-ubuntu-990e391e8fcc). You can skip them if you are an experienced Linux user. ### Requirements - Ubuntu 18/20/22 (others are not tested) - 2 GB RAM -- 60 GB disk space as on November 2022 +- 70 GB disk space as of November 2023 ### Installation script @@ -35,11 +35,11 @@ For new droplets, use the [Installation script](./tools/install_node.sh), includ `sudo bash -c "$(wget -O - https://adamant.im/install_node.sh)"` -The script updates Ubuntu packages, creates user named adamant, installs PostgreSQL, Node.js and other necessary packages, sets up ADAMANT node, and optionally downloads up-to-date ADAMANT blockchain image. +The script updates Ubuntu packages, creates the user named `adamant`, installs PostgreSQL, Node.js, and other necessary packages, sets up the ADAMANT node, and optionally downloads an up-to-date ADAMANT blockchain image. Script parameters: -- `-b`: choose GitHub branch for node installation. Example: `-b dev`. Default is `master`. +- `-b`: choose the GitHub branch for node installation. Example: `-b dev`. Default is `master`. - `-n`: choose `mainnet` or `testnet` network. Example: `-n testnet`. Default is `mainnet`. F. e., @@ -48,7 +48,7 @@ F. e., ### Prerequisites -- Tool chain components — Used for compiling dependencies +- Toolchain components — Used for compiling dependencies `sudo apt-get install -y python build-essential curl automake autoconf libtool` @@ -58,18 +58,18 @@ F. e., - Node.js — Node.js serves as the underlying engine for code execution - System wide via package manager: + System-wide via package manager: ``` - curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - + curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt-get install -y nodejs ``` Locally using nvm: ``` - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash - nvm i --lts=gallium + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash + nvm i --lts=hydrogen ``` - Install PostgreSQL: @@ -80,7 +80,7 @@ F. e., sudo apt-get update sudo apt-get install -y postgresql postgresql-contrib libpq-dev - # Create user if you are working from superuser + # Create a user if you are working from superuser adduser adamant sudo usermod -aG sudo adamant su - adamant @@ -129,7 +129,7 @@ Make the necessary changes to the configuration values in the file. At minimum, ### Bootstrap with a blockchain image -Blockchain image saves time on node sync but you must completely trust the image. If you skip this step, your node will check every single transaction, which takes time (up for several days). +A blockchain image saves time on node sync, but you must completely trust the image. If you skip this step, your node will check every single transaction, which takes time (up to several days). ``` wget https://explorer.adamant.im/db_backup.sql.gz @@ -162,7 +162,7 @@ To add ADAMANT node to crontab for autostart after system reboot (fix installati ## Tests -Before running any tests, run ADAMANT node with a testnet configuration: +Before running any tests, run the ADAMANT node with a testnet configuration: ``` npm run start:testnet From 45b6c56e4241a215431d839322887367b38ab6d1 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 11:31:49 +0400 Subject: [PATCH 05/33] chore: update deps with minor changes --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 0a6edf3f..bd85f3fc 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "author": "ADAMANT Foundation , ADAMANT Tech Labs , Lisk Foundation , lightcurve GmbH ", "license": "GPL-3.0", "dependencies": { - "async": "=3.2.4", + "async": "=3.2.5", "axios": "^0.27.2", "bignumber.js": "=9.1.2", "bitcore-mnemonic": "=8.25.36", @@ -67,7 +67,7 @@ "pg-native": "=3.0.1", "pg-promise": "=10.11.1", "randomstring": "=1.3.0", - "redis": "=4.6.10", + "redis": "=4.6.11", "rimraf": "=3.0.2", "semver": "=7.5.4", "socket.io": "^4.7.2", @@ -84,7 +84,7 @@ "chai-bignumber": "^3.1.0", "eslint-config-google": "^0.14.0", "eslint-formatter-codeframe": "^7.32.1", - "eslint-plugin-jsdoc": "^46.8.2", + "eslint-plugin-jsdoc": "^46.9.0", "grunt": "^1.6.1", "grunt-cli": "^1.4.3", "grunt-contrib-compress": "^2.0.0", From 71c8731030d775440d0b429b8b187785a21bb486 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 11:50:01 +0400 Subject: [PATCH 06/33] chore: switch json-sql lib to from /legacy to npmjs --- legacy/json-sql/.gitignore | 1 - legacy/json-sql/.jshintrc | 19 - legacy/json-sql/.npmignore | 2 - legacy/json-sql/LICENSE | 22 - legacy/json-sql/README.md | 145 -- legacy/json-sql/docs/README.md | 1180 ---------------- legacy/json-sql/gulpfile.js | 27 - legacy/json-sql/lib/builder.js | 233 ---- legacy/json-sql/lib/dialects/base/blocks.js | 441 ------ .../json-sql/lib/dialects/base/conditions.js | 122 -- legacy/json-sql/lib/dialects/base/index.js | 58 - .../lib/dialects/base/logicalOperators.js | 37 - .../json-sql/lib/dialects/base/modifiers.js | 37 - legacy/json-sql/lib/dialects/base/scheme.js | 146 -- .../json-sql/lib/dialects/base/templates.js | 220 --- legacy/json-sql/lib/dialects/mssql/index.js | 13 - .../lib/dialects/postgresql/blocks.js | 33 - .../lib/dialects/postgresql/conditions.js | 87 -- .../json-sql/lib/dialects/postgresql/index.js | 39 - .../lib/dialects/postgresql/templates.js | 269 ---- legacy/json-sql/lib/dialects/sqlite/blocks.js | 23 - legacy/json-sql/lib/dialects/sqlite/index.js | 17 - .../json-sql/lib/dialects/sqlite/templates.js | 236 ---- legacy/json-sql/lib/index.js | 8 - legacy/json-sql/lib/valuesStore.js | 31 - legacy/json-sql/package.json | 44 - legacy/json-sql/tests/0_base.js | 304 ---- legacy/json-sql/tests/1_select.js | 1222 ----------------- legacy/json-sql/tests/2_insert.js | 73 - legacy/json-sql/tests/3_update.js | 123 -- legacy/json-sql/tests/4_delete.js | 58 - legacy/json-sql/tests/5_union.js | 256 ---- legacy/json-sql/tests/6_postgresDialect.js | 140 -- legacy/json-sql/tests/7_create.js | 279 ---- legacy/json-sql/tests/8_index.js | 45 - package.json | 2 +- 36 files changed, 1 insertion(+), 5991 deletions(-) delete mode 100644 legacy/json-sql/.gitignore delete mode 100644 legacy/json-sql/.jshintrc delete mode 100644 legacy/json-sql/.npmignore delete mode 100644 legacy/json-sql/LICENSE delete mode 100644 legacy/json-sql/README.md delete mode 100644 legacy/json-sql/docs/README.md delete mode 100644 legacy/json-sql/gulpfile.js delete mode 100644 legacy/json-sql/lib/builder.js delete mode 100644 legacy/json-sql/lib/dialects/base/blocks.js delete mode 100644 legacy/json-sql/lib/dialects/base/conditions.js delete mode 100644 legacy/json-sql/lib/dialects/base/index.js delete mode 100644 legacy/json-sql/lib/dialects/base/logicalOperators.js delete mode 100644 legacy/json-sql/lib/dialects/base/modifiers.js delete mode 100644 legacy/json-sql/lib/dialects/base/scheme.js delete mode 100644 legacy/json-sql/lib/dialects/base/templates.js delete mode 100644 legacy/json-sql/lib/dialects/mssql/index.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/blocks.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/conditions.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/index.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/templates.js delete mode 100644 legacy/json-sql/lib/dialects/sqlite/blocks.js delete mode 100644 legacy/json-sql/lib/dialects/sqlite/index.js delete mode 100644 legacy/json-sql/lib/dialects/sqlite/templates.js delete mode 100644 legacy/json-sql/lib/index.js delete mode 100644 legacy/json-sql/lib/valuesStore.js delete mode 100644 legacy/json-sql/package.json delete mode 100644 legacy/json-sql/tests/0_base.js delete mode 100644 legacy/json-sql/tests/1_select.js delete mode 100644 legacy/json-sql/tests/2_insert.js delete mode 100644 legacy/json-sql/tests/3_update.js delete mode 100644 legacy/json-sql/tests/4_delete.js delete mode 100644 legacy/json-sql/tests/5_union.js delete mode 100644 legacy/json-sql/tests/6_postgresDialect.js delete mode 100644 legacy/json-sql/tests/7_create.js delete mode 100644 legacy/json-sql/tests/8_index.js diff --git a/legacy/json-sql/.gitignore b/legacy/json-sql/.gitignore deleted file mode 100644 index c2658d7d..00000000 --- a/legacy/json-sql/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/legacy/json-sql/.jshintrc b/legacy/json-sql/.jshintrc deleted file mode 100644 index fe7c5b09..00000000 --- a/legacy/json-sql/.jshintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "node": true, - "freeze": true, - "maxlen": 100, - "smarttabs": true, - "indent": 1, - "quotmark": "single", - "strict": true, - "globalstrict": true, - // use es3 to get 'extra comma' at object literal error - "es3": true, - "undef": true, - "unused": true, - "immed": true, - "eqeqeq": true, - "globals": { - "JSON": false - } -} diff --git a/legacy/json-sql/.npmignore b/legacy/json-sql/.npmignore deleted file mode 100644 index 3796175f..00000000 --- a/legacy/json-sql/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -.npmignore -test \ No newline at end of file diff --git a/legacy/json-sql/LICENSE b/legacy/json-sql/LICENSE deleted file mode 100644 index 2ac98523..00000000 --- a/legacy/json-sql/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright (c) 2013-2014 Oleg Korobenko - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/legacy/json-sql/README.md b/legacy/json-sql/README.md deleted file mode 100644 index 7891eaed..00000000 --- a/legacy/json-sql/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# JSON-SQL - -Library for mapping mongo-style query objects to SQL queries. - -## Quick Start - -Install it with NPM or add it to your package.json: - -``` bash -$ npm install json-sql -``` - -Then: - -``` js -var jsonSql = require('json-sql')(); - -var sql = jsonSql.build({ - type: 'select', - table: 'users', - fields: ['name', 'age'], - condition: {name: 'Max', id: 6} -}); - -sql.query -// sql string: -// select name, age from users where name = $p1 && id = 6; - -sql.values -// hash of values: -// { p1: 'Max' } -``` - -## Documentation - -Documentation is available at the [./docs directory](./docs). - -## Examples - -__Select with join:__ - -``` js -var sql = jsonSql.build({ - type: 'select', - table: 'users', - join: { - documents: { - on: {'user.id': 'documents.userId'} - } - } -}); - -sql.query -// select * from users join documents on user.id = documents.userId; - -sql.values -// {} -``` - -__Insert:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'users', - values: { - name: 'John', - lastname: 'Snow', - age: 24, - gender: 'male' - } -}); - -sql.query -// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3); - -sql.values -// { p1: 'John', p2: 'Snow', p3: 'male' } -``` - -__Update:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'users', - condition: { - id: 5 - }, - modifier: { - role: 'admin' - age: 33 - } -}); - -sql.query -// update users set role = $p1, age = 33 where id = 5; - -sql.values -// { p1: 'admin' } -``` - -__Remove:__ - -``` js -var sql = jsonSql.build({ - type: 'remove', - table: 'users', - condition: { - id: 5 - } -}); - -sql.query -// delete from users where id = 5; - -sql.values -// {} -``` - -For more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests). - -## Tests - -Clone repository from github, `cd` into cloned dir and install dev dependencies: - -``` bash -$ npm install -``` - -Then run tests with command: - -``` bash -$ gulp test -``` - -Or run tests coverage with command: - -``` bash -$ gulp coverage -``` - -## License - -[MIT](./LICENSE) diff --git a/legacy/json-sql/docs/README.md b/legacy/json-sql/docs/README.md deleted file mode 100644 index 8752f703..00000000 --- a/legacy/json-sql/docs/README.md +++ /dev/null @@ -1,1180 +0,0 @@ -# Documentation - -## Table of contents - -* [API](#api) - - [Initialization](#initialization) - - __[build(query)](#buildquery)__ - - [configure(options)](#configureoptions) - - [setDialect(name)](#setdialectname) -* __[Queries](#queries)__ - - [type: 'create'](#type-create) - - [type: 'select'](#type-select) - - [type: 'insert'](#type-insert) - - [type: 'update'](#type-update) - - [type: 'remove'](#type-remove) - - [type: 'union' | 'intersect' | 'except'](#type-union--intersect--except) -* __[Blocks](#blocks)__ -* __[Condition operators](#condition-operators)__ - ---- - -## API - -### Initialization - -To create new instance of json-sql builder you can use factory function: - -``` js -var jsonSql = require('json-sql')(options); -``` - -or create instance by class constructor: - -``` js -var jsonSql = new (require('json-sql').Builder)(options); -``` - -`options` are similar to [configure method options](#available-options). - ---- - -### build(query) - -Create sql query from mongo-style query object. - -`query` is a json object that has required property `type` and a set of query-specific properties. `type` property determines the type of query. List of available values of `type` property you can see at [Queries section](#queries). - -Returns object with properties: - -| Property | Description | -| -------- | ----------- | -| `query` | SQL query string | -| `value` | Array or object with values.
Exists only if `separatedValues = true`. | -| `prefixValues()` | Method to get values with `valuesPrefix`.
Exists only if `separatedValues = true`. | -| `getValuesArray()` | Method to get values as array.
Exists only if `separatedValues = true`. | -| `getValuesObject()` | Method to get values as object.
Exists only if `separatedValues = true`. | - ---- - -### configure(options) - -Set options of json-sql builder instance. - -#### Available options - -| Option name | Default value | Description | -| ----------- | ------------- | ----------- | -| `separatedValues` | `true` | If `true` - create placeholder for each string value and put it value to result `values`.
If `false` - put string values into sql query without placeholder (potential threat of sql injection). | -| `namedValues` | `true` | If `true` - create hash of values with placeholders p1, p2, ...
If `false` - put all values into array.
Option is used if `separatedValues = true`. | -| `valuesPrefix` | `'$'` | Prefix for values placeholders
Option is used if `namedValues = true`. | -| `dialect` | `'base'` | Active dialect. See setDialect for dialects list. | -| `wrappedIdentifiers` | `true` | If `true` - wrap all identifiers with dialect wrapper (name -> "name"). | - ---- - -### setDialect(name) - -Set active dialect, name can has value `'base'`, `'mssql'`, `'mysql'`, `'postrgresql'` or `'sqlite'`. - ---- - -## Queries - -### type: 'create' - ->[ [tableFields](#tableFields) ]
->[ [table](#table) ]
->[ [foreignKeys](#foreignKeys) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - unique: true, - default: "empty" - } - ] -}); - -sql.query -// create table if not exists "users"("name" varchar(16) NOT NULL default "empty" UNIQUE); -``` - ---- - -### type: 'select' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[ [distinct](#distinct) ]
->[ [fields](#fields) ]
->[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
->[ [alias](#alias) ]
->[ [join](#join) ]
->[ [condition](#condition) ]
->[ [group](#group) ]
->[ [sort](#sort) ]
->[ [limit](#limit) ]
->[ [offset](#offset) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'select', - fields: ['a', 'b'] - table: 'table' -}); - -sql.query -// select "a", "b" from "table"; -``` - -If `fields` is not specified in query, result fields is `*` (all columns of the selected rows). - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'select', - table: 'table' -}); - -sql.query -// select * from "table"; -``` - ---- - -### type: 'insert' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[ [or](#or) ]
->[table](#table)
->[values](#values)
->[ [condition](#condition) ]
->[ [returning](#returning) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: {a: 4} -}); - -sql.query -// insert into "table" ("a") values (4); -``` - ---- - -### type: 'update' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[ [or](#or) ]
->[table](#table)
->[modifier](#modifier)
->[ [condition](#condition) ]
->[ [returning](#returning) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'table', - modifier: {a: 5} -}); - -sql.query -// update "table" set a = 5; -``` - ---- - -### type: 'remove' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[table](#table)
->[ [condition](#condition) ]
->[ [returning](#returning) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'remove', - table: 'table' -}); - -sql.query -// delete from "table"; -``` - ---- - -### type: 'union' | 'intersect' | 'except' - ->[ [all](#all) ]
->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[queries](#queries)
->[ [sort](#sort) ]
->[ [limit](#limit) ]
->[ [offset](#offset) ] - -__`type: 'union'` example:__ - -``` js -var sql = jsonSql.build({ - type: 'union', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") union (select * from "table2"); -``` - -__`type: 'intersect'` example:__ - -``` js -var sql = jsonSql.build({ - type: 'intersect', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") intersect (select * from "table2"); -``` - -__`type: 'except'` example:__ - -``` js -var sql = jsonSql.build({ - type: 'except', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") except (select * from "table2"); -``` - ---- - -## Blocks - -Blocks are small chunks of query. - -### with, withRecursive - -Should be an `array` or an `object`. - -If value is an `array`, each item of array should be an `object` and should conform the scheme: - ->name
->[ [fields](#fields) ]
->[query](#query) | [select](#select) | [expression](#expression) - -__Example:__ - -``` js -var sql = jsonSql.build({ - 'with': [{ - name: 'table', - select: {table: 'withTable'} - }], - table: 'table' -}); - -sql.query -// with "table" as (select * from "withTable") select * from "table"; -``` - -If value is an `object`, keys of object interpret as names and each value should be an `object` and should conform the scheme: - ->[ name ]
->[ [fields](#fields) ]
->[query](#query) | [select](#select) | [expression](#expression) - -__Example:__ - -``` js -var sql = jsonSql.build({ - 'with': { - table: { - select: {table: 'withTable'} - } - }, - table: 'table' -}); - -sql.query -// with "table" as (select * from "withTable") select * from "table"; -``` - ---- - -### distinct - -Should be a `boolean`: - -``` -distinct: true -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - distinct: true, - table: 'table' -}); - -sql.query -// select distinct * from "table"; -``` - ---- - -### tableFields - -Should be an `array` - -Contains array of table`s fields - ---- - -### foreignKeys - -Should be an `array` - -__Example:__ - -``` js -var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ], - foreignKeys: [ - { - field: "name", - table: "person", - table_field: "id" - } - ] -}); - -sql.query -// create table "users"(name varchar(16) NOT NULL PRIMARY KEY,age int NOT NULL, FOREIGN KEY (name) REFERENCES person(id)); -``` - ---- - -### fields - -Should be an `array` or an `object`. - -If value is an `array`, each item interprets as [term block](#term). - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [ - 'a', - {b: 'c'}, - {table: 'd', name: 'e', alias: 'f'}, - ['g'] - ], - table: 'table' -}); - -sql.query -// select "a", "b" as "c", "d"."e" as "f", "g" from "table"; -``` - -If value is an `object`, keys of object interpret as field names and each value should be an `object` and should conform the scheme: - ->[ name ]
->[ [table](#table) ]
->[ cast ]
->[ [alias](#alias) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: { - a: 'b', - d: {table: 'c', alias: 'e'} - }, - table: 'table' -}); - -sql.query -// select "a" as "b", "c"."d" as "e" from "table"; -``` - ---- - -### term - -Should be: -* a `string` - interprets as field name; -* another simple type or an `array` - interprets as value; -* an `object` - should conform the scheme: - ->[query](#query) | [select](#select) | [field](#field) | [value](#value) | [func](#func) | [expression](#expression)
->[ cast ]
->[ [alias](#alias) ] - ---- - -### field - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -field: 'fieldName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{field: 'a'}], - table: 'table' -}); - -sql.query -// select "a" from "table"; -``` - -If value is an `object` it should conform the scheme: - ->name
->[ [table](#table) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{field: {name: 'a', table: 'table'}}], - table: 'table' -}); - -sql.query -// select "table"."a" from "table"; -``` - ---- - -### value - -Can have any type. - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [ - {value: 5}, - {value: 'test'} - ], - table: 'table' -}); - -sql.query -// select 5, $p1 from "table"; - -sql.values -// {p1: 'test'} -``` - ---- - -### table - -Should be a `string`: - -``` -table: 'tableName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table' -}); - -sql.query -// select * from "table"; -``` - ---- - -### query - -Should be an `object`. Value interprets as sub-query and process recursively with [build(query)](#buildquery) method. - -__Example:__ - -``` js -var sql = jsonSql.build({ - query: {type: 'select', table: 'table'} -}); - -sql.query -// select * from (select * from "table"); -``` - ---- - -### select - -Should be an `object`. Value interprets as sub-select and process recursively with [build(query)](#buildquery) method. - -__Example:__ - -``` js -var sql = jsonSql.build({ - select: {table: 'table'} -}); - -sql.query -// select * from (select * from "table"); -``` - ---- - -### func - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -func: 'random' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{func: 'random'}], - table: 'table' -}); - -sql.query -// select random() from "table"; -``` - -If value is an `object` it should conform the scheme: - ->name
->[ args ] - -where `name` is a `string` name of function, `args` is an `array` that contains it arguments. - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{ - func: { - name: 'sum', - args: [{field: 'a'}] - } - }], - table: 'table' -}); - -sql.query -// select sum("a") from table; -``` - ---- - -### expression - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -expression: 'random()' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - expression: 'generate_series(2, 4)' -}); - -sql.query -// select * from generate_series(2, 4); -``` - -If value is an `object` it should conform the scheme: - ->pattern
->[ values ] - -where `pattern` is a `string` pattern with placeholders `{placeholderName}`, `values` is a hash that contains values for each `placeholderName`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - expression: { - pattern: 'generate_series({start}, {stop})', - values: {start: 2, stop: 4} - } -}); - -sql.query -// select * from generate_series(2, 4); -``` - ---- - -### alias - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -alias: 'aliasName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - alias: 'alias' -}); - -sql.query -// select * from "table" as "alias"; -``` - -If value is an `object` it should conform the scheme: - ->name
->[ columns ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - alias: {name: 'alias'} -}); - -sql.query -// select * from "table" as "alias"; -``` - ---- - -### join - -Should be an `array` or an `object`. - -If value is an `array`, each item of array should be an `object` and should conform the scheme: - ->[ type ]
->[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
->[ [alias](#alias) ]
->[ on ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - join: [{ - type: 'right', - table: 'joinTable', - on: {'table.a': 'joinTable.b'} - }] -}); - -sql.query -// select * from "table" right join "joinTable" on "table"."a" = "joinTable"."b"; -``` - -If value is an `object`, keys of object interpret as table names and each value should be an `object` and should conform the scheme: - ->[ type ]
->[ [table](#table) | [query](#query) | [select](#select) | [expression](#expression) ]
->[ [alias](#alias) ]
->[ on ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - join: { - joinTable: { - type: 'inner', - on: {'table.a': 'joinTable.b'} - } - }] -}); - -sql.query -// select * from "table" inner join "joinTable" on "table"."a" = "joinTable"."b"; -``` - -__Join with sub-select example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - join: [{ - select: {table: 'joinTable'}, - alias: 'joinTable', - on: {'table.a': 'joinTable.b'} - }] -}); - -sql.query -// select * from "table" join (select * from "joinTable") as "joinTable" on "table"."a" = "joinTable"."b"; -``` - ---- - -### condition - -Should be an `array` or an `object`. - -__`array` example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - condition: [ - {a: {$gt: 1}}, - {b: {$lt: 10}} - ] -}); - -sql.query -// select * from "table" where "a" > 1 and "b" < 10; -``` - -__`object` example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - condition: { - a: {$gt: 1}, - b: {$lt: 10} - } -}); - -sql.query -// select * from "table" where "a" > 1 and "b" < 10; -``` - ---- - -### group - -Should be a `string` or an `array`. - -If value is a `string`: - -``` -group: 'fieldName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - group: 'a' -}); - -sql.query -// select * from "table" group by "a"; -``` - -If value is an `array`: - -``` -group: ['fieldName1', 'fieldName2'] -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - group: ['a', 'b'] -}); - -sql.query -// select * from "table" group by "a", "b"; -``` - ---- - -### sort - -Should be a `string`, an `array` or an `object`. - -If value is a `string`: - -``` -sort: 'fieldName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - sort: 'a' -}); - -sql.query -// select * from "table" order by "a"; -``` - -If value is an `array`: - -``` -sort: ['fieldName1', 'fieldName2'] -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - sort: ['a', 'b'] -}); - -sql.query -// select * from "table" order by "a", "b"; -``` - -If value is an `object`: - -``` -sort: { - fieldName1: 1, - fieldName2: -1 -} -``` - -__Example__: - -``` js -var sql = jsonSql.build({ - table: 'table', - sort: {a: 1, b: -1} -}); - -sql.query -// select * from "table" order by "a" asc, "b" desc; -``` - ---- - -### limit - -Should be a `number`. - -``` -limit: limitValue -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - limit: 5 -}); - -sql.query -// select * from "table" limit 5; -``` - ---- - -### offset - -Should be a `number`. - -``` -offset: offsetValue -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - offset: 5 -}); - -sql.query -// select * from "table" offset 5; -``` - ---- - -### or - -Should be a `string`. - -Available values: 'rollback', 'abort', 'replace', 'fail', 'ignore'. - -``` -or: 'orValue' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - or: 'replace', - table: 'table', - values: {a: 5} -}); - -sql.query -// insert or replace into "table" ("a") values (5); -``` - ---- - -### values - -Should be an `array` or an `object`. - -If value is an `array`, each item should be an `object` and interprets as single inserted row where keys are field names and corresponding values are field values. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: [ - {a: 5, b: 'text1'}, - {a: 6, b: 'text2'} - ] -}); - -sql.query -// insert into "table" ("a", "b") values (5, $p1), (6, $p2); - -sql.values -// {p1: 'text1', p2: 'text2'} -``` - -If value is an `object`, it interprets as single inserted row where keys are field names and corresponding values are field values. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: {a: 5, b: 'text'} -}); - -sql.query -// insert into "table" ("a", "b") values (5, $p1); - -sql.values -// {p1: 'text'} -``` - -Also you can specify fields array. If there no key in value object it value is `null`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - fields: ['a', 'b', 'c'], - values: {c: 'text', b: 5} -}); - -sql.query -// insert into "table" ("a", "b", "c") values (null, 5, $p1); - -sql.values -// {p1: 'text'} -``` - ---- - -### modifier - -Should be an `object`. - -You can specify modifier operator. -Available operators: `$set`, `$inc`, `$dec`, `$mul`, `$div`, `$default`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'table', - modifier: { - $set: {a: 5}, - $default: {b: true}, - $inc: {c: 10} - } -}); - -sql.query -// update "table" set "a" = 5, "b" = default, "c" = "c" + 10; -``` - -If modifier operator is not specified it uses default operator `$set`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'table', - modifier: {a: 5} -}); - -sql.query -// update "table" set "a" = 5; -``` - ---- - -### returning - -Format is similar to [fields](#fields) block. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: {a: 5}, - returning: ['a'] -}); - -sql.query -// insert into "table" ("a") values (5) returning "a"; -``` - ---- - -### all - -Should be a `boolean`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'union', - all: true, - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") union all (select * from "table2"); -``` - ---- - -### queries - -Should be an `array` with minimum 2 items. Each item interprets as sub-query and process recursively with [build(query)](#buildquery) method. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'union', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") union (select * from "table2"); - -//or for sqlite3 -jsonSql.setDialect("sqlite"); -var sql = jsonSql.build({ - type: 'union', - unionqueries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// select * from "table1" union select * from "table2"; -``` - ---- - -## Condition operators - -TODO: write this section \ No newline at end of file diff --git a/legacy/json-sql/gulpfile.js b/legacy/json-sql/gulpfile.js deleted file mode 100644 index c7bbf59b..00000000 --- a/legacy/json-sql/gulpfile.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -var gulp = require('gulp'); -var mocha = require('gulp-mocha'); -var jshint = require('gulp-jshint'); - -gulp.task('test', function() { - return gulp.src(['./tests/*.js'], {read: false}) - .pipe( - mocha({ - reporter: 'spec', - bail: true - }) - ); -}); - -gulp.task('lint', function() { - return gulp.src('./lib/**/*.js') - .pipe(jshint()) - .pipe(jshint.reporter('unix')); -}); - -gulp.task('lintTests', function() { - return gulp.src('./tests/*.js') - .pipe(jshint()) - .pipe(jshint.reporter('unix')); -}); \ No newline at end of file diff --git a/legacy/json-sql/lib/builder.js b/legacy/json-sql/lib/builder.js deleted file mode 100644 index fa0d2a0d..00000000 --- a/legacy/json-sql/lib/builder.js +++ /dev/null @@ -1,233 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -var dialects = { - base: require('./dialects/base'), - mssql: require('./dialects/mssql'), - postgresql: require('./dialects/postgresql'), - sqlite: require('./dialects/sqlite') -}; - -var blockRegExp = /\{([a-z0-9]+)\}(.|$)/ig; - -var Builder = module.exports = function (options) { - this.configure(options); -}; - -Builder.prototype._reset = function () { - if (this.options.separatedValues) { - this._placeholderId = 1; - this._values = this.options.namedValues ? {} : []; - } else { - delete this._placeholderId; - delete this._values; - } - - this._query = ''; -}; - -Builder.prototype._getPlaceholder = function () { - return (this.options.namedValues ? 'p' : '') + (this._placeholderId++); -}; - -Builder.prototype._wrapPlaceholder = function (name) { - return this.options.valuesPrefix + '{' + name + '}'; -}; - -Builder.prototype._pushValue = function (value) { - var valueType = typeof value; - - if (valueType === 'undefined') { - return 'null'; - } else if (value === null || valueType === 'number' || - valueType === 'boolean') { - return String(value); - } else if (valueType === 'string') { - if (this.options.separatedValues) { - var placeholder = this._getPlaceholder(); - if (this.options.namedValues) { - this._values[placeholder] = value; - } else { - this._values.push(value); - } - return this._wrapPlaceholder(placeholder); - } else { - return '\'' + value + '\''; - } - } else if (valueType == 'object') { - if (Object.prototype.toString.call(value) == "[object Array]") { - if (value.length > 0) { - return value.join(",") - } else { - return 'null'; - } - } - else if (Buffer.isBuffer(value)) { - var placeholder = this._getPlaceholder(); - this._values[placeholder] = value; - return this._wrapPlaceholder(placeholder); - //return "X'" + value.toString('hex') + "'"; - } else { - var placeholder = this._getPlaceholder(); - this._values[placeholder] = value; - return this._wrapPlaceholder(placeholder); - //return ("'" + JSON.stringify(value).replace(/'/g, "''") + "'"); - } - } else { - throw new Error('Wrong value type "' + valueType + '"'); - } -}; - -Builder.prototype.configure = function (options) { - options = options || {}; - - this.options = _(options).defaults({ - separatedValues: true, - namedValues: true, - valuesPrefix: '$', - dialect: 'base', - wrappedIdentifiers: true - }); - - this.setDialect(this.options.dialect); - - this._reset(); -}; - -Builder.prototype.buildCondition = function (params) { - var self = this; - var result = ''; - - var condition = params.condition; - var logicalOperator = params.logicalOperator || - this.dialect.config.defaultLogicalOperator; - - if (_.isObject(condition) && !_.isEmpty(condition)) { - // if condition is array: [{a: 1}, {b: 2}] - if (_.isArray(condition)) { - result = _(condition).map(function (item) { - return self.buildCondition({ - condition: item, - operator: params.operator - }); - }); - - // if condition is object - } else { - result = _(condition).map(function (value, field) { - // if field name is operator - if (field[0] === '$') { - // if field name is logical operator: {$logicalOperator: {a: 1}} - if (!self.dialect.logicalOperators.has(field)) { - throw new Error('Unknown logical operator "' + field + '"'); - } - - return self.buildCondition({ - condition: value, - logicalOperator: field, - operator: params.operator - }); - - // if condition item is object: {a: {$operator: 4}} - } else if (_.isObject(value)) { - var logicalOperatorFn = self.dialect.logicalOperators - .get(self.dialect.config.defaultLogicalOperator); - - return logicalOperatorFn(_(value).map(function (operatorValue, operator) { - var operatorFn = self.dialect.conditions.get(operator); - if (!operatorFn) { - throw new Error('Unknown operator "' + operator + '"'); - } - return operatorFn(field, operator, operatorValue); - })); - - // if condition item type is simple: {a: 1, b: 2} - } else { - var operatorFn = self.dialect.conditions.get(params.operator); - if (!operatorFn) { - throw new Error('Unknown operator "' + params.operator + '"'); - } - return operatorFn(field, params.operator, value); - } - }); - } - - result = this.dialect.logicalOperators - .get(logicalOperator)(_(result).compact()); - } - - return result; -}; - -Builder.prototype.buildBlock = function (block, params) { - var blockFn = this.dialect.blocks.get(block); - - if (!blockFn) { - throw new Error('Unknown block "' + block + '"'); - } - - return blockFn(params); -}; - -Builder.prototype.buildTemplate = function (type, params) { - var self = this; - - var template = this.dialect.templates.get(type); - if (!template) { - throw new Error('Unknown template type "' + type + '"'); - } - - params = _({}).defaults(params, template.defaults); - - if (template.validate) { - template.validate(type, params); - } - - return template.pattern.replace(blockRegExp, function (fullMatch, block, space) { - if (typeof params[block] !== 'undefined') { - return self.buildBlock(block, params) + space; - } else { - return ''; - } - }).trim(); -}; - -Builder.prototype.build = function (params) { - var builder = this; - - this._reset(); - - this._query = this.buildTemplate('query', {queryBody: params}) + ';'; - - if (this.options.separatedValues) { - return { - query: this._query, - values: this._values, - prefixValues: function () { - var values = {}; - _(this.getValuesObject()).each(function (value, name) { - values[builder._wrapPlaceholder(name)] = value; - }); - return values; - }, - getValuesArray: function () { - return _.isArray(this.values) ? this.values : _(this.values).values(); - }, - getValuesObject: function () { - return _.isArray(this.values) ? _(_.range(1, this.values.length + 1)).object(this.values) : - this.values; - } - }; - } else { - return {query: this._query}; - } -}; - -Builder.prototype.setDialect = function (name) { - if (!dialects[name]) { - throw new Error('Unknown dialect "' + name + '"'); - } - - this.dialect = new (dialects[name])(this); -}; diff --git a/legacy/json-sql/lib/dialects/base/blocks.js b/legacy/json-sql/lib/dialects/base/blocks.js deleted file mode 100644 index 809c5fd6..00000000 --- a/legacy/json-sql/lib/dialects/base/blocks.js +++ /dev/null @@ -1,441 +0,0 @@ -'use strict'; - -var _ = require('underscore'); -var scheme = require('./scheme.js'); - -function removeTopBrackets(condition) { - if (condition.length && condition[0] === '(' && - condition[condition.length - 1] === ')') { - condition = condition.slice(1, condition.length - 1); - } - - return condition; -} - -module.exports = function(dialect) { - dialect.blocks.add('distinct', function() { - return 'distinct'; - }); - - - dialect.blocks.add('field', function(params) { - var field = params.field || params.$field; - var expression = params.expression || params.$expression; - - if (!field && !expression) { - throw new Error('Neither `field` nor `expression` properties aren\'t set'); - } - - if (field) { - field = this.dialect._wrapIdentifier(field); - - var table = params.table || params.$table; - if (table) { - field = this.buildBlock('table', {table: table}) + '.' + field; - } - - if (expression) { - if (!_.isArray(expression)) expression = [expression]; - var exprSuffix = (new Array(expression.length + 1)).join(')'); - field = expression.join('(') + '(' + field + exprSuffix; - } - } else { - field = expression; - } - - var cast = params.cast || params.$cast; - if (cast) { - field = 'cast(' + field + ' as ' + cast + ')'; - } - - var alias = params.alias || params.$alias; - if (alias) { - field += ' ' + this.buildBlock('alias', {alias: alias}); - } - - return field; - }); - - - dialect.blocks.add('fields', function(params) { - var self = this; - - var fields = params.fields || {}; - var result = ''; - - if (!_.isObject(fields) && !_.isString(fields)) { - throw new Error('Invalid `fields` property type "' + (typeof fields) + '"'); - } - - if (_.isObject(fields)) { - if (_.isEmpty(fields)) { - result = '*'; - } else { - // If fields is array: ['a', {b: 'c'}, {field: '', table: 't', alias: 'r'}] - if (_.isArray(fields)) { - result = _(fields).map(function(item) { - - if (_.isObject(item)) { - // if field is field object: {field: '', table: 't', alias: 'r'} - if (item.field || item.expression) { - return self.buildBlock('field', item); - - // if field is non-field object: {b: 'c'} - } else { - return self.buildBlock('fields', {fields: item}); - } - - // if field is string: 'a' - } else if (_.isString(item)) { - return self.buildBlock('field', {field: item}); - } - }); - - // If fields is object: {a: 'u', b: {table: 't', alias: 'c'}} - } else { - // use keys as field names - result = _(fields).map(function(item, field) { - // b: {table: 't', alias: 'c'} - if (_.isObject(item)) { - if (!item.field) { - item = _.clone(item); - item.field = field; - } - - return self.buildBlock('field', item); - - // a: 'u' - } else if (_.isString(item)) { - return self.buildBlock('field', { - field: field, - alias: item - }); - } - - return ''; - }); - } - - result = result.join(', '); - } - } else { - result = fields; - } - - return result; - }); - - dialect.blocks.add('table', function(params) { - return this.dialect._wrapIdentifier(params.table); - }); - - dialect.blocks.add('expression', function(params) { - return params.expression; - }); - - dialect.blocks.add('name', function(params) { - return this.dialect._wrapIdentifier(params.name); - }); - - dialect.blocks.add('alias', function(params) { - var self = this; - - var result; - - if (_.isObject(params.alias)) { - if (!params.alias.name) { - throw new Error('Alias `name` property is required'); - } - - result = this.dialect._wrapIdentifier(params.alias.name); - - if (_.isArray(params.alias.columns)) { - result += '(' + _(params.alias.columns).map(function(column) { - return self.dialect._wrapIdentifier(column); - }).join(', ') + ')'; - } - } else { - result = this.dialect._wrapIdentifier(params.alias); - } - - return 'as ' + result; - }); - - dialect.blocks.add('condition', function(params) { - var condition = params.condition; - var result = ''; - - if (_.isObject(condition)) { - result = this.buildCondition({ - condition: condition, - operator: '$eq' - }); - } else if (_.isString(condition)) { - result = condition; - } - - if (result) { - result = 'where ' + removeTopBrackets(result); - } - - return result; - }); - - dialect.blocks.add('modifier', function(params) { - var self = this; - - var modifier = params.modifier; - var result = ''; - - // if modifier is object -> call method for each operator - if (_.isObject(modifier)) { - result = _(modifier).map(function(values, field) { - var modifierFn = self.dialect.modifiers.get(field); - var methodParams = values; - - if (!modifierFn) { - modifierFn = self.dialect.modifiers.get(self.dialect.config.defaultModifier); - methodParams = {}; - methodParams[field] = values; - } - - return modifierFn.call(self, methodParams); - }).join(', '); - - // if modifier is string -> not process it - } else if (_.isString(modifier)) { - result = modifier; - } - - if (result) { - result = 'set ' + result; - } - - return result; - }); - - dialect.blocks.add('join', function(params) { - var self = this; - - var join = params.join; - var result = ''; - - // if join is array -> make each joinItem - if (_.isArray(join)) { - result = _(join).map(function(joinItem) { - return self.buildTemplate('joinItem', joinItem); - }).join(' '); - - // if join is object -> set table name from key and make each joinItem - } else if (_.isObject(join)) { - result = _(join).map(function(joinItem, table) { - if (!joinItem.table && !joinItem.query && !joinItem.select) { - joinItem = _.clone(joinItem); - joinItem.table = table; - } - - return self.buildTemplate('joinItem', joinItem); - }).join(' '); - - // if join is string -> not process - } else if (_.isString(join)) { - result = join; - } - - return result; - }); - - dialect.blocks.add('type', function(params) { - return params.type.toLowerCase(); - }); - - - dialect.blocks.add('on', function(params) { - var on = params.on; - var result = ''; - - // `on` block is use `$field` as default compare operator - // because it most used case - if (_.isObject(on)) { - result = this.buildCondition({ - condition: on, - operator: '$field' - }); - } else if (_.isString(on)) { - result = on; - } - - if (result) { - result = 'on ' + removeTopBrackets(result); - } - - return result; - }); - - - dialect.blocks.add('group', function(params) { - var result = ''; - var group = params.group; - - if (_.isArray(group)) { - var self = this; - result = _(group).map(function(field) { - return self.dialect._wrapIdentifier(field); - }).join(', '); - } else if (_.isString(group)) { - result = this.dialect._wrapIdentifier(group); - } else if (_.isObject(group)) { - result = group.expression + (group.having ? " having " + this.buildCondition({ - condition: group.having - }) : ""); - } - - if (result) { - result = 'group by ' + result; - } - - return result; - }); - - - dialect.blocks.add('sort', function(params) { - var result = ''; - var sort = params.sort; - - // if sort is array -> field1, field2, ... - var self = this; - if (_.isArray(sort)) { - result = _(sort).map(function(sortField) { - return self.dialect._wrapIdentifier(sortField); - }).join(', '); - - // if sort is object -> field1 asc, field2 desc, ... - } else if (_.isObject(sort)) { - result = _(sort).map(function(direction, field) { - return self.dialect._wrapIdentifier(field) + ' ' + - (direction > 0 ? 'asc' : 'desc'); - }).join(', '); - - // if sort is string -> not process - } else if (_.isString(sort)) { - result = this.dialect._wrapIdentifier(sort); - } - - if (result) { - result = 'order by ' + result; - } - - return result; - }); - - - dialect.blocks.add('limit', function(params) { - return 'limit ' + this._pushValue(params.limit); - }); - - - dialect.blocks.add('offset', function(params) { - return 'offset ' + this._pushValue(params.offset); - }); - - - dialect.blocks.add('or', function(params) { - return 'or ' + params.or; - }); - - - dialect.blocks.add('values', function(params) { - var self = this; - - var fieldValues = params.values; - - if (!_.isArray(fieldValues)) { - fieldValues = [fieldValues]; - } - - var fields = params.fields || _(fieldValues).chain().map(function(values) { - return _(values).keys(); - }).flatten().uniq().value(); - - fieldValues = _(fieldValues).map(function(values) { - return _(fields).map(function(field) { - return self._pushValue(values[field]); - }); - }); - - return this.buildTemplate('insertValues', { - fields: fields, - fieldValues: fieldValues - }); - }); - - dialect.blocks.add('fieldValues', function(params) { - return _(params.fieldValues).map(function(values) { - return '(' + values.join(', ') + ')'; - }).join(', '); - }); - - dialect.blocks.add('queryBody', function(params) { - var query = params.queryBody || {}; - - return this.buildTemplate(query.type || 'select', query); - }); - - dialect.blocks.add('query', function(params) { - return this.buildTemplate('subQuery', {queryBody: params.query}); - }); - - dialect.blocks.add('select', function(params) { - return this.buildTemplate('subQuery', {queryBody: params.select}); - }); - - dialect.blocks.add('tableFields', function (params) { - return scheme.parse.call(this, params.tableFields, params.foreignKeys); - }); - - dialect.blocks.add('queries', function(params) { - var self = this; - - return _(params.queries).map(function(query) { - return self.buildTemplate('subQuery', {queryBody: query}); - }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); - }); - - dialect.blocks.add('indexOn', function (params) { - return params.indexOn; - }); - - dialect.blocks.add('with', function(params) { - var self = this; - - var withList = params['with']; - var result = ''; - - // if with clause is array -> make each withItem - if (_.isArray(withList)) { - result = _(withList).map(function(withItem) { - return self.buildTemplate('withItem', withItem); - }).join(', '); - - // if with clause is object -> set name from key and make each withItem - } else if (_.isObject(withList)) { - result = _(withList).map(function(withItem, name) { - if (!withItem.name) { - withItem = _.clone(withItem); - withItem.name = name; - } - return self.buildTemplate('withItem', withItem); - }).join(', '); - - // if with clause is string -> not process - } else if (_.isString(withList)) { - result = withList; - } - - return 'with ' + result; - }); - - dialect.blocks.add('returning', function(params) { - return 'returning ' + this.buildBlock('fields', {fields: params.returning}); - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/conditions.js b/legacy/json-sql/lib/dialects/base/conditions.js deleted file mode 100644 index c46be4f3..00000000 --- a/legacy/json-sql/lib/dialects/base/conditions.js +++ /dev/null @@ -1,122 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -// Compare conditions (e.g. $eq, $gt) -function buildCompareCondition(builder, field, operator, value) { - var placeholder; - - // if value is object, than make field block from it - if (value && _.isObject(value)) { - placeholder = builder.buildBlock('field', value); - } else { - // if value is simple - create placeholder for it - placeholder = builder._pushValue(value); - } - - field = builder.dialect._wrapIdentifier(field); - return [field, operator, placeholder].join(' '); -} - -// Contain conditions ($in/$nin) -function buildContainsCondition(builder, field, operator, value) { - var newValue; - - if (_.isArray(value)) { - if (!value.length) value = [null]; - - newValue = '(' + _(value).map(function(item) { - return builder._pushValue(item); - }).join(', ') + ')'; - } else if (_.isObject(value)) { - newValue = builder.buildTemplate('subQuery', {queryBody: value}); - } else { - throw new Error('Invalid `' + operator + '` value type "' + (typeof value) + '"'); - } - - field = builder.dialect._wrapIdentifier(field); - return [field, operator, newValue].join(' '); -} - -module.exports = function(dialect) { - dialect.conditions.add('$eq', function(field, operator, value) { - return buildCompareCondition(this, field, '=', value); - }); - - dialect.conditions.add('$ne', function(field, operator, value) { - return buildCompareCondition(this, field, '!=', value); - }); - - dialect.conditions.add('$gt', function(field, operator, value) { - return buildCompareCondition(this, field, '>', value); - }); - - dialect.conditions.add('$lt', function(field, operator, value) { - return buildCompareCondition(this, field, '<', value); - }); - - dialect.conditions.add('$gte', function(field, operator, value) { - return buildCompareCondition(this, field, '>=', value); - }); - - dialect.conditions.add('$lte', function(field, operator, value) { - return buildCompareCondition(this, field, '<=', value); - }); - - dialect.conditions.add('$is', function(field, operator, value) { - return buildCompareCondition(this, field, 'is', value); - }); - - dialect.conditions.add('$isnot', function(field, operator, value) { - return buildCompareCondition(this, field, 'is not', value); - }); - - dialect.conditions.add('$like', function(field, operator, value) { - return buildCompareCondition(this, field, 'like', value); - }); - - dialect.conditions.add('$null', function(field, operator, value) { - return buildCompareCondition(this, field, 'is' + (value ? '' : ' not'), null); - }); - - dialect.conditions.add('$field', function(field, operator, value) { - var placeholder; - - // if value is object, than make field block from it - if (_.isObject(value)) { - placeholder = this.buildBlock('field', value); - } else { - // $field - special operator, that not make placeholder for value - placeholder = this.dialect._wrapIdentifier(value); - } - - return [this.dialect._wrapIdentifier(field), '=', placeholder].join(' '); - }); - - - dialect.conditions.add('$in', function(field, operator, value) { - return buildContainsCondition(this, field, 'in', value); - }); - - dialect.conditions.add('$nin', function(field, operator, value) { - return buildContainsCondition(this, field, 'not in', value); - }); - - dialect.conditions.add('$between', function(field, operator, value) { - if (!_.isArray(value)) { - throw new Error('Invalid `$between` value type "' + (typeof value) + '"'); - } - - if (value.length < 2) { - throw new Error('`$between` array length should be 2 or greater'); - } - - return [ - this.dialect._wrapIdentifier(field), - 'between', - this._pushValue(value[0]), - 'and', - this._pushValue(value[1]) - ].join(' '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/index.js b/legacy/json-sql/lib/dialects/base/index.js deleted file mode 100644 index 2a0e2c7a..00000000 --- a/legacy/json-sql/lib/dialects/base/index.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var _ = require('underscore'); -var ValuesStore = require('../../valuesStore'); - -var templatesInit = require('./templates'); -var blocksInit = require('./blocks'); -var conditionsInit = require('./conditions'); -var logicalOperatorsInit = require('./logicalOperators'); -var modifiersInit = require('./modifiers'); - -var Dialect = module.exports = function(builder) { - this.builder = builder; - - this.blocks = new ValuesStore({context: builder}); - this.modifiers = new ValuesStore({context: builder}); - this.conditions = new ValuesStore({context: builder}); - this.logicalOperators = new ValuesStore({context: builder}); - this.templates = new ValuesStore({context: builder}); - - templatesInit(this); - blocksInit(this); - conditionsInit(this); - modifiersInit(this); - logicalOperatorsInit(this); - - this.identifierPartsRegexp = new RegExp( - '(\\' + this.config.identifierPrefix + '[^\\' + this.config.identifierSuffix + ']*\\' + - this.config.identifierSuffix + '|[^\\.]+)', 'g' - ); - this.wrappedIdentifierPartRegexp = new RegExp( - '^\\' + this.config.identifierPrefix + '.*\\' + this.config.identifierSuffix + '$' - ); -}; - -Dialect.prototype.config = { - identifierPrefix: '"', - identifierSuffix: '"', - defaultLogicalOperator: '$and', - defaultModifier: '$set' -}; - -Dialect.prototype._wrapIdentifier = function(name) { - if (this.builder.options.wrappedIdentifiers) { - var self = this; - var nameParts = name.match(this.identifierPartsRegexp); - - return _(nameParts).map(function(namePart) { - if (namePart !== '*' && !self.wrappedIdentifierPartRegexp.test(namePart)) { - namePart = self.config.identifierPrefix + namePart + self.config.identifierSuffix; - } - - return namePart; - }).join('.'); - } - - return name; -}; diff --git a/legacy/json-sql/lib/dialects/base/logicalOperators.js b/legacy/json-sql/lib/dialects/base/logicalOperators.js deleted file mode 100644 index fb2879c1..00000000 --- a/legacy/json-sql/lib/dialects/base/logicalOperators.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -function buildJoinOperator(conditions, operator) { - var isBracketsNeeded = conditions.length > 1; - var result = conditions.join(' ' + operator + ' '); - - if (result && isBracketsNeeded) result = '(' + result + ')'; - - return result; -} - -module.exports = function(dialect) { - dialect.logicalOperators.add('$and', function(conditions) { - return buildJoinOperator(conditions, 'and'); - }); - - dialect.logicalOperators.add('$or', function(conditions) { - return buildJoinOperator(conditions, 'or'); - }); - - dialect.logicalOperators.add('$not', function(conditions) { - var result = ''; - - if (_.isArray(conditions)) { - result = dialect.logicalOperators - .get(dialect.config.defaultLogicalOperator)(conditions); - } else if (_.isString(conditions)) { - result = conditions; - } - - if (result) result = 'not ' + result; - - return result; - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/modifiers.js b/legacy/json-sql/lib/dialects/base/modifiers.js deleted file mode 100644 index 3be42aa6..00000000 --- a/legacy/json-sql/lib/dialects/base/modifiers.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - dialect.modifiers.add('$set', function(values) { - var self = this; - - return _(values).map(function(value, field) { - var placeholder = self._pushValue(value); - - return [self.dialect._wrapIdentifier(field), '=', placeholder].join(' '); - }).join(', '); - }); - - dialect.modifiers.add('$inc', function(values) { - var self = this; - - return _(values).map(function(value, field) { - var placeholder = self._pushValue(value); - field = self.dialect._wrapIdentifier(field); - - return [field, '=', field, '+', placeholder].join(' '); - }).join(', '); - }); - - dialect.modifiers.add('$dec', function(values) { - var self = this; - - return _(values).map(function(value, field) { - var placeholder = self._pushValue(value); - field = self.dialect._wrapIdentifier(field); - - return [field, '=', field, '-', placeholder].join(' '); - }).join(', '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/scheme.js b/legacy/json-sql/lib/dialects/base/scheme.js deleted file mode 100644 index 686e16ed..00000000 --- a/legacy/json-sql/lib/dialects/base/scheme.js +++ /dev/null @@ -1,146 +0,0 @@ -var SchemeParser = function () { -} - -var types = { - "Number": {syntax: "int"}, - "BigInt": {syntax: "bigint"}, - "SmallInt": {syntax: "smallint"}, - "String": {syntax: "varchar", length: true}, - "Text": {syntax: "text"}, - "Real": {syntax: "real"}, - "Boolean": {syntax: "boolean"}, - "Blob": {syntax: "blob"}, - "Binary": {syntax: "bytea"} -} - -// for future -var onDeleteTrigger = { - "set_null": "SET NULL", - "cascade": "CASCADE" -} - -function getType(field) { - var s = ""; - var type = types[field.type]; - - if (!type) { - throw new Error("Invalid type of field: " + field.type); - } - - s += type.syntax; - - if (type.length) { - if (!field.length || field.length <= 0) { - throw new Error("Field length can't be less or equal 0"); - } - - s += "(" + field.length + ")"; - } else if (type.default_length) { - s += "(" + type.default_length + ")"; - } - - return s; -} - -function foreignkeys(fields, keys) { - if (!keys || keys.length == 0) { - return ""; - } - - var s = ", "; - var names = []; - fields.forEach(function (field) { - names.push(field.name) - }); - - keys.forEach(function (key, i) { - if (!key.field) { - throw new Error("Provide field for foreign key"); - } - - if (names.indexOf(key.field) < 0) { - throw new Error("Not exists field to make foreign key: " + key.field); - } - - if (!key.table || key.table.trim().length == 0) { - throw new Error("Invalid reference table name"); - } - - if (!key.table_field || key.table_field.trim().length == 0) { - throw new Error("Invalid reference table filed"); - } - - s += "FOREIGN KEY (\"" + key.field + "\") REFERENCES " + key.table + "(\"" + key.table_field + "\")" + (key.on_delete ? " ON DELETE " + key.on_delete : ""); - if (i != keys.length - 1) { - s += ","; - } - }); - - return s; -} - -function parse(fields, fkeys) { - var sql_fields = ""; - var checkPrimaryKey = false; - var names = []; - - fields.forEach(function (field, i) { - if (!field.name) { - throw new Error("Name of field most be provided"); - } - - if (field.name.trim().length == 0) { - throw new Error("Name most contains characters"); - } - - if (names.indexOf(field.name) >= 0) { - throw new Error("Two parameters with same name: " + field.name); - } - - var line = this.dialect._wrapIdentifier(field.name) + " " + getType(field); - - if (field.not_null) { - line += " NOT NULL"; - } - - if (field.default !== undefined) { - var _type = typeof field.default; - var _default = field.default; - if (_type === "string") { - _default = "'" + field.default + "'"; - } - line += " default " + _default; - } - - if (field.unique) { - line += " UNIQUE"; - } - - - if (field.primary_key) { - if (checkPrimaryKey) { - throw new Error("Too much primary key '" + field.name + "' in table"); - } else { - checkPrimaryKey = true; - } - - line += " PRIMARY KEY"; - } - - sql_fields += line; - - names.push(field.name); - - if (i != fields.length - 1) { - sql_fields += ","; - } - }.bind(this)); - - sql_fields += foreignkeys(fields, fkeys); - return sql_fields; -} - -module.exports = { - parse: parse, - foreignkeys: foreignkeys -} diff --git a/legacy/json-sql/lib/dialects/base/templates.js b/legacy/json-sql/lib/dialects/base/templates.js deleted file mode 100644 index 81c60cdb..00000000 --- a/legacy/json-sql/lib/dialects/base/templates.js +++ /dev/null @@ -1,220 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - var availableSourceProps = ['table', 'query', 'select', 'expression']; - var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; - var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; - - // private templates - - dialect.templates.add('query', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - - dialect.templates.add('subQuery', { - pattern: '({queryBody})', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('create', { - pattern: 'create table if not exists {table}({tableFields})', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'tableFields'); - hasArrayProp(type, params, 'tableFields'); - } - }); - - dialect.templates.add('index', { - pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'name') - hasRequiredProp(type, params, 'indexOn'); - hasMinPropLength(type, params, 'name', 1); - hasMinPropLength(type, params, 'indexOn', 1); - } - }) - - - dialect.templates.add('queriesCombination', { - pattern: '{with} {queries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queries'); - hasArrayProp(type, params, 'queries'); - hasMinPropLength(type, params, 'queries', 2); - } - }); - - - dialect.templates.add('insertValues', { - pattern: '({fields}) values {fieldValues}', - validate: function(type, params) { - hasRequiredProp('values', params, 'fields'); - hasArrayProp('values', params, 'fields'); - hasMinPropLength('values', params, 'fields', 1); - - hasRequiredProp('values', params, 'fieldValues'); - hasArrayProp('values', params, 'fieldValues'); - hasMinPropLength('values', params, 'fieldValues', 1); - } - }); - - - dialect.templates.add('joinItem', { - pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', - validate: function(type, params) { - hasOneOfProps('join', params, availableSourceProps); - - if (params.type) { - hasStringProp('join', params, 'type'); - - var splitType = _(params.type.toLowerCase().split(' ')).compact(); - if (_.difference(splitType, availableJoinTypes).length) { - throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); - } - } - } - }); - - - dialect.templates.add('withItem', { - pattern: '{name} {fields} as {query} {select} {expression}', - validate: function(type, params) { - hasRequiredProp('with', params, 'name'); - hasOneOfProps('with', params, ['query', 'select', 'expression']); - } - }); - - - dialect.templates.add('fromItem', { - pattern: '{table} {query} {select} {expression}', - validate: function(type, params) { - hasOneOfProps('from', params, availableSourceProps); - } - }); - - - // public templates - - dialect.templates.add('select', { - pattern: '{with} select {distinct} {fields} ' + - 'from {from} {table} {query} {select} {expression} {alias} ' + - '{join} {condition} {group} {sort} {limit} {offset}', - defaults: { - fields: {} - }, - validate: function(type, params) { - hasOneOfProps(type, params, availableSourceProps); - } - }); - - - dialect.templates.add('insert', { - pattern: '{with} insert {or} into {table} {values} {returning} {condition}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('update', { - pattern: '{with} update {or} {table} {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'modifier'); - hasRequiredProp(type, params, 'table'); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('remove', { - pattern: '{with} delete from {table} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - } - }); - - - dialect.templates.add('union', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('except', dialect.templates.get('queriesCombination')); - - - // validation helpers - - function hasRequiredProp(type, params, propName) { - if (!params[propName]) { - throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); - } - } - - function hasObjectProp(type, params, propName) { - if (!_.isObject(params[propName])) { - throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); - } - } - - function hasArrayProp(type, params, propName) { - if (!_.isArray(params[propName])) { - throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); - } - } - - function hasStringProp(type, params, propName) { - if (!_.isString(params.type)) { - throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); - } - } - - function hasMinPropLength(type, params, propName, length) { - if (params[propName].length < length) { - throw new Error('`' + propName + '` property should not have length less than ' + length + - ' in `' + type + '` clause'); - } - } - - function hasOneOfProps(type, params, expectedPropNames) { - var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); - - if (!propNames.length) { - throw new Error('Neither `' + expectedPropNames.join('`, `') + - '` properties are not set in `' + type + '` clause'); - } - - if (propNames.length > 1) { - throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + - type + '` clause'); - } - } - - function matchesRegExpProp(type, params, propName, regExp) { - if (!params[propName].match(regExp)) { - throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + - type + '` clause'); - } - } -}; diff --git a/legacy/json-sql/lib/dialects/mssql/index.js b/legacy/json-sql/lib/dialects/mssql/index.js deleted file mode 100644 index 8374184f..00000000 --- a/legacy/json-sql/lib/dialects/mssql/index.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -var BaseDialect = require('../base'); -var _ = require('underscore'); -var util = require('util'); - -var Dialect = module.exports = function(builder) { - BaseDialect.call(this, builder); -}; - -util.inherits(Dialect, BaseDialect); - -Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config, {}); diff --git a/legacy/json-sql/lib/dialects/postgresql/blocks.js b/legacy/json-sql/lib/dialects/postgresql/blocks.js deleted file mode 100644 index e6f0066d..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/blocks.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - dialect.blocks.add('offset', function(params) { - var limit = ''; - - if (typeof params.limit === 'undefined') { - limit = this.buildBlock('limit', {limit: -1}) + ' '; - } - - return limit + 'offset ' + this._pushValue(params.offset); - }); - - dialect.blocks.add('unionqueries', function(params) { - var self = this; - - return _(params.unionqueries).map(function(query) { - return self.buildTemplate('subUnionQuery', {queryBody: query}); - }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); - }); - - dialect.blocks.add('conflictFields', function(params) { - var self = this; - - var fields = _(params.conflictFields).map(function(field) { - return self.dialect._wrapIdentifier(field); - }); - - return '(' + fields.join(',') + ')'; - }); -}; diff --git a/legacy/json-sql/lib/dialects/postgresql/conditions.js b/legacy/json-sql/lib/dialects/postgresql/conditions.js deleted file mode 100644 index dddba22a..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/conditions.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -var buildJsonInCondition = function(builder, field, operator, value) { - field = builder.dialect._wrapIdentifier(field); - - var placeholder; - try { - placeholder = builder.buildBlock('field', value); - } catch (e) { - placeholder = builder._pushValue(JSON.stringify(value)); - } - - return [field, operator, placeholder].join(' '); -}; - -var buildJsonHasCondition = function(builder, field, operator, value) { - field = builder.dialect._wrapIdentifier(field); - - var placeholder; - if (_(value).isArray()) { - placeholder = 'array[' + _(value).map(function(item) { - return builder._pushValue(item); - }).join(', ') + ']'; - } else { - placeholder = builder.buildBlock('field', value); - } - - return [field, operator, placeholder].join(' '); -}; - -module.exports = function(dialect) { - dialect.conditions.add('$jsonContains', function(field, operator, value) { - return buildJsonInCondition(this, field, '@>', value); - }); - - dialect.conditions.add('$jsonIn', function(field, operator, value) { - return buildJsonInCondition(this, field, '<@', value); - }); - - dialect.conditions.add('$jsonHas', function(field, operator, value) { - field = this.dialect._wrapIdentifier(field); - - var placeholder = value; - if (_(placeholder).isObject()) { - placeholder = this.buildBlock('field', placeholder); - } else { - placeholder = this._pushValue(value.toString()); - } - - return [field, '?', placeholder].join(' '); - }); - - dialect.conditions.add('$jsonHasAny', function(field, operator, value) { - return buildJsonHasCondition(this, field, '?|', value); - }); - - dialect.conditions.add('$jsonHasAll', function(field, operator, value) { - return buildJsonHasCondition(this, field, '?&', value); - }); - - dialect.conditions.add('$upper', function(field, operator, value) { - return [ - 'upper(' + this.dialect._wrapIdentifier(field) + ')', - '=', - 'upper(' + this._pushValue(value[1]) + ')' - ].join(' '); - }); - - dialect.conditions.add('$lower', function(field, operator, value) { - return [ - 'lower(' + this.dialect._wrapIdentifier(field) + ')', - '=', - 'lower(' + this._pushValue(value[1]) + ')' - ].join(' '); - }); - - dialect.conditions.add('$decode', function (field, operator, value) { - return [ - this.dialect._wrapIdentifier(field), - '=', - 'decode(' + this._pushValue(value[1]) + ',', - this._pushValue(value[2]) + ')' - ].join(' '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/postgresql/index.js b/legacy/json-sql/lib/dialects/postgresql/index.js deleted file mode 100644 index 755dc00d..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/index.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -var BaseDialect = require('../base'); -var _ = require('underscore'); -var util = require('util'); -var blocksInit = require('./blocks'); -var conditionsInit = require('./conditions'); -var templatesInit = require('./templates'); - -var Dialect = module.exports = function (builder) { - BaseDialect.call(this, builder); - blocksInit(this) - conditionsInit(this); - templatesInit(this); -}; - -Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); - -util.inherits(Dialect, BaseDialect); - -Dialect.prototype.config = _({ - jsonSeparatorRegexp: /->>?/g -}).extend(BaseDialect.prototype.config); - -Dialect.prototype._wrapIdentifier = function(name) { - // split by json separator - var nameParts = name.split(this.config.jsonSeparatorRegexp); - var separators = name.match(this.config.jsonSeparatorRegexp); - - // wrap base identifier - var identifier = BaseDialect.prototype._wrapIdentifier.call(this, nameParts[0]); - - // wrap all json identifier and join them with separators - identifier += _(separators).reduce(function(memo, separator, index) { - return memo + separator + '\'' + nameParts[index + 1] + '\''; - }, ''); - - return identifier; -}; diff --git a/legacy/json-sql/lib/dialects/postgresql/templates.js b/legacy/json-sql/lib/dialects/postgresql/templates.js deleted file mode 100644 index 297ed8b0..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/templates.js +++ /dev/null @@ -1,269 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - var availableSourceProps = ['table', 'query', 'select', 'expression']; - var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; - var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; - - // private templates - - dialect.templates.add('query', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subQuery', { - pattern: '({queryBody})', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subUnionQuery', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('create', { - pattern: 'create table if not exists {table}({tableFields})', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'tableFields'); - hasArrayProp(type, params, 'tableFields'); - } - }); - - dialect.templates.add('index', { - pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'name') - hasRequiredProp(type, params, 'indexOn'); - hasMinPropLength(type, params, 'name', 1); - hasMinPropLength(type, params, 'indexOn', 1); - } - }) - - - dialect.templates.add('queriesCombination', { - pattern: '{with} {queries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queries'); - hasArrayProp(type, params, 'queries'); - hasMinPropLength(type, params, 'queries', 2); - } - }); - - dialect.templates.add('queriesUnionCombination', { - pattern: '{with} {unionqueries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'unionqueries'); - hasArrayProp(type, params, 'unionqueries'); - hasMinPropLength(type, params, 'unionqueries', 2); - } - }); - - - dialect.templates.add('insertValues', { - pattern: '({fields}) values {fieldValues}', - validate: function(type, params) { - hasRequiredProp('values', params, 'fields'); - hasArrayProp('values', params, 'fields'); - hasMinPropLength('values', params, 'fields', 1); - - hasRequiredProp('values', params, 'fieldValues'); - hasArrayProp('values', params, 'fieldValues'); - hasMinPropLength('values', params, 'fieldValues', 1); - } - }); - - - dialect.templates.add('joinItem', { - pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', - validate: function(type, params) { - hasOneOfProps('join', params, availableSourceProps); - - if (params.type) { - hasStringProp('join', params, 'type'); - - var splitType = _(params.type.toLowerCase().split(' ')).compact(); - if (_.difference(splitType, availableJoinTypes).length) { - throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); - } - } - } - }); - - - dialect.templates.add('withItem', { - pattern: '{name} {fields} as {query} {select} {expression}', - validate: function(type, params) { - hasRequiredProp('with', params, 'name'); - hasOneOfProps('with', params, ['query', 'select', 'expression']); - } - }); - - - dialect.templates.add('fromItem', { - pattern: '{table} {query} {select} {expression}', - validate: function(type, params) { - hasOneOfProps('from', params, availableSourceProps); - } - }); - - - // public templates - - dialect.templates.add('select', { - pattern: '{with} select {distinct} {fields} ' + - 'from {from} {table} {query} {select} {expression} {alias} ' + - '{join} {condition} {group} {sort} {limit} {offset}', - defaults: { - fields: {} - }, - validate: function(type, params) { - hasOneOfProps(type, params, availableSourceProps); - } - }); - - - dialect.templates.add('insert', { - pattern: '{with} insert {or} into {table} {values} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('insertornothing', { - pattern: '{with} insert {or} into {table} {values} on conflict do nothing {returning} {condition}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('insertorupdate', { - pattern: '{with} insert {or} into {table} {values} on conflict {conflictFields} do update {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasRequiredProp('conflictFields', params, 'conflictFields'); - hasArrayProp('conflictFields', params, 'conflictFields'); - hasMinPropLength('conflictFields', params, 'conflictFields', 1); - hasRequiredProp(type, params, 'modifier'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('update', { - pattern: '{with} update {or} {table} {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'modifier'); - hasRequiredProp(type, params, 'table'); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('remove', { - pattern: '{with} delete from {table} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - } - }); - - - dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); - - - dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('except', dialect.templates.get('queriesCombination')); - - - // validation helpers - - function hasRequiredProp(type, params, propName) { - if (!params[propName]) { - throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); - } - } - - function hasObjectProp(type, params, propName) { - if (!_.isObject(params[propName])) { - throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); - } - } - - function hasArrayProp(type, params, propName) { - if (!_.isArray(params[propName])) { - throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); - } - } - - function hasStringProp(type, params, propName) { - if (!_.isString(params.type)) { - throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); - } - } - - function hasMinPropLength(type, params, propName, length) { - if (params[propName].length < length) { - throw new Error('`' + propName + '` property should not have length less than ' + length + - ' in `' + type + '` clause'); - } - } - - function hasOneOfProps(type, params, expectedPropNames) { - var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); - - if (!propNames.length) { - throw new Error('Neither `' + expectedPropNames.join('`, `') + - '` properties are not set in `' + type + '` clause'); - } - - if (propNames.length > 1) { - throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + - type + '` clause'); - } - } - - function matchesRegExpProp(type, params, propName, regExp) { - if (!params[propName].match(regExp)) { - throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + - type + '` clause'); - } - } -}; diff --git a/legacy/json-sql/lib/dialects/sqlite/blocks.js b/legacy/json-sql/lib/dialects/sqlite/blocks.js deleted file mode 100644 index c3531c2a..00000000 --- a/legacy/json-sql/lib/dialects/sqlite/blocks.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - dialect.blocks.add('offset', function(params) { - var limit = ''; - - if (typeof params.limit === 'undefined') { - limit = this.buildBlock('limit', {limit: -1}) + ' '; - } - - return limit + 'offset ' + this._pushValue(params.offset); - }); - - dialect.blocks.add('unionqueries', function(params) { - var self = this; - - return _(params.unionqueries).map(function(query) { - return self.buildTemplate('subUnionQuery', {queryBody: query}); - }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/sqlite/index.js b/legacy/json-sql/lib/dialects/sqlite/index.js deleted file mode 100644 index 72549400..00000000 --- a/legacy/json-sql/lib/dialects/sqlite/index.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var BaseDialect = require('../base'); -var _ = require('underscore'); -var util = require('util'); -var blocksInit = require('./blocks'); -var templatesInit = require('./templates'); - -var Dialect = module.exports = function (builder) { - BaseDialect.call(this, builder); - blocksInit(this); - templatesInit(this); -}; - -util.inherits(Dialect, BaseDialect); - -Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); diff --git a/legacy/json-sql/lib/dialects/sqlite/templates.js b/legacy/json-sql/lib/dialects/sqlite/templates.js deleted file mode 100644 index ac6e382f..00000000 --- a/legacy/json-sql/lib/dialects/sqlite/templates.js +++ /dev/null @@ -1,236 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - var availableSourceProps = ['table', 'query', 'select', 'expression']; - var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; - var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; - - // private templates - - dialect.templates.add('query', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subQuery', { - pattern: '({queryBody})', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subUnionQuery', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('create', { - pattern: 'create table if not exists {table}({tableFields})', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'tableFields'); - hasArrayProp(type, params, 'tableFields'); - } - }); - - dialect.templates.add('index', { - pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'name') - hasRequiredProp(type, params, 'indexOn'); - hasMinPropLength(type, params, 'name', 1); - hasMinPropLength(type, params, 'indexOn', 1); - } - }) - - - dialect.templates.add('queriesCombination', { - pattern: '{with} {queries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queries'); - hasArrayProp(type, params, 'queries'); - hasMinPropLength(type, params, 'queries', 2); - } - }); - - dialect.templates.add('queriesUnionCombination', { - pattern: '{with} {unionqueries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'unionqueries'); - hasArrayProp(type, params, 'unionqueries'); - hasMinPropLength(type, params, 'unionqueries', 2); - } - }); - - - dialect.templates.add('insertValues', { - pattern: '({fields}) values {fieldValues}', - validate: function(type, params) { - hasRequiredProp('values', params, 'fields'); - hasArrayProp('values', params, 'fields'); - hasMinPropLength('values', params, 'fields', 1); - - hasRequiredProp('values', params, 'fieldValues'); - hasArrayProp('values', params, 'fieldValues'); - hasMinPropLength('values', params, 'fieldValues', 1); - } - }); - - - dialect.templates.add('joinItem', { - pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', - validate: function(type, params) { - hasOneOfProps('join', params, availableSourceProps); - - if (params.type) { - hasStringProp('join', params, 'type'); - - var splitType = _(params.type.toLowerCase().split(' ')).compact(); - if (_.difference(splitType, availableJoinTypes).length) { - throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); - } - } - } - }); - - - dialect.templates.add('withItem', { - pattern: '{name} {fields} as {query} {select} {expression}', - validate: function(type, params) { - hasRequiredProp('with', params, 'name'); - hasOneOfProps('with', params, ['query', 'select', 'expression']); - } - }); - - - dialect.templates.add('fromItem', { - pattern: '{table} {query} {select} {expression}', - validate: function(type, params) { - hasOneOfProps('from', params, availableSourceProps); - } - }); - - - // public templates - - dialect.templates.add('select', { - pattern: '{with} select {distinct} {fields} ' + - 'from {from} {table} {query} {select} {expression} {alias} ' + - '{join} {condition} {group} {sort} {limit} {offset}', - defaults: { - fields: {} - }, - validate: function(type, params) { - hasOneOfProps(type, params, availableSourceProps); - } - }); - - - dialect.templates.add('insert', { - pattern: '{with} insert {or} into {table} {values} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('update', { - pattern: '{with} update {or} {table} {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'modifier'); - hasRequiredProp(type, params, 'table'); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('remove', { - pattern: '{with} delete from {table} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - } - }); - - - dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); - - - dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('except', dialect.templates.get('queriesCombination')); - - - // validation helpers - - function hasRequiredProp(type, params, propName) { - if (!params[propName]) { - throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); - } - } - - function hasObjectProp(type, params, propName) { - if (!_.isObject(params[propName])) { - throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); - } - } - - function hasArrayProp(type, params, propName) { - if (!_.isArray(params[propName])) { - throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); - } - } - - function hasStringProp(type, params, propName) { - if (!_.isString(params.type)) { - throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); - } - } - - function hasMinPropLength(type, params, propName, length) { - if (params[propName].length < length) { - throw new Error('`' + propName + '` property should not have length less than ' + length + - ' in `' + type + '` clause'); - } - } - - function hasOneOfProps(type, params, expectedPropNames) { - var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); - - if (!propNames.length) { - throw new Error('Neither `' + expectedPropNames.join('`, `') + - '` properties are not set in `' + type + '` clause'); - } - - if (propNames.length > 1) { - throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + - type + '` clause'); - } - } - - function matchesRegExpProp(type, params, propName, regExp) { - if (!params[propName].match(regExp)) { - throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + - type + '` clause'); - } - } -}; diff --git a/legacy/json-sql/lib/index.js b/legacy/json-sql/lib/index.js deleted file mode 100644 index 55bbe02a..00000000 --- a/legacy/json-sql/lib/index.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -var Builder = require('./builder'); - -module.exports = function(params) { - return new Builder(params); -}; -module.exports.Builder = Builder; diff --git a/legacy/json-sql/lib/valuesStore.js b/legacy/json-sql/lib/valuesStore.js deleted file mode 100644 index 29b7d395..00000000 --- a/legacy/json-sql/lib/valuesStore.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = ValuesStore; - -function ValuesStore(options) { - options = options || {}; - this.context = options.context || null; - this._values = options.values || {}; -} - -ValuesStore.prototype.add = ValuesStore.prototype.set = function(name, value) { - if (_.isFunction(value) && this.context) { - value = _(value).bind(this.context); - } - - this._values[name] = value; -}; - -ValuesStore.prototype.get = function(name) { - return this._values[name] || null; -}; - -ValuesStore.prototype.remove = function(name) { - delete this._values[name]; -}; - -ValuesStore.prototype.has = function(name) { - return this._values.hasOwnProperty(name); -}; diff --git a/legacy/json-sql/package.json b/legacy/json-sql/package.json deleted file mode 100644 index 73c407e3..00000000 --- a/legacy/json-sql/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "json-sql", - "description": "Node.js json to sql query mapper", - "version": "0.2.6", - "author": { - "name": "Artem Zhukov", - "email": "artzhuchka@gmail.com" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/2do2go/json-sql.git" - }, - "keywords": [ - "json-sql", - "json", - "sql", - "odm", - "mapper", - "db", - "database" - ], - "dependencies": { - "underscore": "=1.8.3" - }, - "devDependencies": { - "jshint": "=2.9.2", - "chai": "=3.5.0", - "gulp": "=3.9.1", - "gulp-jshint": "=2.0.0", - "gulp-mocha": "=2.2.0" - }, - "main": "./lib/index", - "readme": "# JSON-SQL\n\nLibrary for mapping mongo-style query objects to SQL queries.\n\n## Quick Start\n\nInstall it with NPM or add it to your package.json:\n\n``` bash\n$ npm install json-sql\n```\n\nThen:\n\n``` js\nvar jsonSql = require('json-sql')();\n\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tfields: ['name', 'age'],\n\tcondition: {name: 'Max', id: 6}\n});\n\nsql.query\n// sql string:\n// select name, age from users where name = $p1 && id = 6;\n\nsql.values\n// hash of values:\n// { p1: 'Max' }\n```\n\n## Documentation\n\nDocumentation is available at the [./docs directory](./docs).\n\n## Examples\n\n__Select with join:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tjoin: {\n\t\tdocuments: {\n\t\t\ton: {'user.id': 'documents.userId'}\n\t\t}\n\t}\n});\n\nsql.query\n// select * from users join documents on user.id = documents.userId;\n\nsql.values\n// {}\n```\n\n__Insert:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'insert',\n\ttable: 'users',\n\tvalues: {\n\t\tname: 'John',\n\t\tlastname: 'Snow',\n\t\tage: 24,\n\t\tgender: 'male'\n\t}\n});\n\nsql.query\n// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3);\n\nsql.values\n// { p1: 'John', p2: 'Snow', p3: 'male' }\n```\n\n__Update:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'update',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t},\n\tmodifier: {\n\t\trole: 'admin'\n\t\tage: 33\n\t}\n});\n\nsql.query\n// update users set role = $p1, age = 33 where id = 5;\n\nsql.values\n// { p1: 'admin' }\n```\n\n__Remove:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'remove',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t}\n});\n\nsql.query\n// delete from users where id = 5;\n\nsql.values\n// {}\n```\n\nFor more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests).\n\n## Tests\n\nClone repository from github, `cd` into cloned dir and install dev dependencies:\n\n``` bash\n$ npm install\n```\n\nThen run tests with command:\n\n``` bash\n$ gulp test\n```\n\nOr run tests coverage with command:\n\n``` bash\n$ gulp coverage\n```\n\n## License\n\n[MIT](./LICENSE)\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/2do2go/json-sql/issues" - }, - "homepage": "https://github.com/2do2go/json-sql#readme", - "_id": "json-sql@0.2.4", - "_shasum": "df8c5f345b72f421c6fc7da57116c47cae5b5216", - "_resolved": "https://github.com/LiskHQ/json-sql/tarball/master", - "_from": "https://github.com/LiskHQ/json-sql/tarball/master" -} diff --git a/legacy/json-sql/tests/0_base.js b/legacy/json-sql/tests/0_base.js deleted file mode 100644 index b46774be..00000000 --- a/legacy/json-sql/tests/0_base.js +++ /dev/null @@ -1,304 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var Builder = require('../lib').Builder; -var expect = require('chai').expect; - -describe('Builder', function() { - it('should have fields', function() { - expect(jsonSql).to.be.ok; - expect(jsonSql).to.be.an.instanceof(Builder); - - expect(jsonSql.dialect).to.be.ok; - - expect(jsonSql._query).to.be.equal(''); - expect(jsonSql._values).to.be.eql({}); - - expect(jsonSql.dialect.blocks).to.be.ok; - expect(jsonSql.dialect.templates).to.be.ok; - expect(jsonSql.dialect.conditions).to.be.ok; - expect(jsonSql.dialect.modifiers).to.be.ok; - expect(jsonSql.dialect.logicalOperators).to.be.ok; - }); - - it('should throw error with wrong `type` property', function() { - expect(function() { - jsonSql.build({ - type: 'wrong' - }); - }).to.throw('Unknown template type "wrong"'); - }); - - it('should throw error without `table`, `query` and `select` properties', function() { - expect(function() { - jsonSql.build({}); - }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + - 'are not set in `select` clause'); - }); - - it('should throw error with both `table` and `select` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - select: {table: 'payments'} - }); - }).to.throw('Wrong using `table`, `select` properties together in `select` clause'); - }); - - it('should throw error with both `table` and `query` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - query: {table: 'payments'} - }); - }).to.throw('Wrong using `table`, `query` properties together in `select` clause'); - }); - - it('should throw error with both `query` and `select` properties', function() { - expect(function() { - jsonSql.build({ - query: {table: 'payments'}, - select: {table: 'payments'} - }); - }).to.throw('Wrong using `query`, `select` properties together in `select` clause'); - }); - - it('should throw error without `name` property in `with` clause', function() { - expect(function() { - jsonSql.build({ - 'with': [{ - select: { - table: 'payments' - } - }], - table: 'users' - }); - }).to.throw('`name` property is not set in `with` clause'); - }); - - it('should throw error without `query` and `select` properties in `with` clause', function() { - expect(function() { - jsonSql.build({ - 'with': [{ - name: 'payments' - }], - table: 'users' - }); - }).to.throw('Neither `query`, `select`, `expression` properties are not set in `with` clause'); - }); - - it('should throw error with both `query` and `select` properties in `with` clause', function() { - expect(function() { - jsonSql.build({ - 'with': [{ - name: 'payments', - query: {table: 'table1'}, - select: {table: 'table2'} - }], - table: 'users' - }); - }).to.throw('Wrong using `query`, `select` properties together in `with` clause'); - }); - - it('should be ok with array in `with` clause', function() { - var result = jsonSql.build({ - 'with': [{ - name: 'payments', - select: { - table: 'payments' - } - }], - table: 'users' - }); - - expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + - '"users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object in `with` clause', function() { - var result = jsonSql.build({ - 'with': { - payments: { - select: { - table: 'payments' - } - } - }, - table: 'users' - }); - - expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + - '"users";'); - expect(result.values).to.be.eql({}); - }); - - it('should create array values with option `namedValues` = false', function() { - jsonSql.configure({ - namedValues: false - }); - - expect(jsonSql._values).to.be.eql([]); - - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); - expect(result.values).to.be.eql(['John']); - }); - - it('should use prefix `@` for values with option `valuesPrefix` = @', function() { - jsonSql.configure({ - valuesPrefix: '@' - }); - - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); - expect(result.values).to.be.eql({p1: 'John'}); - }); - - it('should return prefixed values with method `prefixValues`', function() { - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); - expect(result.values).to.be.eql({p1: 'John'}); - expect(result.prefixValues()).to.be.eql({'@{p1}': 'John'}); - }); - - it('should return array values with method `getValuesArray`', function() { - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); - expect(result.values).to.be.eql({p1: 'John'}); - expect(result.getValuesArray()).to.be.eql(['John']); - }); - - it('should return object values with method `getValuesObject`', function() { - jsonSql.configure({ - valuesPrefix: '$', - namedValues: false - }); - - expect(jsonSql._values).to.be.eql([]); - - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); - expect(result.values).to.be.eql(['John']); - expect(result.prefixValues()).to.be.eql({'${1}': 'John'}); - expect(result.getValuesObject()).to.be.eql({1: 'John'}); - }); - - it('should create query without values with option `separatedValues` = false', function() { - jsonSql.configure({ - separatedValues: false - }); - - expect(jsonSql._values).to.not.be.ok; - expect(jsonSql._placeholderId).to.not.be.ok; - - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: {name: 'John', surname: 'Doe'} - }); - - expect(result.query).to.be.equal('insert into "users" ("name", "surname") values ' + - '(\'John\', \'Doe\');'); - expect(result.values).to.not.be.ok; - }); - - it('should create query without wrapping identifiers with option `wrappedIdentifiers` = false', - function() { - jsonSql.configure({ - wrappedIdentifiers: false - }); - - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: {name: 'John'} - }); - - expect(result.query).to.be.equal('insert into users (name) values (${p1});'); - } - ); - - it('shouldn\'t wrap identifiers that already wrapped', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - type: 'insert', - table: '"users"', - values: { - '"name"': 'John', - '"users"."age"': 22 - } - }); - - expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); - }); - - it('shouldn\'t split identifiers by dots inside quotes', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - type: 'insert', - table: '"users"', - values: { - '"users.age"': 22 - } - }); - - expect(result.query).to.be.equal('insert into "users" ("users.age") values (22);'); - }); - - it('shouldn\'t wrap asterisk identifier parts', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - fields: ['users.*'], - table: '"users"' - }); - - expect(result.query).to.be.equal('select "users".* from "users";'); - }); - - it('should split identifiers by dots and wrap each part', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - type: 'insert', - table: '"users"', - values: { - 'name': 'John', - 'users.age': 22 - } - }); - - expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); - }); -}); diff --git a/legacy/json-sql/tests/1_select.js b/legacy/json-sql/tests/1_select.js deleted file mode 100644 index 087a1afb..00000000 --- a/legacy/json-sql/tests/1_select.js +++ /dev/null @@ -1,1222 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Select', function() { - describe('type', function() { - it('should be ok without `type` property', function() { - var result = jsonSql.build({ - table: 'users' - }); - - expect(result.query).to.be.equal('select * from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with "select" value', function() { - var result = jsonSql.build({ - type: 'select', - table: 'users' - }); - - expect(result.query).to.be.equal('select * from "users";'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('distinct', function() { - it('should be ok with true value', function() { - var result = jsonSql.build({ - table: 'users', - distinct: true - }); - - expect(result.query).to.be.equal('select distinct * from "users";'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('fields', function() { - it('should be ok with string array', function() { - var result = jsonSql.build({ - table: 'users', - fields: ['name', 'type'] - }); - - expect(result.query).to.be.equal('select "name", "type" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`name`: `alias`, ...)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {userAge: 'age', userScore: 'score'} - }); - - expect(result.query).to.be.equal('select "userAge" as "age", "userScore" as "score" from ' + - '"users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array of objects(`name`: `alias`, ...)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{userAge: 'age'}] - }); - - expect(result.query).to.be.equal('select "userAge" as "age" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'address'}] - }); - - expect(result.query).to.be.equal('select "address" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `table`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'score', table: 'users'}] - }); - - expect(result.query).to.be.equal('select "users"."score" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `alias`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'zoneName', alias: 'zone'}] - }); - - expect(result.query).to.be.equal('select "zoneName" as "zone" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `table`, `alias`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'zoneName', table: 'users', alias: 'zone'}] - }); - - expect(result.query).to.be.equal('select "users"."zoneName" as "zone" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`table`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {score: {table: 'users'}} - }); - - expect(result.query).to.be.equal('select "users"."score" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {zone: {field: 'zone_1', alias: 'zone'}} - }); - - expect(result.query).to.be.equal('select "zone_1" as "zone" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`table`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {score: {table: 'users', alias: 's'}} - }); - - expect(result.query).to.be.equal('select "users"."score" as "s" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `table`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {name: {field: 'name_1', table: 'users', alias: 'name_2'}} - }); - - expect(result.query).to.be.equal('select "users"."name_1" as "name_2" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: 'count(*)' - }] - }); - - expect(result.query).to.be.equal('select count(*) from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: 'count(*)', - alias: 'count' - }] - }); - - expect(result.query).to.be.equal('select count(*) as "count" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`, `field`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: 'sum', - field: 'income', - alias: 'sum' - }] - }); - - expect(result.query).to.be.equal('select sum("income") as "sum" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`[], `field`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: ['abs', 'sum'], - field: 'income', - alias: 'sum' - }] - }); - - expect(result.query).to.be.equal('select abs(sum("income")) as "sum" from "users";'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('alias', function() { - it('should be ok with string `alias` property', function() { - var result = jsonSql.build({ - table: 'users', - alias: 'u' - }); - - expect(result.query).to.be.equal('select * from "users" as "u";'); - expect(result.values).to.be.eql({}); - }); - - it('should throw error if object `alias` does not have `name` property', function() { - expect(function() { - jsonSql.build({ - table: 'users', - alias: {} - }); - }).to.throw('Alias `name` property is required'); - }); - - it('should be ok with object `alias`(`name`) property', function() { - var result = jsonSql.build({ - table: 'users', - alias: { - name: 'u' - } - }); - - expect(result.query).to.be.equal('select * from "users" as "u";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object `alias`(`name`, `columns`) property', function() { - var result = jsonSql.build({ - table: 'users', - alias: { - name: 'u', - columns: ['a', 'b'] - } - }); - - expect(result.query).to.be.equal('select * from "users" as "u"("a", "b");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('query', function() { - it('should be ok with `query` property', function() { - var result = jsonSql.build({ - query: { - type: 'select', - table: 't' - } - }); - - expect(result.query).to.be.equal('select * from (select * from "t");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('select', function() { - it('should be ok with `select` property', function() { - var result = jsonSql.build({ - select: { - table: 't' - } - }); - - expect(result.query).to.be.equal('select * from (select * from "t");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('expression', function() { - it('should be ok with `expression` property', function() { - var result = jsonSql.build({ - expression: 'function()' - }); - - expect(result.query).to.be.equal('select * from function();'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('join', function() { - it('should throw error without `table`, `query` and `select` properties', - function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{}] - }); - }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + - 'are not set in `join` clause'); - } - ); - - it('should throw error with both `table` and `select` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - table: 'a', - select: {table: 'b'} - }] - }); - }).to.throw('Wrong using `table`, `select` properties together in `join` clause'); - }); - - it('should throw error with both `table` and `query` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - table: 'a', - query: {table: 'b'} - }] - }); - }).to.throw('Wrong using `table`, `query` properties together in `join` clause'); - }); - - it('should throw error with both `query` and `select` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - query: 'a', - select: {table: 'b'} - }] - }); - }).to.throw('Wrong using `query`, `select` properties together in `join` clause'); - }); - - it('should throw error with wrong `type` property', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - type: 'wrong', - table: 'payments' - }] - }); - }).to.throw('Invalid `type` property value "wrong" in `join` clause'); - }); - - it('should be ok with correct `type` property', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - type: 'left outer', - table: 'payments' - }] - }); - - expect(result.query).to.be.equal('select * from "users" left outer join "payments";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array `join`', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - table: 'payments' - }] - }); - - expect(result.query).to.be.equal('select * from "users" join "payments";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object `join`', function() { - var result = jsonSql.build({ - table: 'users', - join: { - payments: {} - } - }); - - expect(result.query).to.be.equal('select * from "users" join "payments";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `on` property', function() { - var result = jsonSql.build({ - table: 'users', - join: { - payments: { - on: {'users.name': 'payments.name'} - } - } - }); - - expect(result.query).to.be.equal('select * from "users" join "payments" on "users"."name" = ' + - '"payments"."name";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `query` property', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - query: { - table: 'payments' - }, - on: {'users.name': 'payments.name'} - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'join (select * from "payments") ' + - 'on "users"."name" = "payments"."name";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `select` property', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - select: { - table: 'payments' - }, - on: {'users.name': 'payments.name'} - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'join (select * from "payments") ' + - 'on "users"."name" = "payments"."name";' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('condition', function() { - describe('compare operators', function() { - it('should throw error with wrong operator', function() { - expect(function() { - jsonSql.build({ - table: 'users', - condition: { - name: {$wrong: 'John'} - } - }); - }).to.throw('Unknown operator "$wrong"'); - }); - - it('should be ok with default operator(=)', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: 'John' - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$eq` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$eq: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$ne` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$ne: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" != ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$gt` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$gt: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" > ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$lt` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$lt: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" < ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$gte` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$gte: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" >= ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$lte` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$lte: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" <= ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$is` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$is: null} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$isnot` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$isnot: null} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$like` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$like: 'John%'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" like ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John%' - }); - }); - - it('should be ok with `$null`:true operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$null: true} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$null`:false operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$null: false} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$field` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$field: 'name_2'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object `$field` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$field: {field: 'name_2'}} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: [12, 13, 14]} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (12, 13, 14);'); - expect(result.values).to.be.eql({}); - }); - - it('should add `null` value with empty array in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: []} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (null);'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$nin` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$nin: [12, 13, 14]} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" not in (12, 13, 14);'); - expect(result.values).to.be.eql({}); - }); - - it('should add `null` value with empty array in `$nin` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$nin: []} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" not in (null);'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object subquery in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: { - table: 'test' - }} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + - '"test");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `query` subquery in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: { - query: { - table: 'test' - } - }} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + - '(select * from "test"));'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `select` subquery in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: { - select: { - table: 'test' - } - }} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + - '(select * from "test"));'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$between` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$between: [12, 14]} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" between 12 and 14;'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('logical operators', function() { - it('should throw error with wrong logical operator', function() { - expect(function() { - jsonSql.build({ - table: 'users', - condition: { - $wrong: [ - {name: 'John'}, - {age: 12} - ] - } - }); - }).to.throw('Unknown logical operator "$wrong"'); - }); - - it('should be ok with default logical operator(`$and`)', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: 'John', - age: 12 - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with default logical operator(`$and`) for one field', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: { - $gt: 5, - $lt: 15, - $ne: 10 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" > 5 and "age" < 15 and ' + - '"age" != 10;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array `$and`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [ - {name: 'John'}, - {age: 12} - ] - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object `$and`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: { - name: 'John', - age: 12 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with array `$or`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [ - {name: 'John'}, - {age: 12} - ] - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object `$or`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: { - name: 'John', - age: 12 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with array `$not`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $not: [ - {name: 'John'}, - {age: 12} - ] - } - }); - - expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + - '"age" = 12);'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object `$not`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $not: { - name: 'John', - age: 12 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + - '"age" = 12);'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object [`$or`, `$or`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: [{ - $or: { - name: 'John', - age: 12 - } - }, { - $or: { - name: 'Mark', - age: 14 - } - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} or "age" = 12) and ' + - '("name" = ${p2} or "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with object `$and`:[`$or`, `$or`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [{ - $or: { - name: 'John', - age: 12 - } - }, { - $or: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} or "age" = 12) and ' + - '("name" = ${p2} or "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with object `$or`:[{},{}]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [{ - name: 'John', - age: 12 - }, { - name: 'Mark', - age: 14 - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) or ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with object `$or`:[`$and`, `$and`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [{ - $and: { - name: 'John', - age: 12 - } - }, { - $and: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) or ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with [{}, {}]', function() { - var result = jsonSql.build({ - table: 'users', - condition: [{ - name: 'John', - age: 12 - }, { - name: 'Mark', - age: 14 - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) and ' + - '("name" = ${p2} and "age" = 14);'); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with `$and`:[{}, {}]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [{ - name: 'John', - age: 12 - }, { - name: 'Mark', - age: 14 - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) and ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with `$and`:[`$and`, `$and`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [{ - $and: { - name: 'John', - age: 12 - } - }, { - $and: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) and ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with `$or`:[`$or`, `$or`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [{ - $or: { - name: 'John', - age: 12 - } - }, { - $or: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} or "age" = 12) or ' + - '("name" = ${p2} or "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - }); - }); - - describe('group', function() { - it('should be ok with string value', function() { - var result = jsonSql.build({ - table: 'users', - group: 'age' - }); - - expect(result.query).to.be.equal( - 'select * from "users" group by "age";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array value', function() { - var result = jsonSql.build({ - table: 'users', - group: ['age', 'gender'] - }); - - expect(result.query).to.be.equal( - 'select * from "users" group by "age", "gender";' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('sort', function() { - it('should be ok with string value', function() { - var result = jsonSql.build({ - table: 'users', - sort: 'age' - }); - - expect(result.query).to.be.equal( - 'select * from "users" order by "age";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array value', function() { - var result = jsonSql.build({ - table: 'users', - sort: ['age', 'gender'] - }); - - expect(result.query).to.be.equal( - 'select * from "users" order by "age", "gender";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object value', function() { - var result = jsonSql.build({ - table: 'users', - sort: { - age: 1, - gender: -1 - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" order by "age" asc, "gender" desc;' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('limit, offset', function() { - it('should be ok with `limit` property', function() { - var result = jsonSql.build({ - table: 'users', - limit: 5 - }); - - expect(result.query).to.be.equal( - 'select * from "users" limit 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `offset` property', function() { - var result = jsonSql.build({ - table: 'users', - offset: 5 - }); - - expect(result.query).to.be.equal( - 'select * from "users" offset 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `limit` and `offset` properties', function() { - var result = jsonSql.build({ - table: 'users', - limit: 10, - offset: 20 - }); - - expect(result.query).to.be.equal( - 'select * from "users" limit 10 offset 20;' - ); - expect(result.values).to.be.eql({}); - }); - }); -}); diff --git a/legacy/json-sql/tests/2_insert.js b/legacy/json-sql/tests/2_insert.js deleted file mode 100644 index fe3f1b9d..00000000 --- a/legacy/json-sql/tests/2_insert.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Insert', function() { - it('should throw error without `values` property', function() { - expect(function() { - jsonSql.build({ - type: 'insert', - table: 'users' - }); - }).to.throw('`values` property is not set in `insert` clause'); - }); - - it('should be ok with `values` property', function() { - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: { - name: 'Max' - } - }); - - expect(result.query).to.be.equal('insert into "users" ("name") values (${p1});'); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `with` property', function() { - var result = jsonSql.build({ - 'with': [{ - name: 't_1', - select: { - table: 't_1' - } - }], - type: 'insert', - table: 'users', - values: { - name: 'Max', - age: 17, - lastVisit: null, - active: true - } - }); - - expect(result.query).to.be.equal( - 'with "t_1" as (select * from "t_1") insert into "users" ' + - '("name", "age", "lastVisit", "active") values (${p1}, 17, null, true);' - ); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `returning` property', function() { - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: { - name: 'Max', - age: 17, - lastVisit: null, - active: true - }, - returning: ['users.*'] - }); - - expect(result.query).to.be.equal( - 'insert into "users" ("name", "age", "lastVisit", "active") ' + - 'values (${p1}, 17, null, true) returning "users".*;' - ); - expect(result.values).to.be.eql({p1: 'Max'}); - }); -}); diff --git a/legacy/json-sql/tests/3_update.js b/legacy/json-sql/tests/3_update.js deleted file mode 100644 index 688b4c1d..00000000 --- a/legacy/json-sql/tests/3_update.js +++ /dev/null @@ -1,123 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Update', function() { - describe('modifier', function() { - it('should throw error without `modifier` property', function() { - expect(function() { - jsonSql.build({ - type: 'update', - table: 'users' - }); - }).to.throw('`modifier` property is not set in `update` clause'); - }); - - it('should be ok with default(`$set`)', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - name: 'Max', - age: 16, - lastVisit: null, - active: false - } - }); - - expect(result.query).to.be.equal('update "users" set "name" = ${p1}, "age" = 16, ' + - '"lastVisit" = null, "active" = false;'); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `$set`', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $set: { - name: 'Max' - } - } - }); - - expect(result.query).to.be.equal('update "users" set "name" = ${p1};'); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `$inc`', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $inc: { - age: 4 - } - } - }); - - expect(result.query).to.be.equal('update "users" set "age" = "age" + 4;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$dec`', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $dec: { - age: 2 - } - } - }); - - expect(result.query).to.be.equal('update "users" set "age" = "age" - 2;'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('with', function() { - it('should be ok', function() { - var result = jsonSql.build({ - 'with': [{ - name: 't_1', - select: { - table: 't_1' - } - }], - type: 'update', - table: 'users', - modifier: { - $dec: { - age: 3 - } - } - }); - - expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") update "users" ' + - 'set "age" = "age" - 3;'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('returning', function() { - it('should be ok', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $dec: { - age: 3 - } - }, - returning: ['users.*'] - }); - - expect(result.query).to.be.equal( - 'update "users" set "age" = "age" - 3 returning "users".*;' - ); - expect(result.values).to.be.eql({}); - }); - }); -}); diff --git a/legacy/json-sql/tests/4_delete.js b/legacy/json-sql/tests/4_delete.js deleted file mode 100644 index c90f19f2..00000000 --- a/legacy/json-sql/tests/4_delete.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Delete', function() { - it('should be ok without `condition` property', function() { - var result = jsonSql.build({ - type: 'remove', - table: 'users' - }); - - expect(result.query).to.be.equal('delete from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `condition` property', function() { - var result = jsonSql.build({ - type: 'remove', - table: 'users', - condition: { - a: 5 - } - }); - - expect(result.query).to.be.equal('delete from "users" where "a" = 5;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `with` property', function() { - var result = jsonSql.build({ - 'with': [{ - name: 't_1', - select: { - table: 't_1' - } - }], - type: 'remove', - table: 'users' - }); - - expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") delete from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `returning` property', function() { - var result = jsonSql.build({ - type: 'remove', - table: 'users', - returning: ['users.*'] - }); - - expect(result.query).to.be.equal( - 'delete from "users" returning "users".*;' - ); - expect(result.values).to.be.eql({}); - }); -}); diff --git a/legacy/json-sql/tests/5_union.js b/legacy/json-sql/tests/5_union.js deleted file mode 100644 index a1e79fcf..00000000 --- a/legacy/json-sql/tests/5_union.js +++ /dev/null @@ -1,256 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Union, except, intersect', function() { - describe('queries', function() { - it('should throw error without `queries` property', function() { - expect(function() { - jsonSql.build({ - type: 'union' - }); - }).to.throw('`queries` property is not set in `union` clause'); - }); - - it('should throw error with non-array value', function() { - expect(function() { - jsonSql.build({ - type: 'union', - queries: 'wrong' - }); - }).to.throw('`queries` property should be an array in `union` clause'); - }); - - it('should throw error with value length < 2', function() { - expect(function() { - jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }] - }); - }).to.throw('`queries` property should not have length less than 2 in `union` clause'); - }); - - it('should be ok with value length = 2', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") union (select * from "vipUsers");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('type & all combinations', function() { - it('should be ok with `type` = "union", `all` = true', function() { - var result = jsonSql.build({ - type: 'union', - all: true, - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") union all (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "except"', function() { - var result = jsonSql.build({ - type: 'except', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") except (select * from "vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "except", `all` = true', function() { - var result = jsonSql.build({ - type: 'except', - all: true, - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") except all (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "intersect"', function() { - var result = jsonSql.build({ - type: 'intersect', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") intersect (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "intersect", `all` = true', function() { - var result = jsonSql.build({ - type: 'intersect', - all: true, - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") intersect all (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "union" subquery', function() { - var result = jsonSql.build({ - query: { - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - } - }); - - expect(result.query).to.be.equal('select * from ((select * from "users") union (select * ' + - 'from "vipUsers"));'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('sort', function() { - it('should be ok with string value', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - sort: 'age' - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") order by "age";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array value', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - sort: ['age', 'gender'] - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") order by "age", "gender";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object value', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - sort: { - age: 1, - gender: -1 - } - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") order by "age" asc, "gender" desc;' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('limit, offset', function() { - it('should be ok with `limit` property', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - limit: 5 - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") limit 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `offset` property', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - offset: 5 - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") offset 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `limit` and `offset` properties', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - limit: 10, - offset: 20 - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") limit 10 offset 20;' - ); - expect(result.values).to.be.eql({}); - }); - }); -}); diff --git a/legacy/json-sql/tests/6_postgresDialect.js b/legacy/json-sql/tests/6_postgresDialect.js deleted file mode 100644 index 790c0ec2..00000000 --- a/legacy/json-sql/tests/6_postgresDialect.js +++ /dev/null @@ -1,140 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')({ - dialect: 'postgresql', - namedValues: false -}); -var expect = require('chai').expect; - -describe('PostgreSQL dialect', function() { - describe('json', function() { - it('should correctly wrap each part of json path', function() { - var result = jsonSql.build({ - table: 'test', - fields: ['params->a->>b'], - condition: { - 'params->>c': {$like: '7%'} - } - }); - - expect(result.query).to.be.equal( - 'select "params"->\'a\'->>\'b\' from "test" ' + - 'where "params"->>\'c\' like ${1};' - ); - }); - - it('should be ok with `$jsonContains` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - 'params->a': { - $jsonContains: {b: 1} - } - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params"->\'a\' @> ${1};' - ); - expect(result.values).to.be.eql(['{"b":1}']); - }); - - it('should be ok with `$jsonIn` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - 'params->a': { - $jsonIn: {$field: 'data->b'} - } - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params"->\'a\' <@ "data"->\'b\';' - ); - expect(result.values).to.be.eql([]); - }); - - it('should be ok with `$jsonHas` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$jsonHas: 'account'} - } - }); - - expect(result.query).to.be.equal('select * from "test" where "params" ? ${1};'); - expect(result.values).to.be.eql(['account']); - }); - - it('should be ok with `$jsonHasAny` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$jsonHasAny: ['a', 'b']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params" ?| array[${1}, ${2}];' - ); - expect(result.values).to.be.eql(['a', 'b']); - }); - - it('should be ok with `$jsonHasAll` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$jsonHasAll: ['a', 'b']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params" ?& array[${1}, ${2}];' - ); - expect(result.values).to.be.eql(['a', 'b']); - }); - - it('should be ok with `$upper` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$upper: ['params', '3498862814541110459l']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where upper("params") = upper(${1});' - ); - expect(result.values).to.be.eql(['3498862814541110459l']); - }); - - it('should be ok with `$lower` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$lower: ['params', '3498862814541110459L']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where lower("params") = lower(${1});' - ); - expect(result.values).to.be.eql(['3498862814541110459L']); - }); - - it('should be ok with `$decode` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$decode: ['params', '3498862814541110459L', 'hex']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params" = decode(${1}, ${2});' - ); - expect(result.values).to.be.eql(['3498862814541110459L', 'hex']); - }); - }); -}); diff --git a/legacy/json-sql/tests/7_create.js b/legacy/json-sql/tests/7_create.js deleted file mode 100644 index a93ca702..00000000 --- a/legacy/json-sql/tests/7_create.js +++ /dev/null @@ -1,279 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Create', function () { - it('should throw error without `tableFields` property', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users' - }); - }).to.throw('`tableFields` property is not set in `create` clause'); - }); - - it('should throw error with incorrect field type', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "age", - type: "NotNumber" - } - ] - }); - }).to.throw('Invalid type of field: NotNumber'); - }); - - it('should be ok with `tableFields` property', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "age", - type: "Number" - } - ] - }); - - - expect(result.query).to.be.equal('create table if not exists "users"("age" int);'); - }); - - it('should throw error when length property for string field not provided', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String" - } - ] - }); - }).to.throw('Field length can\'t be less or equal 0'); - }); - - it('should throw error with empty name', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: " ", - type: "String" - } - ] - }); - }).to.throw('Name most contains characters'); - }); - - it('should be ok with string field and length', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16 - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16));'); - }); - - it('should be ok with string field not null', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL);'); - }); - - it('should be ok with string field not null primary key', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY);'); - }); - - - it('should be ok with string field not null unique', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - unique: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL UNIQUE);'); - }); - - - it('should be allow only one primary key field', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "secondname", - type: "String", - length: 16, - not_null: true, - primary_key: true - } - ] - }) - }).to.throw("Too much primary key 'secondname' in table"); - }); - - it('should be allow only unique field name', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "name", - type: "String", - length: 16, - not_null: true - } - ] - }) - }).to.throw("Two parameters with same name: name"); - }); - - it("should allow few fields", function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL);'); - }); - - it("should allow few fields", function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ], - foreignKeys: [ - { - field: "name", - table: "person", - table_field: "id" - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); - }); - - it("should allow few fields", function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ], - foreignKeys: [ - { - field: "name", - table: "person", - table_field: "id" - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); - }); -}); diff --git a/legacy/json-sql/tests/8_index.js b/legacy/json-sql/tests/8_index.js deleted file mode 100644 index 464a85b3..00000000 --- a/legacy/json-sql/tests/8_index.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -/* - jsonSql.build({ - type: 'index', - table: 'users', - index: { - name: "user_id", - field: "id" - } - }); - */ - -describe('Index', function() { - it('should throw error without name property', function() { - expect(function() { - jsonSql.build({ - type: 'index', - table: 'users' - }); - }).to.throw('`name` property is not set in `index` clause'); - }); - - it('should throw error without indexOn property', function() { - expect(function() { - jsonSql.build({ - type: 'index', - table: 'users', - name: 'index_id' - }); - }).to.throw('`indexOn` property is not set in `index` clause'); - }); - - it('should be ok with name and field property', function () { - var result = jsonSql.build({ - type: "index", - table: "users", - name: "index_id", - indexOn: "id" - }); - }); -}); diff --git a/package.json b/package.json index bd85f3fc..fa97b268 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ip": "=1.1.8", "js-nacl": "^1.4.0", "json-schema": "=0.4.0", - "json-sql": "./legacy/json-sql", + "json-sql": "^0.5.0", "lodash": "=4.17.21", "method-override": "=3.0.0", "npm": "=8.17.0", From 42603f54b1a003cb0eae3c6fcce5bc0c1c584b49 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 12:56:01 +0400 Subject: [PATCH 07/33] debug: json-sql --- legacy/json-sql/.gitignore | 1 + legacy/json-sql/.jshintrc | 19 + legacy/json-sql/.npmignore | 2 + legacy/json-sql/LICENSE | 22 + legacy/json-sql/README.md | 145 ++ legacy/json-sql/docs/README.md | 1180 ++++++++++++++++ legacy/json-sql/gulpfile.js | 27 + legacy/json-sql/lib/builder.js | 233 ++++ legacy/json-sql/lib/dialects/base/blocks.js | 441 ++++++ .../json-sql/lib/dialects/base/conditions.js | 122 ++ legacy/json-sql/lib/dialects/base/index.js | 58 + .../lib/dialects/base/logicalOperators.js | 37 + .../json-sql/lib/dialects/base/modifiers.js | 37 + legacy/json-sql/lib/dialects/base/scheme.js | 146 ++ .../json-sql/lib/dialects/base/templates.js | 220 +++ legacy/json-sql/lib/dialects/mssql/index.js | 13 + .../lib/dialects/postgresql/blocks.js | 33 + .../lib/dialects/postgresql/conditions.js | 87 ++ .../json-sql/lib/dialects/postgresql/index.js | 39 + .../lib/dialects/postgresql/templates.js | 269 ++++ legacy/json-sql/lib/dialects/sqlite/blocks.js | 23 + legacy/json-sql/lib/dialects/sqlite/index.js | 17 + .../json-sql/lib/dialects/sqlite/templates.js | 236 ++++ legacy/json-sql/lib/index.js | 8 + legacy/json-sql/lib/valuesStore.js | 31 + legacy/json-sql/package.json | 44 + legacy/json-sql/tests/0_base.js | 304 ++++ legacy/json-sql/tests/1_select.js | 1222 +++++++++++++++++ legacy/json-sql/tests/2_insert.js | 73 + legacy/json-sql/tests/3_update.js | 123 ++ legacy/json-sql/tests/4_delete.js | 58 + legacy/json-sql/tests/5_union.js | 256 ++++ legacy/json-sql/tests/6_postgresDialect.js | 140 ++ legacy/json-sql/tests/7_create.js | 279 ++++ legacy/json-sql/tests/8_index.js | 45 + logic/account.js | 6 + package.json | 2 +- 37 files changed, 5997 insertions(+), 1 deletion(-) create mode 100644 legacy/json-sql/.gitignore create mode 100644 legacy/json-sql/.jshintrc create mode 100644 legacy/json-sql/.npmignore create mode 100644 legacy/json-sql/LICENSE create mode 100644 legacy/json-sql/README.md create mode 100644 legacy/json-sql/docs/README.md create mode 100644 legacy/json-sql/gulpfile.js create mode 100644 legacy/json-sql/lib/builder.js create mode 100644 legacy/json-sql/lib/dialects/base/blocks.js create mode 100644 legacy/json-sql/lib/dialects/base/conditions.js create mode 100644 legacy/json-sql/lib/dialects/base/index.js create mode 100644 legacy/json-sql/lib/dialects/base/logicalOperators.js create mode 100644 legacy/json-sql/lib/dialects/base/modifiers.js create mode 100644 legacy/json-sql/lib/dialects/base/scheme.js create mode 100644 legacy/json-sql/lib/dialects/base/templates.js create mode 100644 legacy/json-sql/lib/dialects/mssql/index.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/blocks.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/conditions.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/index.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/templates.js create mode 100644 legacy/json-sql/lib/dialects/sqlite/blocks.js create mode 100644 legacy/json-sql/lib/dialects/sqlite/index.js create mode 100644 legacy/json-sql/lib/dialects/sqlite/templates.js create mode 100644 legacy/json-sql/lib/index.js create mode 100644 legacy/json-sql/lib/valuesStore.js create mode 100644 legacy/json-sql/package.json create mode 100644 legacy/json-sql/tests/0_base.js create mode 100644 legacy/json-sql/tests/1_select.js create mode 100644 legacy/json-sql/tests/2_insert.js create mode 100644 legacy/json-sql/tests/3_update.js create mode 100644 legacy/json-sql/tests/4_delete.js create mode 100644 legacy/json-sql/tests/5_union.js create mode 100644 legacy/json-sql/tests/6_postgresDialect.js create mode 100644 legacy/json-sql/tests/7_create.js create mode 100644 legacy/json-sql/tests/8_index.js diff --git a/legacy/json-sql/.gitignore b/legacy/json-sql/.gitignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/legacy/json-sql/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/legacy/json-sql/.jshintrc b/legacy/json-sql/.jshintrc new file mode 100644 index 00000000..fe7c5b09 --- /dev/null +++ b/legacy/json-sql/.jshintrc @@ -0,0 +1,19 @@ +{ + "node": true, + "freeze": true, + "maxlen": 100, + "smarttabs": true, + "indent": 1, + "quotmark": "single", + "strict": true, + "globalstrict": true, + // use es3 to get 'extra comma' at object literal error + "es3": true, + "undef": true, + "unused": true, + "immed": true, + "eqeqeq": true, + "globals": { + "JSON": false + } +} diff --git a/legacy/json-sql/.npmignore b/legacy/json-sql/.npmignore new file mode 100644 index 00000000..3796175f --- /dev/null +++ b/legacy/json-sql/.npmignore @@ -0,0 +1,2 @@ +.npmignore +test \ No newline at end of file diff --git a/legacy/json-sql/LICENSE b/legacy/json-sql/LICENSE new file mode 100644 index 00000000..2ac98523 --- /dev/null +++ b/legacy/json-sql/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2013-2014 Oleg Korobenko + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/legacy/json-sql/README.md b/legacy/json-sql/README.md new file mode 100644 index 00000000..7891eaed --- /dev/null +++ b/legacy/json-sql/README.md @@ -0,0 +1,145 @@ +# JSON-SQL + +Library for mapping mongo-style query objects to SQL queries. + +## Quick Start + +Install it with NPM or add it to your package.json: + +``` bash +$ npm install json-sql +``` + +Then: + +``` js +var jsonSql = require('json-sql')(); + +var sql = jsonSql.build({ + type: 'select', + table: 'users', + fields: ['name', 'age'], + condition: {name: 'Max', id: 6} +}); + +sql.query +// sql string: +// select name, age from users where name = $p1 && id = 6; + +sql.values +// hash of values: +// { p1: 'Max' } +``` + +## Documentation + +Documentation is available at the [./docs directory](./docs). + +## Examples + +__Select with join:__ + +``` js +var sql = jsonSql.build({ + type: 'select', + table: 'users', + join: { + documents: { + on: {'user.id': 'documents.userId'} + } + } +}); + +sql.query +// select * from users join documents on user.id = documents.userId; + +sql.values +// {} +``` + +__Insert:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'users', + values: { + name: 'John', + lastname: 'Snow', + age: 24, + gender: 'male' + } +}); + +sql.query +// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3); + +sql.values +// { p1: 'John', p2: 'Snow', p3: 'male' } +``` + +__Update:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'users', + condition: { + id: 5 + }, + modifier: { + role: 'admin' + age: 33 + } +}); + +sql.query +// update users set role = $p1, age = 33 where id = 5; + +sql.values +// { p1: 'admin' } +``` + +__Remove:__ + +``` js +var sql = jsonSql.build({ + type: 'remove', + table: 'users', + condition: { + id: 5 + } +}); + +sql.query +// delete from users where id = 5; + +sql.values +// {} +``` + +For more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests). + +## Tests + +Clone repository from github, `cd` into cloned dir and install dev dependencies: + +``` bash +$ npm install +``` + +Then run tests with command: + +``` bash +$ gulp test +``` + +Or run tests coverage with command: + +``` bash +$ gulp coverage +``` + +## License + +[MIT](./LICENSE) diff --git a/legacy/json-sql/docs/README.md b/legacy/json-sql/docs/README.md new file mode 100644 index 00000000..8752f703 --- /dev/null +++ b/legacy/json-sql/docs/README.md @@ -0,0 +1,1180 @@ +# Documentation + +## Table of contents + +* [API](#api) + - [Initialization](#initialization) + - __[build(query)](#buildquery)__ + - [configure(options)](#configureoptions) + - [setDialect(name)](#setdialectname) +* __[Queries](#queries)__ + - [type: 'create'](#type-create) + - [type: 'select'](#type-select) + - [type: 'insert'](#type-insert) + - [type: 'update'](#type-update) + - [type: 'remove'](#type-remove) + - [type: 'union' | 'intersect' | 'except'](#type-union--intersect--except) +* __[Blocks](#blocks)__ +* __[Condition operators](#condition-operators)__ + +--- + +## API + +### Initialization + +To create new instance of json-sql builder you can use factory function: + +``` js +var jsonSql = require('json-sql')(options); +``` + +or create instance by class constructor: + +``` js +var jsonSql = new (require('json-sql').Builder)(options); +``` + +`options` are similar to [configure method options](#available-options). + +--- + +### build(query) + +Create sql query from mongo-style query object. + +`query` is a json object that has required property `type` and a set of query-specific properties. `type` property determines the type of query. List of available values of `type` property you can see at [Queries section](#queries). + +Returns object with properties: + +| Property | Description | +| -------- | ----------- | +| `query` | SQL query string | +| `value` | Array or object with values.
Exists only if `separatedValues = true`. | +| `prefixValues()` | Method to get values with `valuesPrefix`.
Exists only if `separatedValues = true`. | +| `getValuesArray()` | Method to get values as array.
Exists only if `separatedValues = true`. | +| `getValuesObject()` | Method to get values as object.
Exists only if `separatedValues = true`. | + +--- + +### configure(options) + +Set options of json-sql builder instance. + +#### Available options + +| Option name | Default value | Description | +| ----------- | ------------- | ----------- | +| `separatedValues` | `true` | If `true` - create placeholder for each string value and put it value to result `values`.
If `false` - put string values into sql query without placeholder (potential threat of sql injection). | +| `namedValues` | `true` | If `true` - create hash of values with placeholders p1, p2, ...
If `false` - put all values into array.
Option is used if `separatedValues = true`. | +| `valuesPrefix` | `'$'` | Prefix for values placeholders
Option is used if `namedValues = true`. | +| `dialect` | `'base'` | Active dialect. See setDialect for dialects list. | +| `wrappedIdentifiers` | `true` | If `true` - wrap all identifiers with dialect wrapper (name -> "name"). | + +--- + +### setDialect(name) + +Set active dialect, name can has value `'base'`, `'mssql'`, `'mysql'`, `'postrgresql'` or `'sqlite'`. + +--- + +## Queries + +### type: 'create' + +>[ [tableFields](#tableFields) ]
+>[ [table](#table) ]
+>[ [foreignKeys](#foreignKeys) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + unique: true, + default: "empty" + } + ] +}); + +sql.query +// create table if not exists "users"("name" varchar(16) NOT NULL default "empty" UNIQUE); +``` + +--- + +### type: 'select' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[ [distinct](#distinct) ]
+>[ [fields](#fields) ]
+>[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
+>[ [alias](#alias) ]
+>[ [join](#join) ]
+>[ [condition](#condition) ]
+>[ [group](#group) ]
+>[ [sort](#sort) ]
+>[ [limit](#limit) ]
+>[ [offset](#offset) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'select', + fields: ['a', 'b'] + table: 'table' +}); + +sql.query +// select "a", "b" from "table"; +``` + +If `fields` is not specified in query, result fields is `*` (all columns of the selected rows). + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'select', + table: 'table' +}); + +sql.query +// select * from "table"; +``` + +--- + +### type: 'insert' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[ [or](#or) ]
+>[table](#table)
+>[values](#values)
+>[ [condition](#condition) ]
+>[ [returning](#returning) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: {a: 4} +}); + +sql.query +// insert into "table" ("a") values (4); +``` + +--- + +### type: 'update' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[ [or](#or) ]
+>[table](#table)
+>[modifier](#modifier)
+>[ [condition](#condition) ]
+>[ [returning](#returning) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'table', + modifier: {a: 5} +}); + +sql.query +// update "table" set a = 5; +``` + +--- + +### type: 'remove' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[table](#table)
+>[ [condition](#condition) ]
+>[ [returning](#returning) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'remove', + table: 'table' +}); + +sql.query +// delete from "table"; +``` + +--- + +### type: 'union' | 'intersect' | 'except' + +>[ [all](#all) ]
+>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[queries](#queries)
+>[ [sort](#sort) ]
+>[ [limit](#limit) ]
+>[ [offset](#offset) ] + +__`type: 'union'` example:__ + +``` js +var sql = jsonSql.build({ + type: 'union', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") union (select * from "table2"); +``` + +__`type: 'intersect'` example:__ + +``` js +var sql = jsonSql.build({ + type: 'intersect', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") intersect (select * from "table2"); +``` + +__`type: 'except'` example:__ + +``` js +var sql = jsonSql.build({ + type: 'except', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") except (select * from "table2"); +``` + +--- + +## Blocks + +Blocks are small chunks of query. + +### with, withRecursive + +Should be an `array` or an `object`. + +If value is an `array`, each item of array should be an `object` and should conform the scheme: + +>name
+>[ [fields](#fields) ]
+>[query](#query) | [select](#select) | [expression](#expression) + +__Example:__ + +``` js +var sql = jsonSql.build({ + 'with': [{ + name: 'table', + select: {table: 'withTable'} + }], + table: 'table' +}); + +sql.query +// with "table" as (select * from "withTable") select * from "table"; +``` + +If value is an `object`, keys of object interpret as names and each value should be an `object` and should conform the scheme: + +>[ name ]
+>[ [fields](#fields) ]
+>[query](#query) | [select](#select) | [expression](#expression) + +__Example:__ + +``` js +var sql = jsonSql.build({ + 'with': { + table: { + select: {table: 'withTable'} + } + }, + table: 'table' +}); + +sql.query +// with "table" as (select * from "withTable") select * from "table"; +``` + +--- + +### distinct + +Should be a `boolean`: + +``` +distinct: true +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + distinct: true, + table: 'table' +}); + +sql.query +// select distinct * from "table"; +``` + +--- + +### tableFields + +Should be an `array` + +Contains array of table`s fields + +--- + +### foreignKeys + +Should be an `array` + +__Example:__ + +``` js +var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ], + foreignKeys: [ + { + field: "name", + table: "person", + table_field: "id" + } + ] +}); + +sql.query +// create table "users"(name varchar(16) NOT NULL PRIMARY KEY,age int NOT NULL, FOREIGN KEY (name) REFERENCES person(id)); +``` + +--- + +### fields + +Should be an `array` or an `object`. + +If value is an `array`, each item interprets as [term block](#term). + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [ + 'a', + {b: 'c'}, + {table: 'd', name: 'e', alias: 'f'}, + ['g'] + ], + table: 'table' +}); + +sql.query +// select "a", "b" as "c", "d"."e" as "f", "g" from "table"; +``` + +If value is an `object`, keys of object interpret as field names and each value should be an `object` and should conform the scheme: + +>[ name ]
+>[ [table](#table) ]
+>[ cast ]
+>[ [alias](#alias) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: { + a: 'b', + d: {table: 'c', alias: 'e'} + }, + table: 'table' +}); + +sql.query +// select "a" as "b", "c"."d" as "e" from "table"; +``` + +--- + +### term + +Should be: +* a `string` - interprets as field name; +* another simple type or an `array` - interprets as value; +* an `object` - should conform the scheme: + +>[query](#query) | [select](#select) | [field](#field) | [value](#value) | [func](#func) | [expression](#expression)
+>[ cast ]
+>[ [alias](#alias) ] + +--- + +### field + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +field: 'fieldName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{field: 'a'}], + table: 'table' +}); + +sql.query +// select "a" from "table"; +``` + +If value is an `object` it should conform the scheme: + +>name
+>[ [table](#table) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{field: {name: 'a', table: 'table'}}], + table: 'table' +}); + +sql.query +// select "table"."a" from "table"; +``` + +--- + +### value + +Can have any type. + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [ + {value: 5}, + {value: 'test'} + ], + table: 'table' +}); + +sql.query +// select 5, $p1 from "table"; + +sql.values +// {p1: 'test'} +``` + +--- + +### table + +Should be a `string`: + +``` +table: 'tableName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table' +}); + +sql.query +// select * from "table"; +``` + +--- + +### query + +Should be an `object`. Value interprets as sub-query and process recursively with [build(query)](#buildquery) method. + +__Example:__ + +``` js +var sql = jsonSql.build({ + query: {type: 'select', table: 'table'} +}); + +sql.query +// select * from (select * from "table"); +``` + +--- + +### select + +Should be an `object`. Value interprets as sub-select and process recursively with [build(query)](#buildquery) method. + +__Example:__ + +``` js +var sql = jsonSql.build({ + select: {table: 'table'} +}); + +sql.query +// select * from (select * from "table"); +``` + +--- + +### func + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +func: 'random' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{func: 'random'}], + table: 'table' +}); + +sql.query +// select random() from "table"; +``` + +If value is an `object` it should conform the scheme: + +>name
+>[ args ] + +where `name` is a `string` name of function, `args` is an `array` that contains it arguments. + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{ + func: { + name: 'sum', + args: [{field: 'a'}] + } + }], + table: 'table' +}); + +sql.query +// select sum("a") from table; +``` + +--- + +### expression + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +expression: 'random()' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + expression: 'generate_series(2, 4)' +}); + +sql.query +// select * from generate_series(2, 4); +``` + +If value is an `object` it should conform the scheme: + +>pattern
+>[ values ] + +where `pattern` is a `string` pattern with placeholders `{placeholderName}`, `values` is a hash that contains values for each `placeholderName`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + expression: { + pattern: 'generate_series({start}, {stop})', + values: {start: 2, stop: 4} + } +}); + +sql.query +// select * from generate_series(2, 4); +``` + +--- + +### alias + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +alias: 'aliasName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + alias: 'alias' +}); + +sql.query +// select * from "table" as "alias"; +``` + +If value is an `object` it should conform the scheme: + +>name
+>[ columns ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + alias: {name: 'alias'} +}); + +sql.query +// select * from "table" as "alias"; +``` + +--- + +### join + +Should be an `array` or an `object`. + +If value is an `array`, each item of array should be an `object` and should conform the scheme: + +>[ type ]
+>[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
+>[ [alias](#alias) ]
+>[ on ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + join: [{ + type: 'right', + table: 'joinTable', + on: {'table.a': 'joinTable.b'} + }] +}); + +sql.query +// select * from "table" right join "joinTable" on "table"."a" = "joinTable"."b"; +``` + +If value is an `object`, keys of object interpret as table names and each value should be an `object` and should conform the scheme: + +>[ type ]
+>[ [table](#table) | [query](#query) | [select](#select) | [expression](#expression) ]
+>[ [alias](#alias) ]
+>[ on ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + join: { + joinTable: { + type: 'inner', + on: {'table.a': 'joinTable.b'} + } + }] +}); + +sql.query +// select * from "table" inner join "joinTable" on "table"."a" = "joinTable"."b"; +``` + +__Join with sub-select example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + join: [{ + select: {table: 'joinTable'}, + alias: 'joinTable', + on: {'table.a': 'joinTable.b'} + }] +}); + +sql.query +// select * from "table" join (select * from "joinTable") as "joinTable" on "table"."a" = "joinTable"."b"; +``` + +--- + +### condition + +Should be an `array` or an `object`. + +__`array` example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + condition: [ + {a: {$gt: 1}}, + {b: {$lt: 10}} + ] +}); + +sql.query +// select * from "table" where "a" > 1 and "b" < 10; +``` + +__`object` example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + condition: { + a: {$gt: 1}, + b: {$lt: 10} + } +}); + +sql.query +// select * from "table" where "a" > 1 and "b" < 10; +``` + +--- + +### group + +Should be a `string` or an `array`. + +If value is a `string`: + +``` +group: 'fieldName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + group: 'a' +}); + +sql.query +// select * from "table" group by "a"; +``` + +If value is an `array`: + +``` +group: ['fieldName1', 'fieldName2'] +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + group: ['a', 'b'] +}); + +sql.query +// select * from "table" group by "a", "b"; +``` + +--- + +### sort + +Should be a `string`, an `array` or an `object`. + +If value is a `string`: + +``` +sort: 'fieldName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + sort: 'a' +}); + +sql.query +// select * from "table" order by "a"; +``` + +If value is an `array`: + +``` +sort: ['fieldName1', 'fieldName2'] +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + sort: ['a', 'b'] +}); + +sql.query +// select * from "table" order by "a", "b"; +``` + +If value is an `object`: + +``` +sort: { + fieldName1: 1, + fieldName2: -1 +} +``` + +__Example__: + +``` js +var sql = jsonSql.build({ + table: 'table', + sort: {a: 1, b: -1} +}); + +sql.query +// select * from "table" order by "a" asc, "b" desc; +``` + +--- + +### limit + +Should be a `number`. + +``` +limit: limitValue +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + limit: 5 +}); + +sql.query +// select * from "table" limit 5; +``` + +--- + +### offset + +Should be a `number`. + +``` +offset: offsetValue +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + offset: 5 +}); + +sql.query +// select * from "table" offset 5; +``` + +--- + +### or + +Should be a `string`. + +Available values: 'rollback', 'abort', 'replace', 'fail', 'ignore'. + +``` +or: 'orValue' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + or: 'replace', + table: 'table', + values: {a: 5} +}); + +sql.query +// insert or replace into "table" ("a") values (5); +``` + +--- + +### values + +Should be an `array` or an `object`. + +If value is an `array`, each item should be an `object` and interprets as single inserted row where keys are field names and corresponding values are field values. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: [ + {a: 5, b: 'text1'}, + {a: 6, b: 'text2'} + ] +}); + +sql.query +// insert into "table" ("a", "b") values (5, $p1), (6, $p2); + +sql.values +// {p1: 'text1', p2: 'text2'} +``` + +If value is an `object`, it interprets as single inserted row where keys are field names and corresponding values are field values. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: {a: 5, b: 'text'} +}); + +sql.query +// insert into "table" ("a", "b") values (5, $p1); + +sql.values +// {p1: 'text'} +``` + +Also you can specify fields array. If there no key in value object it value is `null`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + fields: ['a', 'b', 'c'], + values: {c: 'text', b: 5} +}); + +sql.query +// insert into "table" ("a", "b", "c") values (null, 5, $p1); + +sql.values +// {p1: 'text'} +``` + +--- + +### modifier + +Should be an `object`. + +You can specify modifier operator. +Available operators: `$set`, `$inc`, `$dec`, `$mul`, `$div`, `$default`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'table', + modifier: { + $set: {a: 5}, + $default: {b: true}, + $inc: {c: 10} + } +}); + +sql.query +// update "table" set "a" = 5, "b" = default, "c" = "c" + 10; +``` + +If modifier operator is not specified it uses default operator `$set`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'table', + modifier: {a: 5} +}); + +sql.query +// update "table" set "a" = 5; +``` + +--- + +### returning + +Format is similar to [fields](#fields) block. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: {a: 5}, + returning: ['a'] +}); + +sql.query +// insert into "table" ("a") values (5) returning "a"; +``` + +--- + +### all + +Should be a `boolean`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'union', + all: true, + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") union all (select * from "table2"); +``` + +--- + +### queries + +Should be an `array` with minimum 2 items. Each item interprets as sub-query and process recursively with [build(query)](#buildquery) method. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'union', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") union (select * from "table2"); + +//or for sqlite3 +jsonSql.setDialect("sqlite"); +var sql = jsonSql.build({ + type: 'union', + unionqueries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// select * from "table1" union select * from "table2"; +``` + +--- + +## Condition operators + +TODO: write this section \ No newline at end of file diff --git a/legacy/json-sql/gulpfile.js b/legacy/json-sql/gulpfile.js new file mode 100644 index 00000000..c7bbf59b --- /dev/null +++ b/legacy/json-sql/gulpfile.js @@ -0,0 +1,27 @@ +'use strict'; + +var gulp = require('gulp'); +var mocha = require('gulp-mocha'); +var jshint = require('gulp-jshint'); + +gulp.task('test', function() { + return gulp.src(['./tests/*.js'], {read: false}) + .pipe( + mocha({ + reporter: 'spec', + bail: true + }) + ); +}); + +gulp.task('lint', function() { + return gulp.src('./lib/**/*.js') + .pipe(jshint()) + .pipe(jshint.reporter('unix')); +}); + +gulp.task('lintTests', function() { + return gulp.src('./tests/*.js') + .pipe(jshint()) + .pipe(jshint.reporter('unix')); +}); \ No newline at end of file diff --git a/legacy/json-sql/lib/builder.js b/legacy/json-sql/lib/builder.js new file mode 100644 index 00000000..fa0d2a0d --- /dev/null +++ b/legacy/json-sql/lib/builder.js @@ -0,0 +1,233 @@ +'use strict'; + +var _ = require('underscore'); + +var dialects = { + base: require('./dialects/base'), + mssql: require('./dialects/mssql'), + postgresql: require('./dialects/postgresql'), + sqlite: require('./dialects/sqlite') +}; + +var blockRegExp = /\{([a-z0-9]+)\}(.|$)/ig; + +var Builder = module.exports = function (options) { + this.configure(options); +}; + +Builder.prototype._reset = function () { + if (this.options.separatedValues) { + this._placeholderId = 1; + this._values = this.options.namedValues ? {} : []; + } else { + delete this._placeholderId; + delete this._values; + } + + this._query = ''; +}; + +Builder.prototype._getPlaceholder = function () { + return (this.options.namedValues ? 'p' : '') + (this._placeholderId++); +}; + +Builder.prototype._wrapPlaceholder = function (name) { + return this.options.valuesPrefix + '{' + name + '}'; +}; + +Builder.prototype._pushValue = function (value) { + var valueType = typeof value; + + if (valueType === 'undefined') { + return 'null'; + } else if (value === null || valueType === 'number' || + valueType === 'boolean') { + return String(value); + } else if (valueType === 'string') { + if (this.options.separatedValues) { + var placeholder = this._getPlaceholder(); + if (this.options.namedValues) { + this._values[placeholder] = value; + } else { + this._values.push(value); + } + return this._wrapPlaceholder(placeholder); + } else { + return '\'' + value + '\''; + } + } else if (valueType == 'object') { + if (Object.prototype.toString.call(value) == "[object Array]") { + if (value.length > 0) { + return value.join(",") + } else { + return 'null'; + } + } + else if (Buffer.isBuffer(value)) { + var placeholder = this._getPlaceholder(); + this._values[placeholder] = value; + return this._wrapPlaceholder(placeholder); + //return "X'" + value.toString('hex') + "'"; + } else { + var placeholder = this._getPlaceholder(); + this._values[placeholder] = value; + return this._wrapPlaceholder(placeholder); + //return ("'" + JSON.stringify(value).replace(/'/g, "''") + "'"); + } + } else { + throw new Error('Wrong value type "' + valueType + '"'); + } +}; + +Builder.prototype.configure = function (options) { + options = options || {}; + + this.options = _(options).defaults({ + separatedValues: true, + namedValues: true, + valuesPrefix: '$', + dialect: 'base', + wrappedIdentifiers: true + }); + + this.setDialect(this.options.dialect); + + this._reset(); +}; + +Builder.prototype.buildCondition = function (params) { + var self = this; + var result = ''; + + var condition = params.condition; + var logicalOperator = params.logicalOperator || + this.dialect.config.defaultLogicalOperator; + + if (_.isObject(condition) && !_.isEmpty(condition)) { + // if condition is array: [{a: 1}, {b: 2}] + if (_.isArray(condition)) { + result = _(condition).map(function (item) { + return self.buildCondition({ + condition: item, + operator: params.operator + }); + }); + + // if condition is object + } else { + result = _(condition).map(function (value, field) { + // if field name is operator + if (field[0] === '$') { + // if field name is logical operator: {$logicalOperator: {a: 1}} + if (!self.dialect.logicalOperators.has(field)) { + throw new Error('Unknown logical operator "' + field + '"'); + } + + return self.buildCondition({ + condition: value, + logicalOperator: field, + operator: params.operator + }); + + // if condition item is object: {a: {$operator: 4}} + } else if (_.isObject(value)) { + var logicalOperatorFn = self.dialect.logicalOperators + .get(self.dialect.config.defaultLogicalOperator); + + return logicalOperatorFn(_(value).map(function (operatorValue, operator) { + var operatorFn = self.dialect.conditions.get(operator); + if (!operatorFn) { + throw new Error('Unknown operator "' + operator + '"'); + } + return operatorFn(field, operator, operatorValue); + })); + + // if condition item type is simple: {a: 1, b: 2} + } else { + var operatorFn = self.dialect.conditions.get(params.operator); + if (!operatorFn) { + throw new Error('Unknown operator "' + params.operator + '"'); + } + return operatorFn(field, params.operator, value); + } + }); + } + + result = this.dialect.logicalOperators + .get(logicalOperator)(_(result).compact()); + } + + return result; +}; + +Builder.prototype.buildBlock = function (block, params) { + var blockFn = this.dialect.blocks.get(block); + + if (!blockFn) { + throw new Error('Unknown block "' + block + '"'); + } + + return blockFn(params); +}; + +Builder.prototype.buildTemplate = function (type, params) { + var self = this; + + var template = this.dialect.templates.get(type); + if (!template) { + throw new Error('Unknown template type "' + type + '"'); + } + + params = _({}).defaults(params, template.defaults); + + if (template.validate) { + template.validate(type, params); + } + + return template.pattern.replace(blockRegExp, function (fullMatch, block, space) { + if (typeof params[block] !== 'undefined') { + return self.buildBlock(block, params) + space; + } else { + return ''; + } + }).trim(); +}; + +Builder.prototype.build = function (params) { + var builder = this; + + this._reset(); + + this._query = this.buildTemplate('query', {queryBody: params}) + ';'; + + if (this.options.separatedValues) { + return { + query: this._query, + values: this._values, + prefixValues: function () { + var values = {}; + _(this.getValuesObject()).each(function (value, name) { + values[builder._wrapPlaceholder(name)] = value; + }); + return values; + }, + getValuesArray: function () { + return _.isArray(this.values) ? this.values : _(this.values).values(); + }, + getValuesObject: function () { + return _.isArray(this.values) ? _(_.range(1, this.values.length + 1)).object(this.values) : + this.values; + } + }; + } else { + return {query: this._query}; + } +}; + +Builder.prototype.setDialect = function (name) { + if (!dialects[name]) { + throw new Error('Unknown dialect "' + name + '"'); + } + + this.dialect = new (dialects[name])(this); +}; diff --git a/legacy/json-sql/lib/dialects/base/blocks.js b/legacy/json-sql/lib/dialects/base/blocks.js new file mode 100644 index 00000000..809c5fd6 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/blocks.js @@ -0,0 +1,441 @@ +'use strict'; + +var _ = require('underscore'); +var scheme = require('./scheme.js'); + +function removeTopBrackets(condition) { + if (condition.length && condition[0] === '(' && + condition[condition.length - 1] === ')') { + condition = condition.slice(1, condition.length - 1); + } + + return condition; +} + +module.exports = function(dialect) { + dialect.blocks.add('distinct', function() { + return 'distinct'; + }); + + + dialect.blocks.add('field', function(params) { + var field = params.field || params.$field; + var expression = params.expression || params.$expression; + + if (!field && !expression) { + throw new Error('Neither `field` nor `expression` properties aren\'t set'); + } + + if (field) { + field = this.dialect._wrapIdentifier(field); + + var table = params.table || params.$table; + if (table) { + field = this.buildBlock('table', {table: table}) + '.' + field; + } + + if (expression) { + if (!_.isArray(expression)) expression = [expression]; + var exprSuffix = (new Array(expression.length + 1)).join(')'); + field = expression.join('(') + '(' + field + exprSuffix; + } + } else { + field = expression; + } + + var cast = params.cast || params.$cast; + if (cast) { + field = 'cast(' + field + ' as ' + cast + ')'; + } + + var alias = params.alias || params.$alias; + if (alias) { + field += ' ' + this.buildBlock('alias', {alias: alias}); + } + + return field; + }); + + + dialect.blocks.add('fields', function(params) { + var self = this; + + var fields = params.fields || {}; + var result = ''; + + if (!_.isObject(fields) && !_.isString(fields)) { + throw new Error('Invalid `fields` property type "' + (typeof fields) + '"'); + } + + if (_.isObject(fields)) { + if (_.isEmpty(fields)) { + result = '*'; + } else { + // If fields is array: ['a', {b: 'c'}, {field: '', table: 't', alias: 'r'}] + if (_.isArray(fields)) { + result = _(fields).map(function(item) { + + if (_.isObject(item)) { + // if field is field object: {field: '', table: 't', alias: 'r'} + if (item.field || item.expression) { + return self.buildBlock('field', item); + + // if field is non-field object: {b: 'c'} + } else { + return self.buildBlock('fields', {fields: item}); + } + + // if field is string: 'a' + } else if (_.isString(item)) { + return self.buildBlock('field', {field: item}); + } + }); + + // If fields is object: {a: 'u', b: {table: 't', alias: 'c'}} + } else { + // use keys as field names + result = _(fields).map(function(item, field) { + // b: {table: 't', alias: 'c'} + if (_.isObject(item)) { + if (!item.field) { + item = _.clone(item); + item.field = field; + } + + return self.buildBlock('field', item); + + // a: 'u' + } else if (_.isString(item)) { + return self.buildBlock('field', { + field: field, + alias: item + }); + } + + return ''; + }); + } + + result = result.join(', '); + } + } else { + result = fields; + } + + return result; + }); + + dialect.blocks.add('table', function(params) { + return this.dialect._wrapIdentifier(params.table); + }); + + dialect.blocks.add('expression', function(params) { + return params.expression; + }); + + dialect.blocks.add('name', function(params) { + return this.dialect._wrapIdentifier(params.name); + }); + + dialect.blocks.add('alias', function(params) { + var self = this; + + var result; + + if (_.isObject(params.alias)) { + if (!params.alias.name) { + throw new Error('Alias `name` property is required'); + } + + result = this.dialect._wrapIdentifier(params.alias.name); + + if (_.isArray(params.alias.columns)) { + result += '(' + _(params.alias.columns).map(function(column) { + return self.dialect._wrapIdentifier(column); + }).join(', ') + ')'; + } + } else { + result = this.dialect._wrapIdentifier(params.alias); + } + + return 'as ' + result; + }); + + dialect.blocks.add('condition', function(params) { + var condition = params.condition; + var result = ''; + + if (_.isObject(condition)) { + result = this.buildCondition({ + condition: condition, + operator: '$eq' + }); + } else if (_.isString(condition)) { + result = condition; + } + + if (result) { + result = 'where ' + removeTopBrackets(result); + } + + return result; + }); + + dialect.blocks.add('modifier', function(params) { + var self = this; + + var modifier = params.modifier; + var result = ''; + + // if modifier is object -> call method for each operator + if (_.isObject(modifier)) { + result = _(modifier).map(function(values, field) { + var modifierFn = self.dialect.modifiers.get(field); + var methodParams = values; + + if (!modifierFn) { + modifierFn = self.dialect.modifiers.get(self.dialect.config.defaultModifier); + methodParams = {}; + methodParams[field] = values; + } + + return modifierFn.call(self, methodParams); + }).join(', '); + + // if modifier is string -> not process it + } else if (_.isString(modifier)) { + result = modifier; + } + + if (result) { + result = 'set ' + result; + } + + return result; + }); + + dialect.blocks.add('join', function(params) { + var self = this; + + var join = params.join; + var result = ''; + + // if join is array -> make each joinItem + if (_.isArray(join)) { + result = _(join).map(function(joinItem) { + return self.buildTemplate('joinItem', joinItem); + }).join(' '); + + // if join is object -> set table name from key and make each joinItem + } else if (_.isObject(join)) { + result = _(join).map(function(joinItem, table) { + if (!joinItem.table && !joinItem.query && !joinItem.select) { + joinItem = _.clone(joinItem); + joinItem.table = table; + } + + return self.buildTemplate('joinItem', joinItem); + }).join(' '); + + // if join is string -> not process + } else if (_.isString(join)) { + result = join; + } + + return result; + }); + + dialect.blocks.add('type', function(params) { + return params.type.toLowerCase(); + }); + + + dialect.blocks.add('on', function(params) { + var on = params.on; + var result = ''; + + // `on` block is use `$field` as default compare operator + // because it most used case + if (_.isObject(on)) { + result = this.buildCondition({ + condition: on, + operator: '$field' + }); + } else if (_.isString(on)) { + result = on; + } + + if (result) { + result = 'on ' + removeTopBrackets(result); + } + + return result; + }); + + + dialect.blocks.add('group', function(params) { + var result = ''; + var group = params.group; + + if (_.isArray(group)) { + var self = this; + result = _(group).map(function(field) { + return self.dialect._wrapIdentifier(field); + }).join(', '); + } else if (_.isString(group)) { + result = this.dialect._wrapIdentifier(group); + } else if (_.isObject(group)) { + result = group.expression + (group.having ? " having " + this.buildCondition({ + condition: group.having + }) : ""); + } + + if (result) { + result = 'group by ' + result; + } + + return result; + }); + + + dialect.blocks.add('sort', function(params) { + var result = ''; + var sort = params.sort; + + // if sort is array -> field1, field2, ... + var self = this; + if (_.isArray(sort)) { + result = _(sort).map(function(sortField) { + return self.dialect._wrapIdentifier(sortField); + }).join(', '); + + // if sort is object -> field1 asc, field2 desc, ... + } else if (_.isObject(sort)) { + result = _(sort).map(function(direction, field) { + return self.dialect._wrapIdentifier(field) + ' ' + + (direction > 0 ? 'asc' : 'desc'); + }).join(', '); + + // if sort is string -> not process + } else if (_.isString(sort)) { + result = this.dialect._wrapIdentifier(sort); + } + + if (result) { + result = 'order by ' + result; + } + + return result; + }); + + + dialect.blocks.add('limit', function(params) { + return 'limit ' + this._pushValue(params.limit); + }); + + + dialect.blocks.add('offset', function(params) { + return 'offset ' + this._pushValue(params.offset); + }); + + + dialect.blocks.add('or', function(params) { + return 'or ' + params.or; + }); + + + dialect.blocks.add('values', function(params) { + var self = this; + + var fieldValues = params.values; + + if (!_.isArray(fieldValues)) { + fieldValues = [fieldValues]; + } + + var fields = params.fields || _(fieldValues).chain().map(function(values) { + return _(values).keys(); + }).flatten().uniq().value(); + + fieldValues = _(fieldValues).map(function(values) { + return _(fields).map(function(field) { + return self._pushValue(values[field]); + }); + }); + + return this.buildTemplate('insertValues', { + fields: fields, + fieldValues: fieldValues + }); + }); + + dialect.blocks.add('fieldValues', function(params) { + return _(params.fieldValues).map(function(values) { + return '(' + values.join(', ') + ')'; + }).join(', '); + }); + + dialect.blocks.add('queryBody', function(params) { + var query = params.queryBody || {}; + + return this.buildTemplate(query.type || 'select', query); + }); + + dialect.blocks.add('query', function(params) { + return this.buildTemplate('subQuery', {queryBody: params.query}); + }); + + dialect.blocks.add('select', function(params) { + return this.buildTemplate('subQuery', {queryBody: params.select}); + }); + + dialect.blocks.add('tableFields', function (params) { + return scheme.parse.call(this, params.tableFields, params.foreignKeys); + }); + + dialect.blocks.add('queries', function(params) { + var self = this; + + return _(params.queries).map(function(query) { + return self.buildTemplate('subQuery', {queryBody: query}); + }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); + }); + + dialect.blocks.add('indexOn', function (params) { + return params.indexOn; + }); + + dialect.blocks.add('with', function(params) { + var self = this; + + var withList = params['with']; + var result = ''; + + // if with clause is array -> make each withItem + if (_.isArray(withList)) { + result = _(withList).map(function(withItem) { + return self.buildTemplate('withItem', withItem); + }).join(', '); + + // if with clause is object -> set name from key and make each withItem + } else if (_.isObject(withList)) { + result = _(withList).map(function(withItem, name) { + if (!withItem.name) { + withItem = _.clone(withItem); + withItem.name = name; + } + return self.buildTemplate('withItem', withItem); + }).join(', '); + + // if with clause is string -> not process + } else if (_.isString(withList)) { + result = withList; + } + + return 'with ' + result; + }); + + dialect.blocks.add('returning', function(params) { + return 'returning ' + this.buildBlock('fields', {fields: params.returning}); + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/conditions.js b/legacy/json-sql/lib/dialects/base/conditions.js new file mode 100644 index 00000000..c46be4f3 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/conditions.js @@ -0,0 +1,122 @@ +'use strict'; + +var _ = require('underscore'); + +// Compare conditions (e.g. $eq, $gt) +function buildCompareCondition(builder, field, operator, value) { + var placeholder; + + // if value is object, than make field block from it + if (value && _.isObject(value)) { + placeholder = builder.buildBlock('field', value); + } else { + // if value is simple - create placeholder for it + placeholder = builder._pushValue(value); + } + + field = builder.dialect._wrapIdentifier(field); + return [field, operator, placeholder].join(' '); +} + +// Contain conditions ($in/$nin) +function buildContainsCondition(builder, field, operator, value) { + var newValue; + + if (_.isArray(value)) { + if (!value.length) value = [null]; + + newValue = '(' + _(value).map(function(item) { + return builder._pushValue(item); + }).join(', ') + ')'; + } else if (_.isObject(value)) { + newValue = builder.buildTemplate('subQuery', {queryBody: value}); + } else { + throw new Error('Invalid `' + operator + '` value type "' + (typeof value) + '"'); + } + + field = builder.dialect._wrapIdentifier(field); + return [field, operator, newValue].join(' '); +} + +module.exports = function(dialect) { + dialect.conditions.add('$eq', function(field, operator, value) { + return buildCompareCondition(this, field, '=', value); + }); + + dialect.conditions.add('$ne', function(field, operator, value) { + return buildCompareCondition(this, field, '!=', value); + }); + + dialect.conditions.add('$gt', function(field, operator, value) { + return buildCompareCondition(this, field, '>', value); + }); + + dialect.conditions.add('$lt', function(field, operator, value) { + return buildCompareCondition(this, field, '<', value); + }); + + dialect.conditions.add('$gte', function(field, operator, value) { + return buildCompareCondition(this, field, '>=', value); + }); + + dialect.conditions.add('$lte', function(field, operator, value) { + return buildCompareCondition(this, field, '<=', value); + }); + + dialect.conditions.add('$is', function(field, operator, value) { + return buildCompareCondition(this, field, 'is', value); + }); + + dialect.conditions.add('$isnot', function(field, operator, value) { + return buildCompareCondition(this, field, 'is not', value); + }); + + dialect.conditions.add('$like', function(field, operator, value) { + return buildCompareCondition(this, field, 'like', value); + }); + + dialect.conditions.add('$null', function(field, operator, value) { + return buildCompareCondition(this, field, 'is' + (value ? '' : ' not'), null); + }); + + dialect.conditions.add('$field', function(field, operator, value) { + var placeholder; + + // if value is object, than make field block from it + if (_.isObject(value)) { + placeholder = this.buildBlock('field', value); + } else { + // $field - special operator, that not make placeholder for value + placeholder = this.dialect._wrapIdentifier(value); + } + + return [this.dialect._wrapIdentifier(field), '=', placeholder].join(' '); + }); + + + dialect.conditions.add('$in', function(field, operator, value) { + return buildContainsCondition(this, field, 'in', value); + }); + + dialect.conditions.add('$nin', function(field, operator, value) { + return buildContainsCondition(this, field, 'not in', value); + }); + + dialect.conditions.add('$between', function(field, operator, value) { + if (!_.isArray(value)) { + throw new Error('Invalid `$between` value type "' + (typeof value) + '"'); + } + + if (value.length < 2) { + throw new Error('`$between` array length should be 2 or greater'); + } + + return [ + this.dialect._wrapIdentifier(field), + 'between', + this._pushValue(value[0]), + 'and', + this._pushValue(value[1]) + ].join(' '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/index.js b/legacy/json-sql/lib/dialects/base/index.js new file mode 100644 index 00000000..2a0e2c7a --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/index.js @@ -0,0 +1,58 @@ +'use strict'; + +var _ = require('underscore'); +var ValuesStore = require('../../valuesStore'); + +var templatesInit = require('./templates'); +var blocksInit = require('./blocks'); +var conditionsInit = require('./conditions'); +var logicalOperatorsInit = require('./logicalOperators'); +var modifiersInit = require('./modifiers'); + +var Dialect = module.exports = function(builder) { + this.builder = builder; + + this.blocks = new ValuesStore({context: builder}); + this.modifiers = new ValuesStore({context: builder}); + this.conditions = new ValuesStore({context: builder}); + this.logicalOperators = new ValuesStore({context: builder}); + this.templates = new ValuesStore({context: builder}); + + templatesInit(this); + blocksInit(this); + conditionsInit(this); + modifiersInit(this); + logicalOperatorsInit(this); + + this.identifierPartsRegexp = new RegExp( + '(\\' + this.config.identifierPrefix + '[^\\' + this.config.identifierSuffix + ']*\\' + + this.config.identifierSuffix + '|[^\\.]+)', 'g' + ); + this.wrappedIdentifierPartRegexp = new RegExp( + '^\\' + this.config.identifierPrefix + '.*\\' + this.config.identifierSuffix + '$' + ); +}; + +Dialect.prototype.config = { + identifierPrefix: '"', + identifierSuffix: '"', + defaultLogicalOperator: '$and', + defaultModifier: '$set' +}; + +Dialect.prototype._wrapIdentifier = function(name) { + if (this.builder.options.wrappedIdentifiers) { + var self = this; + var nameParts = name.match(this.identifierPartsRegexp); + + return _(nameParts).map(function(namePart) { + if (namePart !== '*' && !self.wrappedIdentifierPartRegexp.test(namePart)) { + namePart = self.config.identifierPrefix + namePart + self.config.identifierSuffix; + } + + return namePart; + }).join('.'); + } + + return name; +}; diff --git a/legacy/json-sql/lib/dialects/base/logicalOperators.js b/legacy/json-sql/lib/dialects/base/logicalOperators.js new file mode 100644 index 00000000..fb2879c1 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/logicalOperators.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('underscore'); + +function buildJoinOperator(conditions, operator) { + var isBracketsNeeded = conditions.length > 1; + var result = conditions.join(' ' + operator + ' '); + + if (result && isBracketsNeeded) result = '(' + result + ')'; + + return result; +} + +module.exports = function(dialect) { + dialect.logicalOperators.add('$and', function(conditions) { + return buildJoinOperator(conditions, 'and'); + }); + + dialect.logicalOperators.add('$or', function(conditions) { + return buildJoinOperator(conditions, 'or'); + }); + + dialect.logicalOperators.add('$not', function(conditions) { + var result = ''; + + if (_.isArray(conditions)) { + result = dialect.logicalOperators + .get(dialect.config.defaultLogicalOperator)(conditions); + } else if (_.isString(conditions)) { + result = conditions; + } + + if (result) result = 'not ' + result; + + return result; + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/modifiers.js b/legacy/json-sql/lib/dialects/base/modifiers.js new file mode 100644 index 00000000..3be42aa6 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/modifiers.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + dialect.modifiers.add('$set', function(values) { + var self = this; + + return _(values).map(function(value, field) { + var placeholder = self._pushValue(value); + + return [self.dialect._wrapIdentifier(field), '=', placeholder].join(' '); + }).join(', '); + }); + + dialect.modifiers.add('$inc', function(values) { + var self = this; + + return _(values).map(function(value, field) { + var placeholder = self._pushValue(value); + field = self.dialect._wrapIdentifier(field); + + return [field, '=', field, '+', placeholder].join(' '); + }).join(', '); + }); + + dialect.modifiers.add('$dec', function(values) { + var self = this; + + return _(values).map(function(value, field) { + var placeholder = self._pushValue(value); + field = self.dialect._wrapIdentifier(field); + + return [field, '=', field, '-', placeholder].join(' '); + }).join(', '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/scheme.js b/legacy/json-sql/lib/dialects/base/scheme.js new file mode 100644 index 00000000..686e16ed --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/scheme.js @@ -0,0 +1,146 @@ +var SchemeParser = function () { +} + +var types = { + "Number": {syntax: "int"}, + "BigInt": {syntax: "bigint"}, + "SmallInt": {syntax: "smallint"}, + "String": {syntax: "varchar", length: true}, + "Text": {syntax: "text"}, + "Real": {syntax: "real"}, + "Boolean": {syntax: "boolean"}, + "Blob": {syntax: "blob"}, + "Binary": {syntax: "bytea"} +} + +// for future +var onDeleteTrigger = { + "set_null": "SET NULL", + "cascade": "CASCADE" +} + +function getType(field) { + var s = ""; + var type = types[field.type]; + + if (!type) { + throw new Error("Invalid type of field: " + field.type); + } + + s += type.syntax; + + if (type.length) { + if (!field.length || field.length <= 0) { + throw new Error("Field length can't be less or equal 0"); + } + + s += "(" + field.length + ")"; + } else if (type.default_length) { + s += "(" + type.default_length + ")"; + } + + return s; +} + +function foreignkeys(fields, keys) { + if (!keys || keys.length == 0) { + return ""; + } + + var s = ", "; + var names = []; + fields.forEach(function (field) { + names.push(field.name) + }); + + keys.forEach(function (key, i) { + if (!key.field) { + throw new Error("Provide field for foreign key"); + } + + if (names.indexOf(key.field) < 0) { + throw new Error("Not exists field to make foreign key: " + key.field); + } + + if (!key.table || key.table.trim().length == 0) { + throw new Error("Invalid reference table name"); + } + + if (!key.table_field || key.table_field.trim().length == 0) { + throw new Error("Invalid reference table filed"); + } + + s += "FOREIGN KEY (\"" + key.field + "\") REFERENCES " + key.table + "(\"" + key.table_field + "\")" + (key.on_delete ? " ON DELETE " + key.on_delete : ""); + if (i != keys.length - 1) { + s += ","; + } + }); + + return s; +} + +function parse(fields, fkeys) { + var sql_fields = ""; + var checkPrimaryKey = false; + var names = []; + + fields.forEach(function (field, i) { + if (!field.name) { + throw new Error("Name of field most be provided"); + } + + if (field.name.trim().length == 0) { + throw new Error("Name most contains characters"); + } + + if (names.indexOf(field.name) >= 0) { + throw new Error("Two parameters with same name: " + field.name); + } + + var line = this.dialect._wrapIdentifier(field.name) + " " + getType(field); + + if (field.not_null) { + line += " NOT NULL"; + } + + if (field.default !== undefined) { + var _type = typeof field.default; + var _default = field.default; + if (_type === "string") { + _default = "'" + field.default + "'"; + } + line += " default " + _default; + } + + if (field.unique) { + line += " UNIQUE"; + } + + + if (field.primary_key) { + if (checkPrimaryKey) { + throw new Error("Too much primary key '" + field.name + "' in table"); + } else { + checkPrimaryKey = true; + } + + line += " PRIMARY KEY"; + } + + sql_fields += line; + + names.push(field.name); + + if (i != fields.length - 1) { + sql_fields += ","; + } + }.bind(this)); + + sql_fields += foreignkeys(fields, fkeys); + return sql_fields; +} + +module.exports = { + parse: parse, + foreignkeys: foreignkeys +} diff --git a/legacy/json-sql/lib/dialects/base/templates.js b/legacy/json-sql/lib/dialects/base/templates.js new file mode 100644 index 00000000..81c60cdb --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/templates.js @@ -0,0 +1,220 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + var availableSourceProps = ['table', 'query', 'select', 'expression']; + var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; + var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; + + // private templates + + dialect.templates.add('query', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + + dialect.templates.add('subQuery', { + pattern: '({queryBody})', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('create', { + pattern: 'create table if not exists {table}({tableFields})', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'tableFields'); + hasArrayProp(type, params, 'tableFields'); + } + }); + + dialect.templates.add('index', { + pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'name') + hasRequiredProp(type, params, 'indexOn'); + hasMinPropLength(type, params, 'name', 1); + hasMinPropLength(type, params, 'indexOn', 1); + } + }) + + + dialect.templates.add('queriesCombination', { + pattern: '{with} {queries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queries'); + hasArrayProp(type, params, 'queries'); + hasMinPropLength(type, params, 'queries', 2); + } + }); + + + dialect.templates.add('insertValues', { + pattern: '({fields}) values {fieldValues}', + validate: function(type, params) { + hasRequiredProp('values', params, 'fields'); + hasArrayProp('values', params, 'fields'); + hasMinPropLength('values', params, 'fields', 1); + + hasRequiredProp('values', params, 'fieldValues'); + hasArrayProp('values', params, 'fieldValues'); + hasMinPropLength('values', params, 'fieldValues', 1); + } + }); + + + dialect.templates.add('joinItem', { + pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', + validate: function(type, params) { + hasOneOfProps('join', params, availableSourceProps); + + if (params.type) { + hasStringProp('join', params, 'type'); + + var splitType = _(params.type.toLowerCase().split(' ')).compact(); + if (_.difference(splitType, availableJoinTypes).length) { + throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); + } + } + } + }); + + + dialect.templates.add('withItem', { + pattern: '{name} {fields} as {query} {select} {expression}', + validate: function(type, params) { + hasRequiredProp('with', params, 'name'); + hasOneOfProps('with', params, ['query', 'select', 'expression']); + } + }); + + + dialect.templates.add('fromItem', { + pattern: '{table} {query} {select} {expression}', + validate: function(type, params) { + hasOneOfProps('from', params, availableSourceProps); + } + }); + + + // public templates + + dialect.templates.add('select', { + pattern: '{with} select {distinct} {fields} ' + + 'from {from} {table} {query} {select} {expression} {alias} ' + + '{join} {condition} {group} {sort} {limit} {offset}', + defaults: { + fields: {} + }, + validate: function(type, params) { + hasOneOfProps(type, params, availableSourceProps); + } + }); + + + dialect.templates.add('insert', { + pattern: '{with} insert {or} into {table} {values} {returning} {condition}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('update', { + pattern: '{with} update {or} {table} {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'modifier'); + hasRequiredProp(type, params, 'table'); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('remove', { + pattern: '{with} delete from {table} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + } + }); + + + dialect.templates.add('union', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('except', dialect.templates.get('queriesCombination')); + + + // validation helpers + + function hasRequiredProp(type, params, propName) { + if (!params[propName]) { + throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); + } + } + + function hasObjectProp(type, params, propName) { + if (!_.isObject(params[propName])) { + throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); + } + } + + function hasArrayProp(type, params, propName) { + if (!_.isArray(params[propName])) { + throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); + } + } + + function hasStringProp(type, params, propName) { + if (!_.isString(params.type)) { + throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); + } + } + + function hasMinPropLength(type, params, propName, length) { + if (params[propName].length < length) { + throw new Error('`' + propName + '` property should not have length less than ' + length + + ' in `' + type + '` clause'); + } + } + + function hasOneOfProps(type, params, expectedPropNames) { + var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); + + if (!propNames.length) { + throw new Error('Neither `' + expectedPropNames.join('`, `') + + '` properties are not set in `' + type + '` clause'); + } + + if (propNames.length > 1) { + throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + + type + '` clause'); + } + } + + function matchesRegExpProp(type, params, propName, regExp) { + if (!params[propName].match(regExp)) { + throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + + type + '` clause'); + } + } +}; diff --git a/legacy/json-sql/lib/dialects/mssql/index.js b/legacy/json-sql/lib/dialects/mssql/index.js new file mode 100644 index 00000000..8374184f --- /dev/null +++ b/legacy/json-sql/lib/dialects/mssql/index.js @@ -0,0 +1,13 @@ +'use strict'; + +var BaseDialect = require('../base'); +var _ = require('underscore'); +var util = require('util'); + +var Dialect = module.exports = function(builder) { + BaseDialect.call(this, builder); +}; + +util.inherits(Dialect, BaseDialect); + +Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config, {}); diff --git a/legacy/json-sql/lib/dialects/postgresql/blocks.js b/legacy/json-sql/lib/dialects/postgresql/blocks.js new file mode 100644 index 00000000..e6f0066d --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/blocks.js @@ -0,0 +1,33 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + dialect.blocks.add('offset', function(params) { + var limit = ''; + + if (typeof params.limit === 'undefined') { + limit = this.buildBlock('limit', {limit: -1}) + ' '; + } + + return limit + 'offset ' + this._pushValue(params.offset); + }); + + dialect.blocks.add('unionqueries', function(params) { + var self = this; + + return _(params.unionqueries).map(function(query) { + return self.buildTemplate('subUnionQuery', {queryBody: query}); + }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); + }); + + dialect.blocks.add('conflictFields', function(params) { + var self = this; + + var fields = _(params.conflictFields).map(function(field) { + return self.dialect._wrapIdentifier(field); + }); + + return '(' + fields.join(',') + ')'; + }); +}; diff --git a/legacy/json-sql/lib/dialects/postgresql/conditions.js b/legacy/json-sql/lib/dialects/postgresql/conditions.js new file mode 100644 index 00000000..dddba22a --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/conditions.js @@ -0,0 +1,87 @@ +'use strict'; + +var _ = require('underscore'); + +var buildJsonInCondition = function(builder, field, operator, value) { + field = builder.dialect._wrapIdentifier(field); + + var placeholder; + try { + placeholder = builder.buildBlock('field', value); + } catch (e) { + placeholder = builder._pushValue(JSON.stringify(value)); + } + + return [field, operator, placeholder].join(' '); +}; + +var buildJsonHasCondition = function(builder, field, operator, value) { + field = builder.dialect._wrapIdentifier(field); + + var placeholder; + if (_(value).isArray()) { + placeholder = 'array[' + _(value).map(function(item) { + return builder._pushValue(item); + }).join(', ') + ']'; + } else { + placeholder = builder.buildBlock('field', value); + } + + return [field, operator, placeholder].join(' '); +}; + +module.exports = function(dialect) { + dialect.conditions.add('$jsonContains', function(field, operator, value) { + return buildJsonInCondition(this, field, '@>', value); + }); + + dialect.conditions.add('$jsonIn', function(field, operator, value) { + return buildJsonInCondition(this, field, '<@', value); + }); + + dialect.conditions.add('$jsonHas', function(field, operator, value) { + field = this.dialect._wrapIdentifier(field); + + var placeholder = value; + if (_(placeholder).isObject()) { + placeholder = this.buildBlock('field', placeholder); + } else { + placeholder = this._pushValue(value.toString()); + } + + return [field, '?', placeholder].join(' '); + }); + + dialect.conditions.add('$jsonHasAny', function(field, operator, value) { + return buildJsonHasCondition(this, field, '?|', value); + }); + + dialect.conditions.add('$jsonHasAll', function(field, operator, value) { + return buildJsonHasCondition(this, field, '?&', value); + }); + + dialect.conditions.add('$upper', function(field, operator, value) { + return [ + 'upper(' + this.dialect._wrapIdentifier(field) + ')', + '=', + 'upper(' + this._pushValue(value[1]) + ')' + ].join(' '); + }); + + dialect.conditions.add('$lower', function(field, operator, value) { + return [ + 'lower(' + this.dialect._wrapIdentifier(field) + ')', + '=', + 'lower(' + this._pushValue(value[1]) + ')' + ].join(' '); + }); + + dialect.conditions.add('$decode', function (field, operator, value) { + return [ + this.dialect._wrapIdentifier(field), + '=', + 'decode(' + this._pushValue(value[1]) + ',', + this._pushValue(value[2]) + ')' + ].join(' '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/postgresql/index.js b/legacy/json-sql/lib/dialects/postgresql/index.js new file mode 100644 index 00000000..755dc00d --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/index.js @@ -0,0 +1,39 @@ +'use strict'; + +var BaseDialect = require('../base'); +var _ = require('underscore'); +var util = require('util'); +var blocksInit = require('./blocks'); +var conditionsInit = require('./conditions'); +var templatesInit = require('./templates'); + +var Dialect = module.exports = function (builder) { + BaseDialect.call(this, builder); + blocksInit(this) + conditionsInit(this); + templatesInit(this); +}; + +Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); + +util.inherits(Dialect, BaseDialect); + +Dialect.prototype.config = _({ + jsonSeparatorRegexp: /->>?/g +}).extend(BaseDialect.prototype.config); + +Dialect.prototype._wrapIdentifier = function(name) { + // split by json separator + var nameParts = name.split(this.config.jsonSeparatorRegexp); + var separators = name.match(this.config.jsonSeparatorRegexp); + + // wrap base identifier + var identifier = BaseDialect.prototype._wrapIdentifier.call(this, nameParts[0]); + + // wrap all json identifier and join them with separators + identifier += _(separators).reduce(function(memo, separator, index) { + return memo + separator + '\'' + nameParts[index + 1] + '\''; + }, ''); + + return identifier; +}; diff --git a/legacy/json-sql/lib/dialects/postgresql/templates.js b/legacy/json-sql/lib/dialects/postgresql/templates.js new file mode 100644 index 00000000..297ed8b0 --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/templates.js @@ -0,0 +1,269 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + var availableSourceProps = ['table', 'query', 'select', 'expression']; + var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; + var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; + + // private templates + + dialect.templates.add('query', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subQuery', { + pattern: '({queryBody})', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subUnionQuery', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('create', { + pattern: 'create table if not exists {table}({tableFields})', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'tableFields'); + hasArrayProp(type, params, 'tableFields'); + } + }); + + dialect.templates.add('index', { + pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'name') + hasRequiredProp(type, params, 'indexOn'); + hasMinPropLength(type, params, 'name', 1); + hasMinPropLength(type, params, 'indexOn', 1); + } + }) + + + dialect.templates.add('queriesCombination', { + pattern: '{with} {queries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queries'); + hasArrayProp(type, params, 'queries'); + hasMinPropLength(type, params, 'queries', 2); + } + }); + + dialect.templates.add('queriesUnionCombination', { + pattern: '{with} {unionqueries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'unionqueries'); + hasArrayProp(type, params, 'unionqueries'); + hasMinPropLength(type, params, 'unionqueries', 2); + } + }); + + + dialect.templates.add('insertValues', { + pattern: '({fields}) values {fieldValues}', + validate: function(type, params) { + hasRequiredProp('values', params, 'fields'); + hasArrayProp('values', params, 'fields'); + hasMinPropLength('values', params, 'fields', 1); + + hasRequiredProp('values', params, 'fieldValues'); + hasArrayProp('values', params, 'fieldValues'); + hasMinPropLength('values', params, 'fieldValues', 1); + } + }); + + + dialect.templates.add('joinItem', { + pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', + validate: function(type, params) { + hasOneOfProps('join', params, availableSourceProps); + + if (params.type) { + hasStringProp('join', params, 'type'); + + var splitType = _(params.type.toLowerCase().split(' ')).compact(); + if (_.difference(splitType, availableJoinTypes).length) { + throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); + } + } + } + }); + + + dialect.templates.add('withItem', { + pattern: '{name} {fields} as {query} {select} {expression}', + validate: function(type, params) { + hasRequiredProp('with', params, 'name'); + hasOneOfProps('with', params, ['query', 'select', 'expression']); + } + }); + + + dialect.templates.add('fromItem', { + pattern: '{table} {query} {select} {expression}', + validate: function(type, params) { + hasOneOfProps('from', params, availableSourceProps); + } + }); + + + // public templates + + dialect.templates.add('select', { + pattern: '{with} select {distinct} {fields} ' + + 'from {from} {table} {query} {select} {expression} {alias} ' + + '{join} {condition} {group} {sort} {limit} {offset}', + defaults: { + fields: {} + }, + validate: function(type, params) { + hasOneOfProps(type, params, availableSourceProps); + } + }); + + + dialect.templates.add('insert', { + pattern: '{with} insert {or} into {table} {values} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('insertornothing', { + pattern: '{with} insert {or} into {table} {values} on conflict do nothing {returning} {condition}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('insertorupdate', { + pattern: '{with} insert {or} into {table} {values} on conflict {conflictFields} do update {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasRequiredProp('conflictFields', params, 'conflictFields'); + hasArrayProp('conflictFields', params, 'conflictFields'); + hasMinPropLength('conflictFields', params, 'conflictFields', 1); + hasRequiredProp(type, params, 'modifier'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('update', { + pattern: '{with} update {or} {table} {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'modifier'); + hasRequiredProp(type, params, 'table'); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('remove', { + pattern: '{with} delete from {table} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + } + }); + + + dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); + + + dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('except', dialect.templates.get('queriesCombination')); + + + // validation helpers + + function hasRequiredProp(type, params, propName) { + if (!params[propName]) { + throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); + } + } + + function hasObjectProp(type, params, propName) { + if (!_.isObject(params[propName])) { + throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); + } + } + + function hasArrayProp(type, params, propName) { + if (!_.isArray(params[propName])) { + throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); + } + } + + function hasStringProp(type, params, propName) { + if (!_.isString(params.type)) { + throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); + } + } + + function hasMinPropLength(type, params, propName, length) { + if (params[propName].length < length) { + throw new Error('`' + propName + '` property should not have length less than ' + length + + ' in `' + type + '` clause'); + } + } + + function hasOneOfProps(type, params, expectedPropNames) { + var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); + + if (!propNames.length) { + throw new Error('Neither `' + expectedPropNames.join('`, `') + + '` properties are not set in `' + type + '` clause'); + } + + if (propNames.length > 1) { + throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + + type + '` clause'); + } + } + + function matchesRegExpProp(type, params, propName, regExp) { + if (!params[propName].match(regExp)) { + throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + + type + '` clause'); + } + } +}; diff --git a/legacy/json-sql/lib/dialects/sqlite/blocks.js b/legacy/json-sql/lib/dialects/sqlite/blocks.js new file mode 100644 index 00000000..c3531c2a --- /dev/null +++ b/legacy/json-sql/lib/dialects/sqlite/blocks.js @@ -0,0 +1,23 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + dialect.blocks.add('offset', function(params) { + var limit = ''; + + if (typeof params.limit === 'undefined') { + limit = this.buildBlock('limit', {limit: -1}) + ' '; + } + + return limit + 'offset ' + this._pushValue(params.offset); + }); + + dialect.blocks.add('unionqueries', function(params) { + var self = this; + + return _(params.unionqueries).map(function(query) { + return self.buildTemplate('subUnionQuery', {queryBody: query}); + }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/sqlite/index.js b/legacy/json-sql/lib/dialects/sqlite/index.js new file mode 100644 index 00000000..72549400 --- /dev/null +++ b/legacy/json-sql/lib/dialects/sqlite/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var BaseDialect = require('../base'); +var _ = require('underscore'); +var util = require('util'); +var blocksInit = require('./blocks'); +var templatesInit = require('./templates'); + +var Dialect = module.exports = function (builder) { + BaseDialect.call(this, builder); + blocksInit(this); + templatesInit(this); +}; + +util.inherits(Dialect, BaseDialect); + +Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); diff --git a/legacy/json-sql/lib/dialects/sqlite/templates.js b/legacy/json-sql/lib/dialects/sqlite/templates.js new file mode 100644 index 00000000..ac6e382f --- /dev/null +++ b/legacy/json-sql/lib/dialects/sqlite/templates.js @@ -0,0 +1,236 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + var availableSourceProps = ['table', 'query', 'select', 'expression']; + var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; + var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; + + // private templates + + dialect.templates.add('query', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subQuery', { + pattern: '({queryBody})', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subUnionQuery', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('create', { + pattern: 'create table if not exists {table}({tableFields})', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'tableFields'); + hasArrayProp(type, params, 'tableFields'); + } + }); + + dialect.templates.add('index', { + pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'name') + hasRequiredProp(type, params, 'indexOn'); + hasMinPropLength(type, params, 'name', 1); + hasMinPropLength(type, params, 'indexOn', 1); + } + }) + + + dialect.templates.add('queriesCombination', { + pattern: '{with} {queries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queries'); + hasArrayProp(type, params, 'queries'); + hasMinPropLength(type, params, 'queries', 2); + } + }); + + dialect.templates.add('queriesUnionCombination', { + pattern: '{with} {unionqueries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'unionqueries'); + hasArrayProp(type, params, 'unionqueries'); + hasMinPropLength(type, params, 'unionqueries', 2); + } + }); + + + dialect.templates.add('insertValues', { + pattern: '({fields}) values {fieldValues}', + validate: function(type, params) { + hasRequiredProp('values', params, 'fields'); + hasArrayProp('values', params, 'fields'); + hasMinPropLength('values', params, 'fields', 1); + + hasRequiredProp('values', params, 'fieldValues'); + hasArrayProp('values', params, 'fieldValues'); + hasMinPropLength('values', params, 'fieldValues', 1); + } + }); + + + dialect.templates.add('joinItem', { + pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', + validate: function(type, params) { + hasOneOfProps('join', params, availableSourceProps); + + if (params.type) { + hasStringProp('join', params, 'type'); + + var splitType = _(params.type.toLowerCase().split(' ')).compact(); + if (_.difference(splitType, availableJoinTypes).length) { + throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); + } + } + } + }); + + + dialect.templates.add('withItem', { + pattern: '{name} {fields} as {query} {select} {expression}', + validate: function(type, params) { + hasRequiredProp('with', params, 'name'); + hasOneOfProps('with', params, ['query', 'select', 'expression']); + } + }); + + + dialect.templates.add('fromItem', { + pattern: '{table} {query} {select} {expression}', + validate: function(type, params) { + hasOneOfProps('from', params, availableSourceProps); + } + }); + + + // public templates + + dialect.templates.add('select', { + pattern: '{with} select {distinct} {fields} ' + + 'from {from} {table} {query} {select} {expression} {alias} ' + + '{join} {condition} {group} {sort} {limit} {offset}', + defaults: { + fields: {} + }, + validate: function(type, params) { + hasOneOfProps(type, params, availableSourceProps); + } + }); + + + dialect.templates.add('insert', { + pattern: '{with} insert {or} into {table} {values} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('update', { + pattern: '{with} update {or} {table} {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'modifier'); + hasRequiredProp(type, params, 'table'); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('remove', { + pattern: '{with} delete from {table} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + } + }); + + + dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); + + + dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('except', dialect.templates.get('queriesCombination')); + + + // validation helpers + + function hasRequiredProp(type, params, propName) { + if (!params[propName]) { + throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); + } + } + + function hasObjectProp(type, params, propName) { + if (!_.isObject(params[propName])) { + throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); + } + } + + function hasArrayProp(type, params, propName) { + if (!_.isArray(params[propName])) { + throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); + } + } + + function hasStringProp(type, params, propName) { + if (!_.isString(params.type)) { + throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); + } + } + + function hasMinPropLength(type, params, propName, length) { + if (params[propName].length < length) { + throw new Error('`' + propName + '` property should not have length less than ' + length + + ' in `' + type + '` clause'); + } + } + + function hasOneOfProps(type, params, expectedPropNames) { + var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); + + if (!propNames.length) { + throw new Error('Neither `' + expectedPropNames.join('`, `') + + '` properties are not set in `' + type + '` clause'); + } + + if (propNames.length > 1) { + throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + + type + '` clause'); + } + } + + function matchesRegExpProp(type, params, propName, regExp) { + if (!params[propName].match(regExp)) { + throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + + type + '` clause'); + } + } +}; diff --git a/legacy/json-sql/lib/index.js b/legacy/json-sql/lib/index.js new file mode 100644 index 00000000..55bbe02a --- /dev/null +++ b/legacy/json-sql/lib/index.js @@ -0,0 +1,8 @@ +'use strict'; + +var Builder = require('./builder'); + +module.exports = function(params) { + return new Builder(params); +}; +module.exports.Builder = Builder; diff --git a/legacy/json-sql/lib/valuesStore.js b/legacy/json-sql/lib/valuesStore.js new file mode 100644 index 00000000..29b7d395 --- /dev/null +++ b/legacy/json-sql/lib/valuesStore.js @@ -0,0 +1,31 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = ValuesStore; + +function ValuesStore(options) { + options = options || {}; + this.context = options.context || null; + this._values = options.values || {}; +} + +ValuesStore.prototype.add = ValuesStore.prototype.set = function(name, value) { + if (_.isFunction(value) && this.context) { + value = _(value).bind(this.context); + } + + this._values[name] = value; +}; + +ValuesStore.prototype.get = function(name) { + return this._values[name] || null; +}; + +ValuesStore.prototype.remove = function(name) { + delete this._values[name]; +}; + +ValuesStore.prototype.has = function(name) { + return this._values.hasOwnProperty(name); +}; diff --git a/legacy/json-sql/package.json b/legacy/json-sql/package.json new file mode 100644 index 00000000..73c407e3 --- /dev/null +++ b/legacy/json-sql/package.json @@ -0,0 +1,44 @@ +{ + "name": "json-sql", + "description": "Node.js json to sql query mapper", + "version": "0.2.6", + "author": { + "name": "Artem Zhukov", + "email": "artzhuchka@gmail.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/2do2go/json-sql.git" + }, + "keywords": [ + "json-sql", + "json", + "sql", + "odm", + "mapper", + "db", + "database" + ], + "dependencies": { + "underscore": "=1.8.3" + }, + "devDependencies": { + "jshint": "=2.9.2", + "chai": "=3.5.0", + "gulp": "=3.9.1", + "gulp-jshint": "=2.0.0", + "gulp-mocha": "=2.2.0" + }, + "main": "./lib/index", + "readme": "# JSON-SQL\n\nLibrary for mapping mongo-style query objects to SQL queries.\n\n## Quick Start\n\nInstall it with NPM or add it to your package.json:\n\n``` bash\n$ npm install json-sql\n```\n\nThen:\n\n``` js\nvar jsonSql = require('json-sql')();\n\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tfields: ['name', 'age'],\n\tcondition: {name: 'Max', id: 6}\n});\n\nsql.query\n// sql string:\n// select name, age from users where name = $p1 && id = 6;\n\nsql.values\n// hash of values:\n// { p1: 'Max' }\n```\n\n## Documentation\n\nDocumentation is available at the [./docs directory](./docs).\n\n## Examples\n\n__Select with join:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tjoin: {\n\t\tdocuments: {\n\t\t\ton: {'user.id': 'documents.userId'}\n\t\t}\n\t}\n});\n\nsql.query\n// select * from users join documents on user.id = documents.userId;\n\nsql.values\n// {}\n```\n\n__Insert:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'insert',\n\ttable: 'users',\n\tvalues: {\n\t\tname: 'John',\n\t\tlastname: 'Snow',\n\t\tage: 24,\n\t\tgender: 'male'\n\t}\n});\n\nsql.query\n// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3);\n\nsql.values\n// { p1: 'John', p2: 'Snow', p3: 'male' }\n```\n\n__Update:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'update',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t},\n\tmodifier: {\n\t\trole: 'admin'\n\t\tage: 33\n\t}\n});\n\nsql.query\n// update users set role = $p1, age = 33 where id = 5;\n\nsql.values\n// { p1: 'admin' }\n```\n\n__Remove:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'remove',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t}\n});\n\nsql.query\n// delete from users where id = 5;\n\nsql.values\n// {}\n```\n\nFor more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests).\n\n## Tests\n\nClone repository from github, `cd` into cloned dir and install dev dependencies:\n\n``` bash\n$ npm install\n```\n\nThen run tests with command:\n\n``` bash\n$ gulp test\n```\n\nOr run tests coverage with command:\n\n``` bash\n$ gulp coverage\n```\n\n## License\n\n[MIT](./LICENSE)\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/2do2go/json-sql/issues" + }, + "homepage": "https://github.com/2do2go/json-sql#readme", + "_id": "json-sql@0.2.4", + "_shasum": "df8c5f345b72f421c6fc7da57116c47cae5b5216", + "_resolved": "https://github.com/LiskHQ/json-sql/tarball/master", + "_from": "https://github.com/LiskHQ/json-sql/tarball/master" +} diff --git a/legacy/json-sql/tests/0_base.js b/legacy/json-sql/tests/0_base.js new file mode 100644 index 00000000..b46774be --- /dev/null +++ b/legacy/json-sql/tests/0_base.js @@ -0,0 +1,304 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var Builder = require('../lib').Builder; +var expect = require('chai').expect; + +describe('Builder', function() { + it('should have fields', function() { + expect(jsonSql).to.be.ok; + expect(jsonSql).to.be.an.instanceof(Builder); + + expect(jsonSql.dialect).to.be.ok; + + expect(jsonSql._query).to.be.equal(''); + expect(jsonSql._values).to.be.eql({}); + + expect(jsonSql.dialect.blocks).to.be.ok; + expect(jsonSql.dialect.templates).to.be.ok; + expect(jsonSql.dialect.conditions).to.be.ok; + expect(jsonSql.dialect.modifiers).to.be.ok; + expect(jsonSql.dialect.logicalOperators).to.be.ok; + }); + + it('should throw error with wrong `type` property', function() { + expect(function() { + jsonSql.build({ + type: 'wrong' + }); + }).to.throw('Unknown template type "wrong"'); + }); + + it('should throw error without `table`, `query` and `select` properties', function() { + expect(function() { + jsonSql.build({}); + }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + + 'are not set in `select` clause'); + }); + + it('should throw error with both `table` and `select` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + select: {table: 'payments'} + }); + }).to.throw('Wrong using `table`, `select` properties together in `select` clause'); + }); + + it('should throw error with both `table` and `query` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + query: {table: 'payments'} + }); + }).to.throw('Wrong using `table`, `query` properties together in `select` clause'); + }); + + it('should throw error with both `query` and `select` properties', function() { + expect(function() { + jsonSql.build({ + query: {table: 'payments'}, + select: {table: 'payments'} + }); + }).to.throw('Wrong using `query`, `select` properties together in `select` clause'); + }); + + it('should throw error without `name` property in `with` clause', function() { + expect(function() { + jsonSql.build({ + 'with': [{ + select: { + table: 'payments' + } + }], + table: 'users' + }); + }).to.throw('`name` property is not set in `with` clause'); + }); + + it('should throw error without `query` and `select` properties in `with` clause', function() { + expect(function() { + jsonSql.build({ + 'with': [{ + name: 'payments' + }], + table: 'users' + }); + }).to.throw('Neither `query`, `select`, `expression` properties are not set in `with` clause'); + }); + + it('should throw error with both `query` and `select` properties in `with` clause', function() { + expect(function() { + jsonSql.build({ + 'with': [{ + name: 'payments', + query: {table: 'table1'}, + select: {table: 'table2'} + }], + table: 'users' + }); + }).to.throw('Wrong using `query`, `select` properties together in `with` clause'); + }); + + it('should be ok with array in `with` clause', function() { + var result = jsonSql.build({ + 'with': [{ + name: 'payments', + select: { + table: 'payments' + } + }], + table: 'users' + }); + + expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + + '"users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object in `with` clause', function() { + var result = jsonSql.build({ + 'with': { + payments: { + select: { + table: 'payments' + } + } + }, + table: 'users' + }); + + expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + + '"users";'); + expect(result.values).to.be.eql({}); + }); + + it('should create array values with option `namedValues` = false', function() { + jsonSql.configure({ + namedValues: false + }); + + expect(jsonSql._values).to.be.eql([]); + + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); + expect(result.values).to.be.eql(['John']); + }); + + it('should use prefix `@` for values with option `valuesPrefix` = @', function() { + jsonSql.configure({ + valuesPrefix: '@' + }); + + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); + expect(result.values).to.be.eql({p1: 'John'}); + }); + + it('should return prefixed values with method `prefixValues`', function() { + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); + expect(result.values).to.be.eql({p1: 'John'}); + expect(result.prefixValues()).to.be.eql({'@{p1}': 'John'}); + }); + + it('should return array values with method `getValuesArray`', function() { + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); + expect(result.values).to.be.eql({p1: 'John'}); + expect(result.getValuesArray()).to.be.eql(['John']); + }); + + it('should return object values with method `getValuesObject`', function() { + jsonSql.configure({ + valuesPrefix: '$', + namedValues: false + }); + + expect(jsonSql._values).to.be.eql([]); + + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); + expect(result.values).to.be.eql(['John']); + expect(result.prefixValues()).to.be.eql({'${1}': 'John'}); + expect(result.getValuesObject()).to.be.eql({1: 'John'}); + }); + + it('should create query without values with option `separatedValues` = false', function() { + jsonSql.configure({ + separatedValues: false + }); + + expect(jsonSql._values).to.not.be.ok; + expect(jsonSql._placeholderId).to.not.be.ok; + + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: {name: 'John', surname: 'Doe'} + }); + + expect(result.query).to.be.equal('insert into "users" ("name", "surname") values ' + + '(\'John\', \'Doe\');'); + expect(result.values).to.not.be.ok; + }); + + it('should create query without wrapping identifiers with option `wrappedIdentifiers` = false', + function() { + jsonSql.configure({ + wrappedIdentifiers: false + }); + + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: {name: 'John'} + }); + + expect(result.query).to.be.equal('insert into users (name) values (${p1});'); + } + ); + + it('shouldn\'t wrap identifiers that already wrapped', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + type: 'insert', + table: '"users"', + values: { + '"name"': 'John', + '"users"."age"': 22 + } + }); + + expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); + }); + + it('shouldn\'t split identifiers by dots inside quotes', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + type: 'insert', + table: '"users"', + values: { + '"users.age"': 22 + } + }); + + expect(result.query).to.be.equal('insert into "users" ("users.age") values (22);'); + }); + + it('shouldn\'t wrap asterisk identifier parts', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + fields: ['users.*'], + table: '"users"' + }); + + expect(result.query).to.be.equal('select "users".* from "users";'); + }); + + it('should split identifiers by dots and wrap each part', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + type: 'insert', + table: '"users"', + values: { + 'name': 'John', + 'users.age': 22 + } + }); + + expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); + }); +}); diff --git a/legacy/json-sql/tests/1_select.js b/legacy/json-sql/tests/1_select.js new file mode 100644 index 00000000..087a1afb --- /dev/null +++ b/legacy/json-sql/tests/1_select.js @@ -0,0 +1,1222 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Select', function() { + describe('type', function() { + it('should be ok without `type` property', function() { + var result = jsonSql.build({ + table: 'users' + }); + + expect(result.query).to.be.equal('select * from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with "select" value', function() { + var result = jsonSql.build({ + type: 'select', + table: 'users' + }); + + expect(result.query).to.be.equal('select * from "users";'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('distinct', function() { + it('should be ok with true value', function() { + var result = jsonSql.build({ + table: 'users', + distinct: true + }); + + expect(result.query).to.be.equal('select distinct * from "users";'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('fields', function() { + it('should be ok with string array', function() { + var result = jsonSql.build({ + table: 'users', + fields: ['name', 'type'] + }); + + expect(result.query).to.be.equal('select "name", "type" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`name`: `alias`, ...)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {userAge: 'age', userScore: 'score'} + }); + + expect(result.query).to.be.equal('select "userAge" as "age", "userScore" as "score" from ' + + '"users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array of objects(`name`: `alias`, ...)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{userAge: 'age'}] + }); + + expect(result.query).to.be.equal('select "userAge" as "age" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'address'}] + }); + + expect(result.query).to.be.equal('select "address" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `table`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'score', table: 'users'}] + }); + + expect(result.query).to.be.equal('select "users"."score" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `alias`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'zoneName', alias: 'zone'}] + }); + + expect(result.query).to.be.equal('select "zoneName" as "zone" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `table`, `alias`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'zoneName', table: 'users', alias: 'zone'}] + }); + + expect(result.query).to.be.equal('select "users"."zoneName" as "zone" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`table`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {score: {table: 'users'}} + }); + + expect(result.query).to.be.equal('select "users"."score" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {zone: {field: 'zone_1', alias: 'zone'}} + }); + + expect(result.query).to.be.equal('select "zone_1" as "zone" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`table`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {score: {table: 'users', alias: 's'}} + }); + + expect(result.query).to.be.equal('select "users"."score" as "s" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `table`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {name: {field: 'name_1', table: 'users', alias: 'name_2'}} + }); + + expect(result.query).to.be.equal('select "users"."name_1" as "name_2" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: 'count(*)' + }] + }); + + expect(result.query).to.be.equal('select count(*) from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: 'count(*)', + alias: 'count' + }] + }); + + expect(result.query).to.be.equal('select count(*) as "count" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`, `field`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: 'sum', + field: 'income', + alias: 'sum' + }] + }); + + expect(result.query).to.be.equal('select sum("income") as "sum" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`[], `field`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: ['abs', 'sum'], + field: 'income', + alias: 'sum' + }] + }); + + expect(result.query).to.be.equal('select abs(sum("income")) as "sum" from "users";'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('alias', function() { + it('should be ok with string `alias` property', function() { + var result = jsonSql.build({ + table: 'users', + alias: 'u' + }); + + expect(result.query).to.be.equal('select * from "users" as "u";'); + expect(result.values).to.be.eql({}); + }); + + it('should throw error if object `alias` does not have `name` property', function() { + expect(function() { + jsonSql.build({ + table: 'users', + alias: {} + }); + }).to.throw('Alias `name` property is required'); + }); + + it('should be ok with object `alias`(`name`) property', function() { + var result = jsonSql.build({ + table: 'users', + alias: { + name: 'u' + } + }); + + expect(result.query).to.be.equal('select * from "users" as "u";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object `alias`(`name`, `columns`) property', function() { + var result = jsonSql.build({ + table: 'users', + alias: { + name: 'u', + columns: ['a', 'b'] + } + }); + + expect(result.query).to.be.equal('select * from "users" as "u"("a", "b");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('query', function() { + it('should be ok with `query` property', function() { + var result = jsonSql.build({ + query: { + type: 'select', + table: 't' + } + }); + + expect(result.query).to.be.equal('select * from (select * from "t");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('select', function() { + it('should be ok with `select` property', function() { + var result = jsonSql.build({ + select: { + table: 't' + } + }); + + expect(result.query).to.be.equal('select * from (select * from "t");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('expression', function() { + it('should be ok with `expression` property', function() { + var result = jsonSql.build({ + expression: 'function()' + }); + + expect(result.query).to.be.equal('select * from function();'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('join', function() { + it('should throw error without `table`, `query` and `select` properties', + function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{}] + }); + }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + + 'are not set in `join` clause'); + } + ); + + it('should throw error with both `table` and `select` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + table: 'a', + select: {table: 'b'} + }] + }); + }).to.throw('Wrong using `table`, `select` properties together in `join` clause'); + }); + + it('should throw error with both `table` and `query` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + table: 'a', + query: {table: 'b'} + }] + }); + }).to.throw('Wrong using `table`, `query` properties together in `join` clause'); + }); + + it('should throw error with both `query` and `select` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + query: 'a', + select: {table: 'b'} + }] + }); + }).to.throw('Wrong using `query`, `select` properties together in `join` clause'); + }); + + it('should throw error with wrong `type` property', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + type: 'wrong', + table: 'payments' + }] + }); + }).to.throw('Invalid `type` property value "wrong" in `join` clause'); + }); + + it('should be ok with correct `type` property', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + type: 'left outer', + table: 'payments' + }] + }); + + expect(result.query).to.be.equal('select * from "users" left outer join "payments";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array `join`', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + table: 'payments' + }] + }); + + expect(result.query).to.be.equal('select * from "users" join "payments";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object `join`', function() { + var result = jsonSql.build({ + table: 'users', + join: { + payments: {} + } + }); + + expect(result.query).to.be.equal('select * from "users" join "payments";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `on` property', function() { + var result = jsonSql.build({ + table: 'users', + join: { + payments: { + on: {'users.name': 'payments.name'} + } + } + }); + + expect(result.query).to.be.equal('select * from "users" join "payments" on "users"."name" = ' + + '"payments"."name";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `query` property', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + query: { + table: 'payments' + }, + on: {'users.name': 'payments.name'} + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'join (select * from "payments") ' + + 'on "users"."name" = "payments"."name";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `select` property', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + select: { + table: 'payments' + }, + on: {'users.name': 'payments.name'} + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'join (select * from "payments") ' + + 'on "users"."name" = "payments"."name";' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('condition', function() { + describe('compare operators', function() { + it('should throw error with wrong operator', function() { + expect(function() { + jsonSql.build({ + table: 'users', + condition: { + name: {$wrong: 'John'} + } + }); + }).to.throw('Unknown operator "$wrong"'); + }); + + it('should be ok with default operator(=)', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: 'John' + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$eq` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$eq: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$ne` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$ne: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" != ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$gt` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$gt: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" > ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$lt` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$lt: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" < ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$gte` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$gte: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" >= ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$lte` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$lte: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" <= ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$is` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$is: null} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$isnot` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$isnot: null} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$like` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$like: 'John%'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" like ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John%' + }); + }); + + it('should be ok with `$null`:true operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$null: true} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$null`:false operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$null: false} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$field` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$field: 'name_2'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object `$field` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$field: {field: 'name_2'}} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: [12, 13, 14]} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (12, 13, 14);'); + expect(result.values).to.be.eql({}); + }); + + it('should add `null` value with empty array in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: []} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (null);'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$nin` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$nin: [12, 13, 14]} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" not in (12, 13, 14);'); + expect(result.values).to.be.eql({}); + }); + + it('should add `null` value with empty array in `$nin` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$nin: []} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" not in (null);'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object subquery in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: { + table: 'test' + }} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + + '"test");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `query` subquery in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: { + query: { + table: 'test' + } + }} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + + '(select * from "test"));'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `select` subquery in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: { + select: { + table: 'test' + } + }} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + + '(select * from "test"));'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$between` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$between: [12, 14]} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" between 12 and 14;'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('logical operators', function() { + it('should throw error with wrong logical operator', function() { + expect(function() { + jsonSql.build({ + table: 'users', + condition: { + $wrong: [ + {name: 'John'}, + {age: 12} + ] + } + }); + }).to.throw('Unknown logical operator "$wrong"'); + }); + + it('should be ok with default logical operator(`$and`)', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: 'John', + age: 12 + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with default logical operator(`$and`) for one field', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: { + $gt: 5, + $lt: 15, + $ne: 10 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" > 5 and "age" < 15 and ' + + '"age" != 10;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array `$and`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [ + {name: 'John'}, + {age: 12} + ] + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object `$and`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: { + name: 'John', + age: 12 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with array `$or`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [ + {name: 'John'}, + {age: 12} + ] + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object `$or`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: { + name: 'John', + age: 12 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with array `$not`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $not: [ + {name: 'John'}, + {age: 12} + ] + } + }); + + expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + + '"age" = 12);'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object `$not`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $not: { + name: 'John', + age: 12 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + + '"age" = 12);'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object [`$or`, `$or`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: [{ + $or: { + name: 'John', + age: 12 + } + }, { + $or: { + name: 'Mark', + age: 14 + } + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} or "age" = 12) and ' + + '("name" = ${p2} or "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with object `$and`:[`$or`, `$or`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [{ + $or: { + name: 'John', + age: 12 + } + }, { + $or: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} or "age" = 12) and ' + + '("name" = ${p2} or "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with object `$or`:[{},{}]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [{ + name: 'John', + age: 12 + }, { + name: 'Mark', + age: 14 + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) or ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with object `$or`:[`$and`, `$and`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [{ + $and: { + name: 'John', + age: 12 + } + }, { + $and: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) or ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with [{}, {}]', function() { + var result = jsonSql.build({ + table: 'users', + condition: [{ + name: 'John', + age: 12 + }, { + name: 'Mark', + age: 14 + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) and ' + + '("name" = ${p2} and "age" = 14);'); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with `$and`:[{}, {}]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [{ + name: 'John', + age: 12 + }, { + name: 'Mark', + age: 14 + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) and ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with `$and`:[`$and`, `$and`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [{ + $and: { + name: 'John', + age: 12 + } + }, { + $and: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) and ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with `$or`:[`$or`, `$or`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [{ + $or: { + name: 'John', + age: 12 + } + }, { + $or: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} or "age" = 12) or ' + + '("name" = ${p2} or "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + }); + }); + + describe('group', function() { + it('should be ok with string value', function() { + var result = jsonSql.build({ + table: 'users', + group: 'age' + }); + + expect(result.query).to.be.equal( + 'select * from "users" group by "age";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array value', function() { + var result = jsonSql.build({ + table: 'users', + group: ['age', 'gender'] + }); + + expect(result.query).to.be.equal( + 'select * from "users" group by "age", "gender";' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('sort', function() { + it('should be ok with string value', function() { + var result = jsonSql.build({ + table: 'users', + sort: 'age' + }); + + expect(result.query).to.be.equal( + 'select * from "users" order by "age";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array value', function() { + var result = jsonSql.build({ + table: 'users', + sort: ['age', 'gender'] + }); + + expect(result.query).to.be.equal( + 'select * from "users" order by "age", "gender";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object value', function() { + var result = jsonSql.build({ + table: 'users', + sort: { + age: 1, + gender: -1 + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" order by "age" asc, "gender" desc;' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('limit, offset', function() { + it('should be ok with `limit` property', function() { + var result = jsonSql.build({ + table: 'users', + limit: 5 + }); + + expect(result.query).to.be.equal( + 'select * from "users" limit 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `offset` property', function() { + var result = jsonSql.build({ + table: 'users', + offset: 5 + }); + + expect(result.query).to.be.equal( + 'select * from "users" offset 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `limit` and `offset` properties', function() { + var result = jsonSql.build({ + table: 'users', + limit: 10, + offset: 20 + }); + + expect(result.query).to.be.equal( + 'select * from "users" limit 10 offset 20;' + ); + expect(result.values).to.be.eql({}); + }); + }); +}); diff --git a/legacy/json-sql/tests/2_insert.js b/legacy/json-sql/tests/2_insert.js new file mode 100644 index 00000000..fe3f1b9d --- /dev/null +++ b/legacy/json-sql/tests/2_insert.js @@ -0,0 +1,73 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Insert', function() { + it('should throw error without `values` property', function() { + expect(function() { + jsonSql.build({ + type: 'insert', + table: 'users' + }); + }).to.throw('`values` property is not set in `insert` clause'); + }); + + it('should be ok with `values` property', function() { + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: { + name: 'Max' + } + }); + + expect(result.query).to.be.equal('insert into "users" ("name") values (${p1});'); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `with` property', function() { + var result = jsonSql.build({ + 'with': [{ + name: 't_1', + select: { + table: 't_1' + } + }], + type: 'insert', + table: 'users', + values: { + name: 'Max', + age: 17, + lastVisit: null, + active: true + } + }); + + expect(result.query).to.be.equal( + 'with "t_1" as (select * from "t_1") insert into "users" ' + + '("name", "age", "lastVisit", "active") values (${p1}, 17, null, true);' + ); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `returning` property', function() { + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: { + name: 'Max', + age: 17, + lastVisit: null, + active: true + }, + returning: ['users.*'] + }); + + expect(result.query).to.be.equal( + 'insert into "users" ("name", "age", "lastVisit", "active") ' + + 'values (${p1}, 17, null, true) returning "users".*;' + ); + expect(result.values).to.be.eql({p1: 'Max'}); + }); +}); diff --git a/legacy/json-sql/tests/3_update.js b/legacy/json-sql/tests/3_update.js new file mode 100644 index 00000000..688b4c1d --- /dev/null +++ b/legacy/json-sql/tests/3_update.js @@ -0,0 +1,123 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Update', function() { + describe('modifier', function() { + it('should throw error without `modifier` property', function() { + expect(function() { + jsonSql.build({ + type: 'update', + table: 'users' + }); + }).to.throw('`modifier` property is not set in `update` clause'); + }); + + it('should be ok with default(`$set`)', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + name: 'Max', + age: 16, + lastVisit: null, + active: false + } + }); + + expect(result.query).to.be.equal('update "users" set "name" = ${p1}, "age" = 16, ' + + '"lastVisit" = null, "active" = false;'); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `$set`', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $set: { + name: 'Max' + } + } + }); + + expect(result.query).to.be.equal('update "users" set "name" = ${p1};'); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `$inc`', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $inc: { + age: 4 + } + } + }); + + expect(result.query).to.be.equal('update "users" set "age" = "age" + 4;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$dec`', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $dec: { + age: 2 + } + } + }); + + expect(result.query).to.be.equal('update "users" set "age" = "age" - 2;'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('with', function() { + it('should be ok', function() { + var result = jsonSql.build({ + 'with': [{ + name: 't_1', + select: { + table: 't_1' + } + }], + type: 'update', + table: 'users', + modifier: { + $dec: { + age: 3 + } + } + }); + + expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") update "users" ' + + 'set "age" = "age" - 3;'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('returning', function() { + it('should be ok', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $dec: { + age: 3 + } + }, + returning: ['users.*'] + }); + + expect(result.query).to.be.equal( + 'update "users" set "age" = "age" - 3 returning "users".*;' + ); + expect(result.values).to.be.eql({}); + }); + }); +}); diff --git a/legacy/json-sql/tests/4_delete.js b/legacy/json-sql/tests/4_delete.js new file mode 100644 index 00000000..c90f19f2 --- /dev/null +++ b/legacy/json-sql/tests/4_delete.js @@ -0,0 +1,58 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Delete', function() { + it('should be ok without `condition` property', function() { + var result = jsonSql.build({ + type: 'remove', + table: 'users' + }); + + expect(result.query).to.be.equal('delete from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `condition` property', function() { + var result = jsonSql.build({ + type: 'remove', + table: 'users', + condition: { + a: 5 + } + }); + + expect(result.query).to.be.equal('delete from "users" where "a" = 5;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `with` property', function() { + var result = jsonSql.build({ + 'with': [{ + name: 't_1', + select: { + table: 't_1' + } + }], + type: 'remove', + table: 'users' + }); + + expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") delete from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `returning` property', function() { + var result = jsonSql.build({ + type: 'remove', + table: 'users', + returning: ['users.*'] + }); + + expect(result.query).to.be.equal( + 'delete from "users" returning "users".*;' + ); + expect(result.values).to.be.eql({}); + }); +}); diff --git a/legacy/json-sql/tests/5_union.js b/legacy/json-sql/tests/5_union.js new file mode 100644 index 00000000..a1e79fcf --- /dev/null +++ b/legacy/json-sql/tests/5_union.js @@ -0,0 +1,256 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Union, except, intersect', function() { + describe('queries', function() { + it('should throw error without `queries` property', function() { + expect(function() { + jsonSql.build({ + type: 'union' + }); + }).to.throw('`queries` property is not set in `union` clause'); + }); + + it('should throw error with non-array value', function() { + expect(function() { + jsonSql.build({ + type: 'union', + queries: 'wrong' + }); + }).to.throw('`queries` property should be an array in `union` clause'); + }); + + it('should throw error with value length < 2', function() { + expect(function() { + jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }] + }); + }).to.throw('`queries` property should not have length less than 2 in `union` clause'); + }); + + it('should be ok with value length = 2', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") union (select * from "vipUsers");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('type & all combinations', function() { + it('should be ok with `type` = "union", `all` = true', function() { + var result = jsonSql.build({ + type: 'union', + all: true, + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") union all (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "except"', function() { + var result = jsonSql.build({ + type: 'except', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") except (select * from "vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "except", `all` = true', function() { + var result = jsonSql.build({ + type: 'except', + all: true, + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") except all (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "intersect"', function() { + var result = jsonSql.build({ + type: 'intersect', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") intersect (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "intersect", `all` = true', function() { + var result = jsonSql.build({ + type: 'intersect', + all: true, + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") intersect all (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "union" subquery', function() { + var result = jsonSql.build({ + query: { + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + } + }); + + expect(result.query).to.be.equal('select * from ((select * from "users") union (select * ' + + 'from "vipUsers"));'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('sort', function() { + it('should be ok with string value', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + sort: 'age' + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") order by "age";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array value', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + sort: ['age', 'gender'] + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") order by "age", "gender";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object value', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + sort: { + age: 1, + gender: -1 + } + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") order by "age" asc, "gender" desc;' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('limit, offset', function() { + it('should be ok with `limit` property', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + limit: 5 + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") limit 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `offset` property', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + offset: 5 + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") offset 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `limit` and `offset` properties', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + limit: 10, + offset: 20 + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") limit 10 offset 20;' + ); + expect(result.values).to.be.eql({}); + }); + }); +}); diff --git a/legacy/json-sql/tests/6_postgresDialect.js b/legacy/json-sql/tests/6_postgresDialect.js new file mode 100644 index 00000000..790c0ec2 --- /dev/null +++ b/legacy/json-sql/tests/6_postgresDialect.js @@ -0,0 +1,140 @@ +'use strict'; + +var jsonSql = require('../lib')({ + dialect: 'postgresql', + namedValues: false +}); +var expect = require('chai').expect; + +describe('PostgreSQL dialect', function() { + describe('json', function() { + it('should correctly wrap each part of json path', function() { + var result = jsonSql.build({ + table: 'test', + fields: ['params->a->>b'], + condition: { + 'params->>c': {$like: '7%'} + } + }); + + expect(result.query).to.be.equal( + 'select "params"->\'a\'->>\'b\' from "test" ' + + 'where "params"->>\'c\' like ${1};' + ); + }); + + it('should be ok with `$jsonContains` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + 'params->a': { + $jsonContains: {b: 1} + } + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params"->\'a\' @> ${1};' + ); + expect(result.values).to.be.eql(['{"b":1}']); + }); + + it('should be ok with `$jsonIn` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + 'params->a': { + $jsonIn: {$field: 'data->b'} + } + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params"->\'a\' <@ "data"->\'b\';' + ); + expect(result.values).to.be.eql([]); + }); + + it('should be ok with `$jsonHas` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$jsonHas: 'account'} + } + }); + + expect(result.query).to.be.equal('select * from "test" where "params" ? ${1};'); + expect(result.values).to.be.eql(['account']); + }); + + it('should be ok with `$jsonHasAny` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$jsonHasAny: ['a', 'b']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params" ?| array[${1}, ${2}];' + ); + expect(result.values).to.be.eql(['a', 'b']); + }); + + it('should be ok with `$jsonHasAll` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$jsonHasAll: ['a', 'b']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params" ?& array[${1}, ${2}];' + ); + expect(result.values).to.be.eql(['a', 'b']); + }); + + it('should be ok with `$upper` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$upper: ['params', '3498862814541110459l']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where upper("params") = upper(${1});' + ); + expect(result.values).to.be.eql(['3498862814541110459l']); + }); + + it('should be ok with `$lower` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$lower: ['params', '3498862814541110459L']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where lower("params") = lower(${1});' + ); + expect(result.values).to.be.eql(['3498862814541110459L']); + }); + + it('should be ok with `$decode` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$decode: ['params', '3498862814541110459L', 'hex']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params" = decode(${1}, ${2});' + ); + expect(result.values).to.be.eql(['3498862814541110459L', 'hex']); + }); + }); +}); diff --git a/legacy/json-sql/tests/7_create.js b/legacy/json-sql/tests/7_create.js new file mode 100644 index 00000000..a93ca702 --- /dev/null +++ b/legacy/json-sql/tests/7_create.js @@ -0,0 +1,279 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Create', function () { + it('should throw error without `tableFields` property', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users' + }); + }).to.throw('`tableFields` property is not set in `create` clause'); + }); + + it('should throw error with incorrect field type', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "age", + type: "NotNumber" + } + ] + }); + }).to.throw('Invalid type of field: NotNumber'); + }); + + it('should be ok with `tableFields` property', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "age", + type: "Number" + } + ] + }); + + + expect(result.query).to.be.equal('create table if not exists "users"("age" int);'); + }); + + it('should throw error when length property for string field not provided', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String" + } + ] + }); + }).to.throw('Field length can\'t be less or equal 0'); + }); + + it('should throw error with empty name', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: " ", + type: "String" + } + ] + }); + }).to.throw('Name most contains characters'); + }); + + it('should be ok with string field and length', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16 + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16));'); + }); + + it('should be ok with string field not null', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL);'); + }); + + it('should be ok with string field not null primary key', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY);'); + }); + + + it('should be ok with string field not null unique', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + unique: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL UNIQUE);'); + }); + + + it('should be allow only one primary key field', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "secondname", + type: "String", + length: 16, + not_null: true, + primary_key: true + } + ] + }) + }).to.throw("Too much primary key 'secondname' in table"); + }); + + it('should be allow only unique field name', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "name", + type: "String", + length: 16, + not_null: true + } + ] + }) + }).to.throw("Two parameters with same name: name"); + }); + + it("should allow few fields", function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL);'); + }); + + it("should allow few fields", function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ], + foreignKeys: [ + { + field: "name", + table: "person", + table_field: "id" + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); + }); + + it("should allow few fields", function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ], + foreignKeys: [ + { + field: "name", + table: "person", + table_field: "id" + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); + }); +}); diff --git a/legacy/json-sql/tests/8_index.js b/legacy/json-sql/tests/8_index.js new file mode 100644 index 00000000..464a85b3 --- /dev/null +++ b/legacy/json-sql/tests/8_index.js @@ -0,0 +1,45 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +/* + jsonSql.build({ + type: 'index', + table: 'users', + index: { + name: "user_id", + field: "id" + } + }); + */ + +describe('Index', function() { + it('should throw error without name property', function() { + expect(function() { + jsonSql.build({ + type: 'index', + table: 'users' + }); + }).to.throw('`name` property is not set in `index` clause'); + }); + + it('should throw error without indexOn property', function() { + expect(function() { + jsonSql.build({ + type: 'index', + table: 'users', + name: 'index_id' + }); + }).to.throw('`indexOn` property is not set in `index` clause'); + }); + + it('should be ok with name and field property', function () { + var result = jsonSql.build({ + type: "index", + table: "users", + name: "index_id", + indexOn: "id" + }); + }); +}); diff --git a/logic/account.js b/logic/account.js index 3640730b..32ab85f0 100644 --- a/logic/account.js +++ b/logic/account.js @@ -604,6 +604,10 @@ Account.prototype.getAll = function (filter, fields, cb) { }; } + console.log('!!!!') + console.log('!!!!', filter.address) + + var sql = jsonSql.build({ type: 'select', table: this.table, @@ -615,6 +619,8 @@ Account.prototype.getAll = function (filter, fields, cb) { fields: realFields }); + console.log('!!!!', sql.query, sql.values) + this.scope.db.query(sql.query, sql.values).then(function (rows) { return setImmediate(cb, null, rows); }).catch(function (err) { diff --git a/package.json b/package.json index fa97b268..bd85f3fc 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ip": "=1.1.8", "js-nacl": "^1.4.0", "json-schema": "=0.4.0", - "json-sql": "^0.5.0", + "json-sql": "./legacy/json-sql", "lodash": "=4.17.21", "method-override": "=3.0.0", "npm": "=8.17.0", From af057c15b9623776954a79ae5ff4b477e8014c90 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 13:00:51 +0400 Subject: [PATCH 08/33] debug: json-sql --- logic/account.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index 32ab85f0..cb79c8e8 100644 --- a/logic/account.js +++ b/logic/account.js @@ -598,9 +598,11 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; + console.log('!!!!') + console.log('!!!!', filter.address) if (typeof filter.address === 'string') { filter.address = { - $upper: ['address', filter.address] + address: filter.address.toUpperCase() }; } From a3ca2bb2d5028d8c1df60cd77e7836532d1792c6 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 13:02:44 +0400 Subject: [PATCH 09/33] debug: json-sql --- logic/account.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/logic/account.js b/logic/account.js index cb79c8e8..868afe79 100644 --- a/logic/account.js +++ b/logic/account.js @@ -601,9 +601,7 @@ Account.prototype.getAll = function (filter, fields, cb) { console.log('!!!!') console.log('!!!!', filter.address) if (typeof filter.address === 'string') { - filter.address = { - address: filter.address.toUpperCase() - }; + filter.address = filter.address.toUpperCase(); } console.log('!!!!') From a752fda61fcc514776b35f7bea363fc3c0ea7b5d Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 13:19:00 +0400 Subject: [PATCH 10/33] debug: json-sql --- logic/account.js | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/logic/account.js b/logic/account.js index 868afe79..b3bd7c13 100644 --- a/logic/account.js +++ b/logic/account.js @@ -598,10 +598,10 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; - console.log('!!!!') - console.log('!!!!', filter.address) if (typeof filter.address === 'string') { - filter.address = filter.address.toUpperCase(); + filter.address = { + $ilike: ['address', filter.address] + }; } console.log('!!!!') diff --git a/package.json b/package.json index bd85f3fc..fa97b268 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ip": "=1.1.8", "js-nacl": "^1.4.0", "json-schema": "=0.4.0", - "json-sql": "./legacy/json-sql", + "json-sql": "^0.5.0", "lodash": "=4.17.21", "method-override": "=3.0.0", "npm": "=8.17.0", From e7743981ee6b633b04b7430c628cef13868287e9 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 13:24:27 +0400 Subject: [PATCH 11/33] debug: json-sql --- logic/account.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index b3bd7c13..91d1fe24 100644 --- a/logic/account.js +++ b/logic/account.js @@ -600,7 +600,8 @@ Account.prototype.getAll = function (filter, fields, cb) { if (typeof filter.address === 'string') { filter.address = { - $ilike: ['address', filter.address] + // $ilike: ['address', filter.address] + address: {$ilike: filter.address} }; } From dec765e70b9b680a9e2bff5411b33cc83b87b41e Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 13:26:41 +0400 Subject: [PATCH 12/33] debug: json-sql --- logic/account.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index 91d1fe24..2a476e54 100644 --- a/logic/account.js +++ b/logic/account.js @@ -601,10 +601,24 @@ Account.prototype.getAll = function (filter, fields, cb) { if (typeof filter.address === 'string') { filter.address = { // $ilike: ['address', filter.address] - address: {$ilike: filter.address} + $ilike: filter.address }; } + // it('should be ok with `$ilike` conditional operator', function() { + // var result = jsonSql.build({ + // table: 'test', + // condition: { + // params: {$ilike: 'hello%'} + // } + // }); + + // expect(result.query).to.be.equal( + // 'select * from "test" where "params" ilike $1;' + // ); + // expect(result.values).to.be.eql(['hello%']); + // }); + console.log('!!!!') console.log('!!!!', filter.address) From 30e2ef9d2aeb30e8dcc1272b335cbde917a5ecf1 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 13:45:53 +0400 Subject: [PATCH 13/33] debug: json-sql --- logic/account.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/logic/account.js b/logic/account.js index 2a476e54..84e8d34b 100644 --- a/logic/account.js +++ b/logic/account.js @@ -598,12 +598,12 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; - if (typeof filter.address === 'string') { - filter.address = { - // $ilike: ['address', filter.address] - $ilike: filter.address - }; - } + // if (typeof filter.address === 'string') { + // filter.address = { + // // $ilike: ['address', filter.address] + // $ilike: filter.address + // }; + // } // it('should be ok with `$ilike` conditional operator', function() { // var result = jsonSql.build({ From 089f229f0868aabef1074a297c17b5e1e33f6926 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 14:10:24 +0400 Subject: [PATCH 14/33] debug: json-sql --- logic/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index 84e8d34b..59fff597 100644 --- a/logic/account.js +++ b/logic/account.js @@ -3,7 +3,7 @@ var async = require('async'); var pgp = require('pg-promise'); var path = require('path'); -var jsonSql = require('json-sql')(); +var jsonSql = require('json-sql')({dialect: 'postgresql', namedValues: false}); jsonSql.setDialect('postgresql'); var constants = require('../helpers/constants.js'); var slots = require('../helpers/slots.js'); From 6566e07bf72822c869715309d177b95c953a5821 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 14:11:43 +0400 Subject: [PATCH 15/33] debug: json-sql --- logic/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index 59fff597..e3466c5e 100644 --- a/logic/account.js +++ b/logic/account.js @@ -3,7 +3,7 @@ var async = require('async'); var pgp = require('pg-promise'); var path = require('path'); -var jsonSql = require('json-sql')({dialect: 'postgresql', namedValues: false}); +var jsonSql = require('json-sql')({dialect: 'postgresql'}); jsonSql.setDialect('postgresql'); var constants = require('../helpers/constants.js'); var slots = require('../helpers/slots.js'); From 043e412077db7796716fa4f66694d3e39bbf7608 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 14:17:33 +0400 Subject: [PATCH 16/33] debug: json-sql --- logic/account.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/logic/account.js b/logic/account.js index e3466c5e..1b690863 100644 --- a/logic/account.js +++ b/logic/account.js @@ -3,7 +3,7 @@ var async = require('async'); var pgp = require('pg-promise'); var path = require('path'); -var jsonSql = require('json-sql')({dialect: 'postgresql'}); +var jsonSql = require('json-sql')({namedValues: false}); jsonSql.setDialect('postgresql'); var constants = require('../helpers/constants.js'); var slots = require('../helpers/slots.js'); @@ -598,12 +598,12 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; - // if (typeof filter.address === 'string') { - // filter.address = { - // // $ilike: ['address', filter.address] - // $ilike: filter.address - // }; - // } + if (typeof filter.address === 'string') { + filter.address = { + // $ilike: ['address', filter.address] + $ilike: filter.address + }; + } // it('should be ok with `$ilike` conditional operator', function() { // var result = jsonSql.build({ From 24155a9a90a99164dcc5c49f8318d04fc72048ed Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 14:25:46 +0400 Subject: [PATCH 17/33] debug: json-sql --- legacy/json-sql/.gitignore | 1 - legacy/json-sql/.jshintrc | 19 - legacy/json-sql/.npmignore | 2 - legacy/json-sql/LICENSE | 22 - legacy/json-sql/README.md | 145 -- legacy/json-sql/docs/README.md | 1180 ---------------- legacy/json-sql/gulpfile.js | 27 - legacy/json-sql/lib/builder.js | 233 ---- legacy/json-sql/lib/dialects/base/blocks.js | 441 ------ .../json-sql/lib/dialects/base/conditions.js | 122 -- legacy/json-sql/lib/dialects/base/index.js | 58 - .../lib/dialects/base/logicalOperators.js | 37 - .../json-sql/lib/dialects/base/modifiers.js | 37 - legacy/json-sql/lib/dialects/base/scheme.js | 146 -- .../json-sql/lib/dialects/base/templates.js | 220 --- legacy/json-sql/lib/dialects/mssql/index.js | 13 - .../lib/dialects/postgresql/blocks.js | 33 - .../lib/dialects/postgresql/conditions.js | 87 -- .../json-sql/lib/dialects/postgresql/index.js | 39 - .../lib/dialects/postgresql/templates.js | 269 ---- legacy/json-sql/lib/dialects/sqlite/blocks.js | 23 - legacy/json-sql/lib/dialects/sqlite/index.js | 17 - .../json-sql/lib/dialects/sqlite/templates.js | 236 ---- legacy/json-sql/lib/index.js | 8 - legacy/json-sql/lib/valuesStore.js | 31 - legacy/json-sql/package.json | 44 - legacy/json-sql/tests/0_base.js | 304 ---- legacy/json-sql/tests/1_select.js | 1222 ----------------- legacy/json-sql/tests/2_insert.js | 73 - legacy/json-sql/tests/3_update.js | 123 -- legacy/json-sql/tests/4_delete.js | 58 - legacy/json-sql/tests/5_union.js | 256 ---- legacy/json-sql/tests/6_postgresDialect.js | 140 -- legacy/json-sql/tests/7_create.js | 279 ---- legacy/json-sql/tests/8_index.js | 45 - logic/account.js | 26 +- 36 files changed, 4 insertions(+), 6012 deletions(-) delete mode 100644 legacy/json-sql/.gitignore delete mode 100644 legacy/json-sql/.jshintrc delete mode 100644 legacy/json-sql/.npmignore delete mode 100644 legacy/json-sql/LICENSE delete mode 100644 legacy/json-sql/README.md delete mode 100644 legacy/json-sql/docs/README.md delete mode 100644 legacy/json-sql/gulpfile.js delete mode 100644 legacy/json-sql/lib/builder.js delete mode 100644 legacy/json-sql/lib/dialects/base/blocks.js delete mode 100644 legacy/json-sql/lib/dialects/base/conditions.js delete mode 100644 legacy/json-sql/lib/dialects/base/index.js delete mode 100644 legacy/json-sql/lib/dialects/base/logicalOperators.js delete mode 100644 legacy/json-sql/lib/dialects/base/modifiers.js delete mode 100644 legacy/json-sql/lib/dialects/base/scheme.js delete mode 100644 legacy/json-sql/lib/dialects/base/templates.js delete mode 100644 legacy/json-sql/lib/dialects/mssql/index.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/blocks.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/conditions.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/index.js delete mode 100644 legacy/json-sql/lib/dialects/postgresql/templates.js delete mode 100644 legacy/json-sql/lib/dialects/sqlite/blocks.js delete mode 100644 legacy/json-sql/lib/dialects/sqlite/index.js delete mode 100644 legacy/json-sql/lib/dialects/sqlite/templates.js delete mode 100644 legacy/json-sql/lib/index.js delete mode 100644 legacy/json-sql/lib/valuesStore.js delete mode 100644 legacy/json-sql/package.json delete mode 100644 legacy/json-sql/tests/0_base.js delete mode 100644 legacy/json-sql/tests/1_select.js delete mode 100644 legacy/json-sql/tests/2_insert.js delete mode 100644 legacy/json-sql/tests/3_update.js delete mode 100644 legacy/json-sql/tests/4_delete.js delete mode 100644 legacy/json-sql/tests/5_union.js delete mode 100644 legacy/json-sql/tests/6_postgresDialect.js delete mode 100644 legacy/json-sql/tests/7_create.js delete mode 100644 legacy/json-sql/tests/8_index.js diff --git a/legacy/json-sql/.gitignore b/legacy/json-sql/.gitignore deleted file mode 100644 index c2658d7d..00000000 --- a/legacy/json-sql/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/legacy/json-sql/.jshintrc b/legacy/json-sql/.jshintrc deleted file mode 100644 index fe7c5b09..00000000 --- a/legacy/json-sql/.jshintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "node": true, - "freeze": true, - "maxlen": 100, - "smarttabs": true, - "indent": 1, - "quotmark": "single", - "strict": true, - "globalstrict": true, - // use es3 to get 'extra comma' at object literal error - "es3": true, - "undef": true, - "unused": true, - "immed": true, - "eqeqeq": true, - "globals": { - "JSON": false - } -} diff --git a/legacy/json-sql/.npmignore b/legacy/json-sql/.npmignore deleted file mode 100644 index 3796175f..00000000 --- a/legacy/json-sql/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -.npmignore -test \ No newline at end of file diff --git a/legacy/json-sql/LICENSE b/legacy/json-sql/LICENSE deleted file mode 100644 index 2ac98523..00000000 --- a/legacy/json-sql/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright (c) 2013-2014 Oleg Korobenko - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/legacy/json-sql/README.md b/legacy/json-sql/README.md deleted file mode 100644 index 7891eaed..00000000 --- a/legacy/json-sql/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# JSON-SQL - -Library for mapping mongo-style query objects to SQL queries. - -## Quick Start - -Install it with NPM or add it to your package.json: - -``` bash -$ npm install json-sql -``` - -Then: - -``` js -var jsonSql = require('json-sql')(); - -var sql = jsonSql.build({ - type: 'select', - table: 'users', - fields: ['name', 'age'], - condition: {name: 'Max', id: 6} -}); - -sql.query -// sql string: -// select name, age from users where name = $p1 && id = 6; - -sql.values -// hash of values: -// { p1: 'Max' } -``` - -## Documentation - -Documentation is available at the [./docs directory](./docs). - -## Examples - -__Select with join:__ - -``` js -var sql = jsonSql.build({ - type: 'select', - table: 'users', - join: { - documents: { - on: {'user.id': 'documents.userId'} - } - } -}); - -sql.query -// select * from users join documents on user.id = documents.userId; - -sql.values -// {} -``` - -__Insert:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'users', - values: { - name: 'John', - lastname: 'Snow', - age: 24, - gender: 'male' - } -}); - -sql.query -// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3); - -sql.values -// { p1: 'John', p2: 'Snow', p3: 'male' } -``` - -__Update:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'users', - condition: { - id: 5 - }, - modifier: { - role: 'admin' - age: 33 - } -}); - -sql.query -// update users set role = $p1, age = 33 where id = 5; - -sql.values -// { p1: 'admin' } -``` - -__Remove:__ - -``` js -var sql = jsonSql.build({ - type: 'remove', - table: 'users', - condition: { - id: 5 - } -}); - -sql.query -// delete from users where id = 5; - -sql.values -// {} -``` - -For more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests). - -## Tests - -Clone repository from github, `cd` into cloned dir and install dev dependencies: - -``` bash -$ npm install -``` - -Then run tests with command: - -``` bash -$ gulp test -``` - -Or run tests coverage with command: - -``` bash -$ gulp coverage -``` - -## License - -[MIT](./LICENSE) diff --git a/legacy/json-sql/docs/README.md b/legacy/json-sql/docs/README.md deleted file mode 100644 index 8752f703..00000000 --- a/legacy/json-sql/docs/README.md +++ /dev/null @@ -1,1180 +0,0 @@ -# Documentation - -## Table of contents - -* [API](#api) - - [Initialization](#initialization) - - __[build(query)](#buildquery)__ - - [configure(options)](#configureoptions) - - [setDialect(name)](#setdialectname) -* __[Queries](#queries)__ - - [type: 'create'](#type-create) - - [type: 'select'](#type-select) - - [type: 'insert'](#type-insert) - - [type: 'update'](#type-update) - - [type: 'remove'](#type-remove) - - [type: 'union' | 'intersect' | 'except'](#type-union--intersect--except) -* __[Blocks](#blocks)__ -* __[Condition operators](#condition-operators)__ - ---- - -## API - -### Initialization - -To create new instance of json-sql builder you can use factory function: - -``` js -var jsonSql = require('json-sql')(options); -``` - -or create instance by class constructor: - -``` js -var jsonSql = new (require('json-sql').Builder)(options); -``` - -`options` are similar to [configure method options](#available-options). - ---- - -### build(query) - -Create sql query from mongo-style query object. - -`query` is a json object that has required property `type` and a set of query-specific properties. `type` property determines the type of query. List of available values of `type` property you can see at [Queries section](#queries). - -Returns object with properties: - -| Property | Description | -| -------- | ----------- | -| `query` | SQL query string | -| `value` | Array or object with values.
Exists only if `separatedValues = true`. | -| `prefixValues()` | Method to get values with `valuesPrefix`.
Exists only if `separatedValues = true`. | -| `getValuesArray()` | Method to get values as array.
Exists only if `separatedValues = true`. | -| `getValuesObject()` | Method to get values as object.
Exists only if `separatedValues = true`. | - ---- - -### configure(options) - -Set options of json-sql builder instance. - -#### Available options - -| Option name | Default value | Description | -| ----------- | ------------- | ----------- | -| `separatedValues` | `true` | If `true` - create placeholder for each string value and put it value to result `values`.
If `false` - put string values into sql query without placeholder (potential threat of sql injection). | -| `namedValues` | `true` | If `true` - create hash of values with placeholders p1, p2, ...
If `false` - put all values into array.
Option is used if `separatedValues = true`. | -| `valuesPrefix` | `'$'` | Prefix for values placeholders
Option is used if `namedValues = true`. | -| `dialect` | `'base'` | Active dialect. See setDialect for dialects list. | -| `wrappedIdentifiers` | `true` | If `true` - wrap all identifiers with dialect wrapper (name -> "name"). | - ---- - -### setDialect(name) - -Set active dialect, name can has value `'base'`, `'mssql'`, `'mysql'`, `'postrgresql'` or `'sqlite'`. - ---- - -## Queries - -### type: 'create' - ->[ [tableFields](#tableFields) ]
->[ [table](#table) ]
->[ [foreignKeys](#foreignKeys) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - unique: true, - default: "empty" - } - ] -}); - -sql.query -// create table if not exists "users"("name" varchar(16) NOT NULL default "empty" UNIQUE); -``` - ---- - -### type: 'select' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[ [distinct](#distinct) ]
->[ [fields](#fields) ]
->[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
->[ [alias](#alias) ]
->[ [join](#join) ]
->[ [condition](#condition) ]
->[ [group](#group) ]
->[ [sort](#sort) ]
->[ [limit](#limit) ]
->[ [offset](#offset) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'select', - fields: ['a', 'b'] - table: 'table' -}); - -sql.query -// select "a", "b" from "table"; -``` - -If `fields` is not specified in query, result fields is `*` (all columns of the selected rows). - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'select', - table: 'table' -}); - -sql.query -// select * from "table"; -``` - ---- - -### type: 'insert' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[ [or](#or) ]
->[table](#table)
->[values](#values)
->[ [condition](#condition) ]
->[ [returning](#returning) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: {a: 4} -}); - -sql.query -// insert into "table" ("a") values (4); -``` - ---- - -### type: 'update' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[ [or](#or) ]
->[table](#table)
->[modifier](#modifier)
->[ [condition](#condition) ]
->[ [returning](#returning) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'table', - modifier: {a: 5} -}); - -sql.query -// update "table" set a = 5; -``` - ---- - -### type: 'remove' - ->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[table](#table)
->[ [condition](#condition) ]
->[ [returning](#returning) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'remove', - table: 'table' -}); - -sql.query -// delete from "table"; -``` - ---- - -### type: 'union' | 'intersect' | 'except' - ->[ [all](#all) ]
->[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
->[queries](#queries)
->[ [sort](#sort) ]
->[ [limit](#limit) ]
->[ [offset](#offset) ] - -__`type: 'union'` example:__ - -``` js -var sql = jsonSql.build({ - type: 'union', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") union (select * from "table2"); -``` - -__`type: 'intersect'` example:__ - -``` js -var sql = jsonSql.build({ - type: 'intersect', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") intersect (select * from "table2"); -``` - -__`type: 'except'` example:__ - -``` js -var sql = jsonSql.build({ - type: 'except', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") except (select * from "table2"); -``` - ---- - -## Blocks - -Blocks are small chunks of query. - -### with, withRecursive - -Should be an `array` or an `object`. - -If value is an `array`, each item of array should be an `object` and should conform the scheme: - ->name
->[ [fields](#fields) ]
->[query](#query) | [select](#select) | [expression](#expression) - -__Example:__ - -``` js -var sql = jsonSql.build({ - 'with': [{ - name: 'table', - select: {table: 'withTable'} - }], - table: 'table' -}); - -sql.query -// with "table" as (select * from "withTable") select * from "table"; -``` - -If value is an `object`, keys of object interpret as names and each value should be an `object` and should conform the scheme: - ->[ name ]
->[ [fields](#fields) ]
->[query](#query) | [select](#select) | [expression](#expression) - -__Example:__ - -``` js -var sql = jsonSql.build({ - 'with': { - table: { - select: {table: 'withTable'} - } - }, - table: 'table' -}); - -sql.query -// with "table" as (select * from "withTable") select * from "table"; -``` - ---- - -### distinct - -Should be a `boolean`: - -``` -distinct: true -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - distinct: true, - table: 'table' -}); - -sql.query -// select distinct * from "table"; -``` - ---- - -### tableFields - -Should be an `array` - -Contains array of table`s fields - ---- - -### foreignKeys - -Should be an `array` - -__Example:__ - -``` js -var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ], - foreignKeys: [ - { - field: "name", - table: "person", - table_field: "id" - } - ] -}); - -sql.query -// create table "users"(name varchar(16) NOT NULL PRIMARY KEY,age int NOT NULL, FOREIGN KEY (name) REFERENCES person(id)); -``` - ---- - -### fields - -Should be an `array` or an `object`. - -If value is an `array`, each item interprets as [term block](#term). - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [ - 'a', - {b: 'c'}, - {table: 'd', name: 'e', alias: 'f'}, - ['g'] - ], - table: 'table' -}); - -sql.query -// select "a", "b" as "c", "d"."e" as "f", "g" from "table"; -``` - -If value is an `object`, keys of object interpret as field names and each value should be an `object` and should conform the scheme: - ->[ name ]
->[ [table](#table) ]
->[ cast ]
->[ [alias](#alias) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: { - a: 'b', - d: {table: 'c', alias: 'e'} - }, - table: 'table' -}); - -sql.query -// select "a" as "b", "c"."d" as "e" from "table"; -``` - ---- - -### term - -Should be: -* a `string` - interprets as field name; -* another simple type or an `array` - interprets as value; -* an `object` - should conform the scheme: - ->[query](#query) | [select](#select) | [field](#field) | [value](#value) | [func](#func) | [expression](#expression)
->[ cast ]
->[ [alias](#alias) ] - ---- - -### field - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -field: 'fieldName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{field: 'a'}], - table: 'table' -}); - -sql.query -// select "a" from "table"; -``` - -If value is an `object` it should conform the scheme: - ->name
->[ [table](#table) ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{field: {name: 'a', table: 'table'}}], - table: 'table' -}); - -sql.query -// select "table"."a" from "table"; -``` - ---- - -### value - -Can have any type. - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [ - {value: 5}, - {value: 'test'} - ], - table: 'table' -}); - -sql.query -// select 5, $p1 from "table"; - -sql.values -// {p1: 'test'} -``` - ---- - -### table - -Should be a `string`: - -``` -table: 'tableName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table' -}); - -sql.query -// select * from "table"; -``` - ---- - -### query - -Should be an `object`. Value interprets as sub-query and process recursively with [build(query)](#buildquery) method. - -__Example:__ - -``` js -var sql = jsonSql.build({ - query: {type: 'select', table: 'table'} -}); - -sql.query -// select * from (select * from "table"); -``` - ---- - -### select - -Should be an `object`. Value interprets as sub-select and process recursively with [build(query)](#buildquery) method. - -__Example:__ - -``` js -var sql = jsonSql.build({ - select: {table: 'table'} -}); - -sql.query -// select * from (select * from "table"); -``` - ---- - -### func - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -func: 'random' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{func: 'random'}], - table: 'table' -}); - -sql.query -// select random() from "table"; -``` - -If value is an `object` it should conform the scheme: - ->name
->[ args ] - -where `name` is a `string` name of function, `args` is an `array` that contains it arguments. - -__Example:__ - -``` js -var sql = jsonSql.build({ - fields: [{ - func: { - name: 'sum', - args: [{field: 'a'}] - } - }], - table: 'table' -}); - -sql.query -// select sum("a") from table; -``` - ---- - -### expression - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -expression: 'random()' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - expression: 'generate_series(2, 4)' -}); - -sql.query -// select * from generate_series(2, 4); -``` - -If value is an `object` it should conform the scheme: - ->pattern
->[ values ] - -where `pattern` is a `string` pattern with placeholders `{placeholderName}`, `values` is a hash that contains values for each `placeholderName`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - expression: { - pattern: 'generate_series({start}, {stop})', - values: {start: 2, stop: 4} - } -}); - -sql.query -// select * from generate_series(2, 4); -``` - ---- - -### alias - -Should be a `string` or an `object`. - -If value is a `string`: - -``` -alias: 'aliasName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - alias: 'alias' -}); - -sql.query -// select * from "table" as "alias"; -``` - -If value is an `object` it should conform the scheme: - ->name
->[ columns ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - alias: {name: 'alias'} -}); - -sql.query -// select * from "table" as "alias"; -``` - ---- - -### join - -Should be an `array` or an `object`. - -If value is an `array`, each item of array should be an `object` and should conform the scheme: - ->[ type ]
->[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
->[ [alias](#alias) ]
->[ on ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - join: [{ - type: 'right', - table: 'joinTable', - on: {'table.a': 'joinTable.b'} - }] -}); - -sql.query -// select * from "table" right join "joinTable" on "table"."a" = "joinTable"."b"; -``` - -If value is an `object`, keys of object interpret as table names and each value should be an `object` and should conform the scheme: - ->[ type ]
->[ [table](#table) | [query](#query) | [select](#select) | [expression](#expression) ]
->[ [alias](#alias) ]
->[ on ] - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - join: { - joinTable: { - type: 'inner', - on: {'table.a': 'joinTable.b'} - } - }] -}); - -sql.query -// select * from "table" inner join "joinTable" on "table"."a" = "joinTable"."b"; -``` - -__Join with sub-select example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - join: [{ - select: {table: 'joinTable'}, - alias: 'joinTable', - on: {'table.a': 'joinTable.b'} - }] -}); - -sql.query -// select * from "table" join (select * from "joinTable") as "joinTable" on "table"."a" = "joinTable"."b"; -``` - ---- - -### condition - -Should be an `array` or an `object`. - -__`array` example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - condition: [ - {a: {$gt: 1}}, - {b: {$lt: 10}} - ] -}); - -sql.query -// select * from "table" where "a" > 1 and "b" < 10; -``` - -__`object` example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - condition: { - a: {$gt: 1}, - b: {$lt: 10} - } -}); - -sql.query -// select * from "table" where "a" > 1 and "b" < 10; -``` - ---- - -### group - -Should be a `string` or an `array`. - -If value is a `string`: - -``` -group: 'fieldName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - group: 'a' -}); - -sql.query -// select * from "table" group by "a"; -``` - -If value is an `array`: - -``` -group: ['fieldName1', 'fieldName2'] -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - group: ['a', 'b'] -}); - -sql.query -// select * from "table" group by "a", "b"; -``` - ---- - -### sort - -Should be a `string`, an `array` or an `object`. - -If value is a `string`: - -``` -sort: 'fieldName' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - sort: 'a' -}); - -sql.query -// select * from "table" order by "a"; -``` - -If value is an `array`: - -``` -sort: ['fieldName1', 'fieldName2'] -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - sort: ['a', 'b'] -}); - -sql.query -// select * from "table" order by "a", "b"; -``` - -If value is an `object`: - -``` -sort: { - fieldName1: 1, - fieldName2: -1 -} -``` - -__Example__: - -``` js -var sql = jsonSql.build({ - table: 'table', - sort: {a: 1, b: -1} -}); - -sql.query -// select * from "table" order by "a" asc, "b" desc; -``` - ---- - -### limit - -Should be a `number`. - -``` -limit: limitValue -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - limit: 5 -}); - -sql.query -// select * from "table" limit 5; -``` - ---- - -### offset - -Should be a `number`. - -``` -offset: offsetValue -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - table: 'table', - offset: 5 -}); - -sql.query -// select * from "table" offset 5; -``` - ---- - -### or - -Should be a `string`. - -Available values: 'rollback', 'abort', 'replace', 'fail', 'ignore'. - -``` -or: 'orValue' -``` - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - or: 'replace', - table: 'table', - values: {a: 5} -}); - -sql.query -// insert or replace into "table" ("a") values (5); -``` - ---- - -### values - -Should be an `array` or an `object`. - -If value is an `array`, each item should be an `object` and interprets as single inserted row where keys are field names and corresponding values are field values. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: [ - {a: 5, b: 'text1'}, - {a: 6, b: 'text2'} - ] -}); - -sql.query -// insert into "table" ("a", "b") values (5, $p1), (6, $p2); - -sql.values -// {p1: 'text1', p2: 'text2'} -``` - -If value is an `object`, it interprets as single inserted row where keys are field names and corresponding values are field values. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: {a: 5, b: 'text'} -}); - -sql.query -// insert into "table" ("a", "b") values (5, $p1); - -sql.values -// {p1: 'text'} -``` - -Also you can specify fields array. If there no key in value object it value is `null`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - fields: ['a', 'b', 'c'], - values: {c: 'text', b: 5} -}); - -sql.query -// insert into "table" ("a", "b", "c") values (null, 5, $p1); - -sql.values -// {p1: 'text'} -``` - ---- - -### modifier - -Should be an `object`. - -You can specify modifier operator. -Available operators: `$set`, `$inc`, `$dec`, `$mul`, `$div`, `$default`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'table', - modifier: { - $set: {a: 5}, - $default: {b: true}, - $inc: {c: 10} - } -}); - -sql.query -// update "table" set "a" = 5, "b" = default, "c" = "c" + 10; -``` - -If modifier operator is not specified it uses default operator `$set`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'update', - table: 'table', - modifier: {a: 5} -}); - -sql.query -// update "table" set "a" = 5; -``` - ---- - -### returning - -Format is similar to [fields](#fields) block. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'insert', - table: 'table', - values: {a: 5}, - returning: ['a'] -}); - -sql.query -// insert into "table" ("a") values (5) returning "a"; -``` - ---- - -### all - -Should be a `boolean`. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'union', - all: true, - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") union all (select * from "table2"); -``` - ---- - -### queries - -Should be an `array` with minimum 2 items. Each item interprets as sub-query and process recursively with [build(query)](#buildquery) method. - -__Example:__ - -``` js -var sql = jsonSql.build({ - type: 'union', - queries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// (select * from "table1") union (select * from "table2"); - -//or for sqlite3 -jsonSql.setDialect("sqlite"); -var sql = jsonSql.build({ - type: 'union', - unionqueries: [ - {type: 'select', table: 'table1'}, - {type: 'select', table: 'table2'} - ] -}); - -sql.query -// select * from "table1" union select * from "table2"; -``` - ---- - -## Condition operators - -TODO: write this section \ No newline at end of file diff --git a/legacy/json-sql/gulpfile.js b/legacy/json-sql/gulpfile.js deleted file mode 100644 index c7bbf59b..00000000 --- a/legacy/json-sql/gulpfile.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -var gulp = require('gulp'); -var mocha = require('gulp-mocha'); -var jshint = require('gulp-jshint'); - -gulp.task('test', function() { - return gulp.src(['./tests/*.js'], {read: false}) - .pipe( - mocha({ - reporter: 'spec', - bail: true - }) - ); -}); - -gulp.task('lint', function() { - return gulp.src('./lib/**/*.js') - .pipe(jshint()) - .pipe(jshint.reporter('unix')); -}); - -gulp.task('lintTests', function() { - return gulp.src('./tests/*.js') - .pipe(jshint()) - .pipe(jshint.reporter('unix')); -}); \ No newline at end of file diff --git a/legacy/json-sql/lib/builder.js b/legacy/json-sql/lib/builder.js deleted file mode 100644 index fa0d2a0d..00000000 --- a/legacy/json-sql/lib/builder.js +++ /dev/null @@ -1,233 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -var dialects = { - base: require('./dialects/base'), - mssql: require('./dialects/mssql'), - postgresql: require('./dialects/postgresql'), - sqlite: require('./dialects/sqlite') -}; - -var blockRegExp = /\{([a-z0-9]+)\}(.|$)/ig; - -var Builder = module.exports = function (options) { - this.configure(options); -}; - -Builder.prototype._reset = function () { - if (this.options.separatedValues) { - this._placeholderId = 1; - this._values = this.options.namedValues ? {} : []; - } else { - delete this._placeholderId; - delete this._values; - } - - this._query = ''; -}; - -Builder.prototype._getPlaceholder = function () { - return (this.options.namedValues ? 'p' : '') + (this._placeholderId++); -}; - -Builder.prototype._wrapPlaceholder = function (name) { - return this.options.valuesPrefix + '{' + name + '}'; -}; - -Builder.prototype._pushValue = function (value) { - var valueType = typeof value; - - if (valueType === 'undefined') { - return 'null'; - } else if (value === null || valueType === 'number' || - valueType === 'boolean') { - return String(value); - } else if (valueType === 'string') { - if (this.options.separatedValues) { - var placeholder = this._getPlaceholder(); - if (this.options.namedValues) { - this._values[placeholder] = value; - } else { - this._values.push(value); - } - return this._wrapPlaceholder(placeholder); - } else { - return '\'' + value + '\''; - } - } else if (valueType == 'object') { - if (Object.prototype.toString.call(value) == "[object Array]") { - if (value.length > 0) { - return value.join(",") - } else { - return 'null'; - } - } - else if (Buffer.isBuffer(value)) { - var placeholder = this._getPlaceholder(); - this._values[placeholder] = value; - return this._wrapPlaceholder(placeholder); - //return "X'" + value.toString('hex') + "'"; - } else { - var placeholder = this._getPlaceholder(); - this._values[placeholder] = value; - return this._wrapPlaceholder(placeholder); - //return ("'" + JSON.stringify(value).replace(/'/g, "''") + "'"); - } - } else { - throw new Error('Wrong value type "' + valueType + '"'); - } -}; - -Builder.prototype.configure = function (options) { - options = options || {}; - - this.options = _(options).defaults({ - separatedValues: true, - namedValues: true, - valuesPrefix: '$', - dialect: 'base', - wrappedIdentifiers: true - }); - - this.setDialect(this.options.dialect); - - this._reset(); -}; - -Builder.prototype.buildCondition = function (params) { - var self = this; - var result = ''; - - var condition = params.condition; - var logicalOperator = params.logicalOperator || - this.dialect.config.defaultLogicalOperator; - - if (_.isObject(condition) && !_.isEmpty(condition)) { - // if condition is array: [{a: 1}, {b: 2}] - if (_.isArray(condition)) { - result = _(condition).map(function (item) { - return self.buildCondition({ - condition: item, - operator: params.operator - }); - }); - - // if condition is object - } else { - result = _(condition).map(function (value, field) { - // if field name is operator - if (field[0] === '$') { - // if field name is logical operator: {$logicalOperator: {a: 1}} - if (!self.dialect.logicalOperators.has(field)) { - throw new Error('Unknown logical operator "' + field + '"'); - } - - return self.buildCondition({ - condition: value, - logicalOperator: field, - operator: params.operator - }); - - // if condition item is object: {a: {$operator: 4}} - } else if (_.isObject(value)) { - var logicalOperatorFn = self.dialect.logicalOperators - .get(self.dialect.config.defaultLogicalOperator); - - return logicalOperatorFn(_(value).map(function (operatorValue, operator) { - var operatorFn = self.dialect.conditions.get(operator); - if (!operatorFn) { - throw new Error('Unknown operator "' + operator + '"'); - } - return operatorFn(field, operator, operatorValue); - })); - - // if condition item type is simple: {a: 1, b: 2} - } else { - var operatorFn = self.dialect.conditions.get(params.operator); - if (!operatorFn) { - throw new Error('Unknown operator "' + params.operator + '"'); - } - return operatorFn(field, params.operator, value); - } - }); - } - - result = this.dialect.logicalOperators - .get(logicalOperator)(_(result).compact()); - } - - return result; -}; - -Builder.prototype.buildBlock = function (block, params) { - var blockFn = this.dialect.blocks.get(block); - - if (!blockFn) { - throw new Error('Unknown block "' + block + '"'); - } - - return blockFn(params); -}; - -Builder.prototype.buildTemplate = function (type, params) { - var self = this; - - var template = this.dialect.templates.get(type); - if (!template) { - throw new Error('Unknown template type "' + type + '"'); - } - - params = _({}).defaults(params, template.defaults); - - if (template.validate) { - template.validate(type, params); - } - - return template.pattern.replace(blockRegExp, function (fullMatch, block, space) { - if (typeof params[block] !== 'undefined') { - return self.buildBlock(block, params) + space; - } else { - return ''; - } - }).trim(); -}; - -Builder.prototype.build = function (params) { - var builder = this; - - this._reset(); - - this._query = this.buildTemplate('query', {queryBody: params}) + ';'; - - if (this.options.separatedValues) { - return { - query: this._query, - values: this._values, - prefixValues: function () { - var values = {}; - _(this.getValuesObject()).each(function (value, name) { - values[builder._wrapPlaceholder(name)] = value; - }); - return values; - }, - getValuesArray: function () { - return _.isArray(this.values) ? this.values : _(this.values).values(); - }, - getValuesObject: function () { - return _.isArray(this.values) ? _(_.range(1, this.values.length + 1)).object(this.values) : - this.values; - } - }; - } else { - return {query: this._query}; - } -}; - -Builder.prototype.setDialect = function (name) { - if (!dialects[name]) { - throw new Error('Unknown dialect "' + name + '"'); - } - - this.dialect = new (dialects[name])(this); -}; diff --git a/legacy/json-sql/lib/dialects/base/blocks.js b/legacy/json-sql/lib/dialects/base/blocks.js deleted file mode 100644 index 809c5fd6..00000000 --- a/legacy/json-sql/lib/dialects/base/blocks.js +++ /dev/null @@ -1,441 +0,0 @@ -'use strict'; - -var _ = require('underscore'); -var scheme = require('./scheme.js'); - -function removeTopBrackets(condition) { - if (condition.length && condition[0] === '(' && - condition[condition.length - 1] === ')') { - condition = condition.slice(1, condition.length - 1); - } - - return condition; -} - -module.exports = function(dialect) { - dialect.blocks.add('distinct', function() { - return 'distinct'; - }); - - - dialect.blocks.add('field', function(params) { - var field = params.field || params.$field; - var expression = params.expression || params.$expression; - - if (!field && !expression) { - throw new Error('Neither `field` nor `expression` properties aren\'t set'); - } - - if (field) { - field = this.dialect._wrapIdentifier(field); - - var table = params.table || params.$table; - if (table) { - field = this.buildBlock('table', {table: table}) + '.' + field; - } - - if (expression) { - if (!_.isArray(expression)) expression = [expression]; - var exprSuffix = (new Array(expression.length + 1)).join(')'); - field = expression.join('(') + '(' + field + exprSuffix; - } - } else { - field = expression; - } - - var cast = params.cast || params.$cast; - if (cast) { - field = 'cast(' + field + ' as ' + cast + ')'; - } - - var alias = params.alias || params.$alias; - if (alias) { - field += ' ' + this.buildBlock('alias', {alias: alias}); - } - - return field; - }); - - - dialect.blocks.add('fields', function(params) { - var self = this; - - var fields = params.fields || {}; - var result = ''; - - if (!_.isObject(fields) && !_.isString(fields)) { - throw new Error('Invalid `fields` property type "' + (typeof fields) + '"'); - } - - if (_.isObject(fields)) { - if (_.isEmpty(fields)) { - result = '*'; - } else { - // If fields is array: ['a', {b: 'c'}, {field: '', table: 't', alias: 'r'}] - if (_.isArray(fields)) { - result = _(fields).map(function(item) { - - if (_.isObject(item)) { - // if field is field object: {field: '', table: 't', alias: 'r'} - if (item.field || item.expression) { - return self.buildBlock('field', item); - - // if field is non-field object: {b: 'c'} - } else { - return self.buildBlock('fields', {fields: item}); - } - - // if field is string: 'a' - } else if (_.isString(item)) { - return self.buildBlock('field', {field: item}); - } - }); - - // If fields is object: {a: 'u', b: {table: 't', alias: 'c'}} - } else { - // use keys as field names - result = _(fields).map(function(item, field) { - // b: {table: 't', alias: 'c'} - if (_.isObject(item)) { - if (!item.field) { - item = _.clone(item); - item.field = field; - } - - return self.buildBlock('field', item); - - // a: 'u' - } else if (_.isString(item)) { - return self.buildBlock('field', { - field: field, - alias: item - }); - } - - return ''; - }); - } - - result = result.join(', '); - } - } else { - result = fields; - } - - return result; - }); - - dialect.blocks.add('table', function(params) { - return this.dialect._wrapIdentifier(params.table); - }); - - dialect.blocks.add('expression', function(params) { - return params.expression; - }); - - dialect.blocks.add('name', function(params) { - return this.dialect._wrapIdentifier(params.name); - }); - - dialect.blocks.add('alias', function(params) { - var self = this; - - var result; - - if (_.isObject(params.alias)) { - if (!params.alias.name) { - throw new Error('Alias `name` property is required'); - } - - result = this.dialect._wrapIdentifier(params.alias.name); - - if (_.isArray(params.alias.columns)) { - result += '(' + _(params.alias.columns).map(function(column) { - return self.dialect._wrapIdentifier(column); - }).join(', ') + ')'; - } - } else { - result = this.dialect._wrapIdentifier(params.alias); - } - - return 'as ' + result; - }); - - dialect.blocks.add('condition', function(params) { - var condition = params.condition; - var result = ''; - - if (_.isObject(condition)) { - result = this.buildCondition({ - condition: condition, - operator: '$eq' - }); - } else if (_.isString(condition)) { - result = condition; - } - - if (result) { - result = 'where ' + removeTopBrackets(result); - } - - return result; - }); - - dialect.blocks.add('modifier', function(params) { - var self = this; - - var modifier = params.modifier; - var result = ''; - - // if modifier is object -> call method for each operator - if (_.isObject(modifier)) { - result = _(modifier).map(function(values, field) { - var modifierFn = self.dialect.modifiers.get(field); - var methodParams = values; - - if (!modifierFn) { - modifierFn = self.dialect.modifiers.get(self.dialect.config.defaultModifier); - methodParams = {}; - methodParams[field] = values; - } - - return modifierFn.call(self, methodParams); - }).join(', '); - - // if modifier is string -> not process it - } else if (_.isString(modifier)) { - result = modifier; - } - - if (result) { - result = 'set ' + result; - } - - return result; - }); - - dialect.blocks.add('join', function(params) { - var self = this; - - var join = params.join; - var result = ''; - - // if join is array -> make each joinItem - if (_.isArray(join)) { - result = _(join).map(function(joinItem) { - return self.buildTemplate('joinItem', joinItem); - }).join(' '); - - // if join is object -> set table name from key and make each joinItem - } else if (_.isObject(join)) { - result = _(join).map(function(joinItem, table) { - if (!joinItem.table && !joinItem.query && !joinItem.select) { - joinItem = _.clone(joinItem); - joinItem.table = table; - } - - return self.buildTemplate('joinItem', joinItem); - }).join(' '); - - // if join is string -> not process - } else if (_.isString(join)) { - result = join; - } - - return result; - }); - - dialect.blocks.add('type', function(params) { - return params.type.toLowerCase(); - }); - - - dialect.blocks.add('on', function(params) { - var on = params.on; - var result = ''; - - // `on` block is use `$field` as default compare operator - // because it most used case - if (_.isObject(on)) { - result = this.buildCondition({ - condition: on, - operator: '$field' - }); - } else if (_.isString(on)) { - result = on; - } - - if (result) { - result = 'on ' + removeTopBrackets(result); - } - - return result; - }); - - - dialect.blocks.add('group', function(params) { - var result = ''; - var group = params.group; - - if (_.isArray(group)) { - var self = this; - result = _(group).map(function(field) { - return self.dialect._wrapIdentifier(field); - }).join(', '); - } else if (_.isString(group)) { - result = this.dialect._wrapIdentifier(group); - } else if (_.isObject(group)) { - result = group.expression + (group.having ? " having " + this.buildCondition({ - condition: group.having - }) : ""); - } - - if (result) { - result = 'group by ' + result; - } - - return result; - }); - - - dialect.blocks.add('sort', function(params) { - var result = ''; - var sort = params.sort; - - // if sort is array -> field1, field2, ... - var self = this; - if (_.isArray(sort)) { - result = _(sort).map(function(sortField) { - return self.dialect._wrapIdentifier(sortField); - }).join(', '); - - // if sort is object -> field1 asc, field2 desc, ... - } else if (_.isObject(sort)) { - result = _(sort).map(function(direction, field) { - return self.dialect._wrapIdentifier(field) + ' ' + - (direction > 0 ? 'asc' : 'desc'); - }).join(', '); - - // if sort is string -> not process - } else if (_.isString(sort)) { - result = this.dialect._wrapIdentifier(sort); - } - - if (result) { - result = 'order by ' + result; - } - - return result; - }); - - - dialect.blocks.add('limit', function(params) { - return 'limit ' + this._pushValue(params.limit); - }); - - - dialect.blocks.add('offset', function(params) { - return 'offset ' + this._pushValue(params.offset); - }); - - - dialect.blocks.add('or', function(params) { - return 'or ' + params.or; - }); - - - dialect.blocks.add('values', function(params) { - var self = this; - - var fieldValues = params.values; - - if (!_.isArray(fieldValues)) { - fieldValues = [fieldValues]; - } - - var fields = params.fields || _(fieldValues).chain().map(function(values) { - return _(values).keys(); - }).flatten().uniq().value(); - - fieldValues = _(fieldValues).map(function(values) { - return _(fields).map(function(field) { - return self._pushValue(values[field]); - }); - }); - - return this.buildTemplate('insertValues', { - fields: fields, - fieldValues: fieldValues - }); - }); - - dialect.blocks.add('fieldValues', function(params) { - return _(params.fieldValues).map(function(values) { - return '(' + values.join(', ') + ')'; - }).join(', '); - }); - - dialect.blocks.add('queryBody', function(params) { - var query = params.queryBody || {}; - - return this.buildTemplate(query.type || 'select', query); - }); - - dialect.blocks.add('query', function(params) { - return this.buildTemplate('subQuery', {queryBody: params.query}); - }); - - dialect.blocks.add('select', function(params) { - return this.buildTemplate('subQuery', {queryBody: params.select}); - }); - - dialect.blocks.add('tableFields', function (params) { - return scheme.parse.call(this, params.tableFields, params.foreignKeys); - }); - - dialect.blocks.add('queries', function(params) { - var self = this; - - return _(params.queries).map(function(query) { - return self.buildTemplate('subQuery', {queryBody: query}); - }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); - }); - - dialect.blocks.add('indexOn', function (params) { - return params.indexOn; - }); - - dialect.blocks.add('with', function(params) { - var self = this; - - var withList = params['with']; - var result = ''; - - // if with clause is array -> make each withItem - if (_.isArray(withList)) { - result = _(withList).map(function(withItem) { - return self.buildTemplate('withItem', withItem); - }).join(', '); - - // if with clause is object -> set name from key and make each withItem - } else if (_.isObject(withList)) { - result = _(withList).map(function(withItem, name) { - if (!withItem.name) { - withItem = _.clone(withItem); - withItem.name = name; - } - return self.buildTemplate('withItem', withItem); - }).join(', '); - - // if with clause is string -> not process - } else if (_.isString(withList)) { - result = withList; - } - - return 'with ' + result; - }); - - dialect.blocks.add('returning', function(params) { - return 'returning ' + this.buildBlock('fields', {fields: params.returning}); - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/conditions.js b/legacy/json-sql/lib/dialects/base/conditions.js deleted file mode 100644 index c46be4f3..00000000 --- a/legacy/json-sql/lib/dialects/base/conditions.js +++ /dev/null @@ -1,122 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -// Compare conditions (e.g. $eq, $gt) -function buildCompareCondition(builder, field, operator, value) { - var placeholder; - - // if value is object, than make field block from it - if (value && _.isObject(value)) { - placeholder = builder.buildBlock('field', value); - } else { - // if value is simple - create placeholder for it - placeholder = builder._pushValue(value); - } - - field = builder.dialect._wrapIdentifier(field); - return [field, operator, placeholder].join(' '); -} - -// Contain conditions ($in/$nin) -function buildContainsCondition(builder, field, operator, value) { - var newValue; - - if (_.isArray(value)) { - if (!value.length) value = [null]; - - newValue = '(' + _(value).map(function(item) { - return builder._pushValue(item); - }).join(', ') + ')'; - } else if (_.isObject(value)) { - newValue = builder.buildTemplate('subQuery', {queryBody: value}); - } else { - throw new Error('Invalid `' + operator + '` value type "' + (typeof value) + '"'); - } - - field = builder.dialect._wrapIdentifier(field); - return [field, operator, newValue].join(' '); -} - -module.exports = function(dialect) { - dialect.conditions.add('$eq', function(field, operator, value) { - return buildCompareCondition(this, field, '=', value); - }); - - dialect.conditions.add('$ne', function(field, operator, value) { - return buildCompareCondition(this, field, '!=', value); - }); - - dialect.conditions.add('$gt', function(field, operator, value) { - return buildCompareCondition(this, field, '>', value); - }); - - dialect.conditions.add('$lt', function(field, operator, value) { - return buildCompareCondition(this, field, '<', value); - }); - - dialect.conditions.add('$gte', function(field, operator, value) { - return buildCompareCondition(this, field, '>=', value); - }); - - dialect.conditions.add('$lte', function(field, operator, value) { - return buildCompareCondition(this, field, '<=', value); - }); - - dialect.conditions.add('$is', function(field, operator, value) { - return buildCompareCondition(this, field, 'is', value); - }); - - dialect.conditions.add('$isnot', function(field, operator, value) { - return buildCompareCondition(this, field, 'is not', value); - }); - - dialect.conditions.add('$like', function(field, operator, value) { - return buildCompareCondition(this, field, 'like', value); - }); - - dialect.conditions.add('$null', function(field, operator, value) { - return buildCompareCondition(this, field, 'is' + (value ? '' : ' not'), null); - }); - - dialect.conditions.add('$field', function(field, operator, value) { - var placeholder; - - // if value is object, than make field block from it - if (_.isObject(value)) { - placeholder = this.buildBlock('field', value); - } else { - // $field - special operator, that not make placeholder for value - placeholder = this.dialect._wrapIdentifier(value); - } - - return [this.dialect._wrapIdentifier(field), '=', placeholder].join(' '); - }); - - - dialect.conditions.add('$in', function(field, operator, value) { - return buildContainsCondition(this, field, 'in', value); - }); - - dialect.conditions.add('$nin', function(field, operator, value) { - return buildContainsCondition(this, field, 'not in', value); - }); - - dialect.conditions.add('$between', function(field, operator, value) { - if (!_.isArray(value)) { - throw new Error('Invalid `$between` value type "' + (typeof value) + '"'); - } - - if (value.length < 2) { - throw new Error('`$between` array length should be 2 or greater'); - } - - return [ - this.dialect._wrapIdentifier(field), - 'between', - this._pushValue(value[0]), - 'and', - this._pushValue(value[1]) - ].join(' '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/index.js b/legacy/json-sql/lib/dialects/base/index.js deleted file mode 100644 index 2a0e2c7a..00000000 --- a/legacy/json-sql/lib/dialects/base/index.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var _ = require('underscore'); -var ValuesStore = require('../../valuesStore'); - -var templatesInit = require('./templates'); -var blocksInit = require('./blocks'); -var conditionsInit = require('./conditions'); -var logicalOperatorsInit = require('./logicalOperators'); -var modifiersInit = require('./modifiers'); - -var Dialect = module.exports = function(builder) { - this.builder = builder; - - this.blocks = new ValuesStore({context: builder}); - this.modifiers = new ValuesStore({context: builder}); - this.conditions = new ValuesStore({context: builder}); - this.logicalOperators = new ValuesStore({context: builder}); - this.templates = new ValuesStore({context: builder}); - - templatesInit(this); - blocksInit(this); - conditionsInit(this); - modifiersInit(this); - logicalOperatorsInit(this); - - this.identifierPartsRegexp = new RegExp( - '(\\' + this.config.identifierPrefix + '[^\\' + this.config.identifierSuffix + ']*\\' + - this.config.identifierSuffix + '|[^\\.]+)', 'g' - ); - this.wrappedIdentifierPartRegexp = new RegExp( - '^\\' + this.config.identifierPrefix + '.*\\' + this.config.identifierSuffix + '$' - ); -}; - -Dialect.prototype.config = { - identifierPrefix: '"', - identifierSuffix: '"', - defaultLogicalOperator: '$and', - defaultModifier: '$set' -}; - -Dialect.prototype._wrapIdentifier = function(name) { - if (this.builder.options.wrappedIdentifiers) { - var self = this; - var nameParts = name.match(this.identifierPartsRegexp); - - return _(nameParts).map(function(namePart) { - if (namePart !== '*' && !self.wrappedIdentifierPartRegexp.test(namePart)) { - namePart = self.config.identifierPrefix + namePart + self.config.identifierSuffix; - } - - return namePart; - }).join('.'); - } - - return name; -}; diff --git a/legacy/json-sql/lib/dialects/base/logicalOperators.js b/legacy/json-sql/lib/dialects/base/logicalOperators.js deleted file mode 100644 index fb2879c1..00000000 --- a/legacy/json-sql/lib/dialects/base/logicalOperators.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -function buildJoinOperator(conditions, operator) { - var isBracketsNeeded = conditions.length > 1; - var result = conditions.join(' ' + operator + ' '); - - if (result && isBracketsNeeded) result = '(' + result + ')'; - - return result; -} - -module.exports = function(dialect) { - dialect.logicalOperators.add('$and', function(conditions) { - return buildJoinOperator(conditions, 'and'); - }); - - dialect.logicalOperators.add('$or', function(conditions) { - return buildJoinOperator(conditions, 'or'); - }); - - dialect.logicalOperators.add('$not', function(conditions) { - var result = ''; - - if (_.isArray(conditions)) { - result = dialect.logicalOperators - .get(dialect.config.defaultLogicalOperator)(conditions); - } else if (_.isString(conditions)) { - result = conditions; - } - - if (result) result = 'not ' + result; - - return result; - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/modifiers.js b/legacy/json-sql/lib/dialects/base/modifiers.js deleted file mode 100644 index 3be42aa6..00000000 --- a/legacy/json-sql/lib/dialects/base/modifiers.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - dialect.modifiers.add('$set', function(values) { - var self = this; - - return _(values).map(function(value, field) { - var placeholder = self._pushValue(value); - - return [self.dialect._wrapIdentifier(field), '=', placeholder].join(' '); - }).join(', '); - }); - - dialect.modifiers.add('$inc', function(values) { - var self = this; - - return _(values).map(function(value, field) { - var placeholder = self._pushValue(value); - field = self.dialect._wrapIdentifier(field); - - return [field, '=', field, '+', placeholder].join(' '); - }).join(', '); - }); - - dialect.modifiers.add('$dec', function(values) { - var self = this; - - return _(values).map(function(value, field) { - var placeholder = self._pushValue(value); - field = self.dialect._wrapIdentifier(field); - - return [field, '=', field, '-', placeholder].join(' '); - }).join(', '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/base/scheme.js b/legacy/json-sql/lib/dialects/base/scheme.js deleted file mode 100644 index 686e16ed..00000000 --- a/legacy/json-sql/lib/dialects/base/scheme.js +++ /dev/null @@ -1,146 +0,0 @@ -var SchemeParser = function () { -} - -var types = { - "Number": {syntax: "int"}, - "BigInt": {syntax: "bigint"}, - "SmallInt": {syntax: "smallint"}, - "String": {syntax: "varchar", length: true}, - "Text": {syntax: "text"}, - "Real": {syntax: "real"}, - "Boolean": {syntax: "boolean"}, - "Blob": {syntax: "blob"}, - "Binary": {syntax: "bytea"} -} - -// for future -var onDeleteTrigger = { - "set_null": "SET NULL", - "cascade": "CASCADE" -} - -function getType(field) { - var s = ""; - var type = types[field.type]; - - if (!type) { - throw new Error("Invalid type of field: " + field.type); - } - - s += type.syntax; - - if (type.length) { - if (!field.length || field.length <= 0) { - throw new Error("Field length can't be less or equal 0"); - } - - s += "(" + field.length + ")"; - } else if (type.default_length) { - s += "(" + type.default_length + ")"; - } - - return s; -} - -function foreignkeys(fields, keys) { - if (!keys || keys.length == 0) { - return ""; - } - - var s = ", "; - var names = []; - fields.forEach(function (field) { - names.push(field.name) - }); - - keys.forEach(function (key, i) { - if (!key.field) { - throw new Error("Provide field for foreign key"); - } - - if (names.indexOf(key.field) < 0) { - throw new Error("Not exists field to make foreign key: " + key.field); - } - - if (!key.table || key.table.trim().length == 0) { - throw new Error("Invalid reference table name"); - } - - if (!key.table_field || key.table_field.trim().length == 0) { - throw new Error("Invalid reference table filed"); - } - - s += "FOREIGN KEY (\"" + key.field + "\") REFERENCES " + key.table + "(\"" + key.table_field + "\")" + (key.on_delete ? " ON DELETE " + key.on_delete : ""); - if (i != keys.length - 1) { - s += ","; - } - }); - - return s; -} - -function parse(fields, fkeys) { - var sql_fields = ""; - var checkPrimaryKey = false; - var names = []; - - fields.forEach(function (field, i) { - if (!field.name) { - throw new Error("Name of field most be provided"); - } - - if (field.name.trim().length == 0) { - throw new Error("Name most contains characters"); - } - - if (names.indexOf(field.name) >= 0) { - throw new Error("Two parameters with same name: " + field.name); - } - - var line = this.dialect._wrapIdentifier(field.name) + " " + getType(field); - - if (field.not_null) { - line += " NOT NULL"; - } - - if (field.default !== undefined) { - var _type = typeof field.default; - var _default = field.default; - if (_type === "string") { - _default = "'" + field.default + "'"; - } - line += " default " + _default; - } - - if (field.unique) { - line += " UNIQUE"; - } - - - if (field.primary_key) { - if (checkPrimaryKey) { - throw new Error("Too much primary key '" + field.name + "' in table"); - } else { - checkPrimaryKey = true; - } - - line += " PRIMARY KEY"; - } - - sql_fields += line; - - names.push(field.name); - - if (i != fields.length - 1) { - sql_fields += ","; - } - }.bind(this)); - - sql_fields += foreignkeys(fields, fkeys); - return sql_fields; -} - -module.exports = { - parse: parse, - foreignkeys: foreignkeys -} diff --git a/legacy/json-sql/lib/dialects/base/templates.js b/legacy/json-sql/lib/dialects/base/templates.js deleted file mode 100644 index 81c60cdb..00000000 --- a/legacy/json-sql/lib/dialects/base/templates.js +++ /dev/null @@ -1,220 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - var availableSourceProps = ['table', 'query', 'select', 'expression']; - var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; - var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; - - // private templates - - dialect.templates.add('query', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - - dialect.templates.add('subQuery', { - pattern: '({queryBody})', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('create', { - pattern: 'create table if not exists {table}({tableFields})', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'tableFields'); - hasArrayProp(type, params, 'tableFields'); - } - }); - - dialect.templates.add('index', { - pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'name') - hasRequiredProp(type, params, 'indexOn'); - hasMinPropLength(type, params, 'name', 1); - hasMinPropLength(type, params, 'indexOn', 1); - } - }) - - - dialect.templates.add('queriesCombination', { - pattern: '{with} {queries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queries'); - hasArrayProp(type, params, 'queries'); - hasMinPropLength(type, params, 'queries', 2); - } - }); - - - dialect.templates.add('insertValues', { - pattern: '({fields}) values {fieldValues}', - validate: function(type, params) { - hasRequiredProp('values', params, 'fields'); - hasArrayProp('values', params, 'fields'); - hasMinPropLength('values', params, 'fields', 1); - - hasRequiredProp('values', params, 'fieldValues'); - hasArrayProp('values', params, 'fieldValues'); - hasMinPropLength('values', params, 'fieldValues', 1); - } - }); - - - dialect.templates.add('joinItem', { - pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', - validate: function(type, params) { - hasOneOfProps('join', params, availableSourceProps); - - if (params.type) { - hasStringProp('join', params, 'type'); - - var splitType = _(params.type.toLowerCase().split(' ')).compact(); - if (_.difference(splitType, availableJoinTypes).length) { - throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); - } - } - } - }); - - - dialect.templates.add('withItem', { - pattern: '{name} {fields} as {query} {select} {expression}', - validate: function(type, params) { - hasRequiredProp('with', params, 'name'); - hasOneOfProps('with', params, ['query', 'select', 'expression']); - } - }); - - - dialect.templates.add('fromItem', { - pattern: '{table} {query} {select} {expression}', - validate: function(type, params) { - hasOneOfProps('from', params, availableSourceProps); - } - }); - - - // public templates - - dialect.templates.add('select', { - pattern: '{with} select {distinct} {fields} ' + - 'from {from} {table} {query} {select} {expression} {alias} ' + - '{join} {condition} {group} {sort} {limit} {offset}', - defaults: { - fields: {} - }, - validate: function(type, params) { - hasOneOfProps(type, params, availableSourceProps); - } - }); - - - dialect.templates.add('insert', { - pattern: '{with} insert {or} into {table} {values} {returning} {condition}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('update', { - pattern: '{with} update {or} {table} {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'modifier'); - hasRequiredProp(type, params, 'table'); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('remove', { - pattern: '{with} delete from {table} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - } - }); - - - dialect.templates.add('union', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('except', dialect.templates.get('queriesCombination')); - - - // validation helpers - - function hasRequiredProp(type, params, propName) { - if (!params[propName]) { - throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); - } - } - - function hasObjectProp(type, params, propName) { - if (!_.isObject(params[propName])) { - throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); - } - } - - function hasArrayProp(type, params, propName) { - if (!_.isArray(params[propName])) { - throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); - } - } - - function hasStringProp(type, params, propName) { - if (!_.isString(params.type)) { - throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); - } - } - - function hasMinPropLength(type, params, propName, length) { - if (params[propName].length < length) { - throw new Error('`' + propName + '` property should not have length less than ' + length + - ' in `' + type + '` clause'); - } - } - - function hasOneOfProps(type, params, expectedPropNames) { - var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); - - if (!propNames.length) { - throw new Error('Neither `' + expectedPropNames.join('`, `') + - '` properties are not set in `' + type + '` clause'); - } - - if (propNames.length > 1) { - throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + - type + '` clause'); - } - } - - function matchesRegExpProp(type, params, propName, regExp) { - if (!params[propName].match(regExp)) { - throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + - type + '` clause'); - } - } -}; diff --git a/legacy/json-sql/lib/dialects/mssql/index.js b/legacy/json-sql/lib/dialects/mssql/index.js deleted file mode 100644 index 8374184f..00000000 --- a/legacy/json-sql/lib/dialects/mssql/index.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -var BaseDialect = require('../base'); -var _ = require('underscore'); -var util = require('util'); - -var Dialect = module.exports = function(builder) { - BaseDialect.call(this, builder); -}; - -util.inherits(Dialect, BaseDialect); - -Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config, {}); diff --git a/legacy/json-sql/lib/dialects/postgresql/blocks.js b/legacy/json-sql/lib/dialects/postgresql/blocks.js deleted file mode 100644 index e6f0066d..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/blocks.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - dialect.blocks.add('offset', function(params) { - var limit = ''; - - if (typeof params.limit === 'undefined') { - limit = this.buildBlock('limit', {limit: -1}) + ' '; - } - - return limit + 'offset ' + this._pushValue(params.offset); - }); - - dialect.blocks.add('unionqueries', function(params) { - var self = this; - - return _(params.unionqueries).map(function(query) { - return self.buildTemplate('subUnionQuery', {queryBody: query}); - }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); - }); - - dialect.blocks.add('conflictFields', function(params) { - var self = this; - - var fields = _(params.conflictFields).map(function(field) { - return self.dialect._wrapIdentifier(field); - }); - - return '(' + fields.join(',') + ')'; - }); -}; diff --git a/legacy/json-sql/lib/dialects/postgresql/conditions.js b/legacy/json-sql/lib/dialects/postgresql/conditions.js deleted file mode 100644 index dddba22a..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/conditions.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -var buildJsonInCondition = function(builder, field, operator, value) { - field = builder.dialect._wrapIdentifier(field); - - var placeholder; - try { - placeholder = builder.buildBlock('field', value); - } catch (e) { - placeholder = builder._pushValue(JSON.stringify(value)); - } - - return [field, operator, placeholder].join(' '); -}; - -var buildJsonHasCondition = function(builder, field, operator, value) { - field = builder.dialect._wrapIdentifier(field); - - var placeholder; - if (_(value).isArray()) { - placeholder = 'array[' + _(value).map(function(item) { - return builder._pushValue(item); - }).join(', ') + ']'; - } else { - placeholder = builder.buildBlock('field', value); - } - - return [field, operator, placeholder].join(' '); -}; - -module.exports = function(dialect) { - dialect.conditions.add('$jsonContains', function(field, operator, value) { - return buildJsonInCondition(this, field, '@>', value); - }); - - dialect.conditions.add('$jsonIn', function(field, operator, value) { - return buildJsonInCondition(this, field, '<@', value); - }); - - dialect.conditions.add('$jsonHas', function(field, operator, value) { - field = this.dialect._wrapIdentifier(field); - - var placeholder = value; - if (_(placeholder).isObject()) { - placeholder = this.buildBlock('field', placeholder); - } else { - placeholder = this._pushValue(value.toString()); - } - - return [field, '?', placeholder].join(' '); - }); - - dialect.conditions.add('$jsonHasAny', function(field, operator, value) { - return buildJsonHasCondition(this, field, '?|', value); - }); - - dialect.conditions.add('$jsonHasAll', function(field, operator, value) { - return buildJsonHasCondition(this, field, '?&', value); - }); - - dialect.conditions.add('$upper', function(field, operator, value) { - return [ - 'upper(' + this.dialect._wrapIdentifier(field) + ')', - '=', - 'upper(' + this._pushValue(value[1]) + ')' - ].join(' '); - }); - - dialect.conditions.add('$lower', function(field, operator, value) { - return [ - 'lower(' + this.dialect._wrapIdentifier(field) + ')', - '=', - 'lower(' + this._pushValue(value[1]) + ')' - ].join(' '); - }); - - dialect.conditions.add('$decode', function (field, operator, value) { - return [ - this.dialect._wrapIdentifier(field), - '=', - 'decode(' + this._pushValue(value[1]) + ',', - this._pushValue(value[2]) + ')' - ].join(' '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/postgresql/index.js b/legacy/json-sql/lib/dialects/postgresql/index.js deleted file mode 100644 index 755dc00d..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/index.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -var BaseDialect = require('../base'); -var _ = require('underscore'); -var util = require('util'); -var blocksInit = require('./blocks'); -var conditionsInit = require('./conditions'); -var templatesInit = require('./templates'); - -var Dialect = module.exports = function (builder) { - BaseDialect.call(this, builder); - blocksInit(this) - conditionsInit(this); - templatesInit(this); -}; - -Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); - -util.inherits(Dialect, BaseDialect); - -Dialect.prototype.config = _({ - jsonSeparatorRegexp: /->>?/g -}).extend(BaseDialect.prototype.config); - -Dialect.prototype._wrapIdentifier = function(name) { - // split by json separator - var nameParts = name.split(this.config.jsonSeparatorRegexp); - var separators = name.match(this.config.jsonSeparatorRegexp); - - // wrap base identifier - var identifier = BaseDialect.prototype._wrapIdentifier.call(this, nameParts[0]); - - // wrap all json identifier and join them with separators - identifier += _(separators).reduce(function(memo, separator, index) { - return memo + separator + '\'' + nameParts[index + 1] + '\''; - }, ''); - - return identifier; -}; diff --git a/legacy/json-sql/lib/dialects/postgresql/templates.js b/legacy/json-sql/lib/dialects/postgresql/templates.js deleted file mode 100644 index 297ed8b0..00000000 --- a/legacy/json-sql/lib/dialects/postgresql/templates.js +++ /dev/null @@ -1,269 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - var availableSourceProps = ['table', 'query', 'select', 'expression']; - var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; - var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; - - // private templates - - dialect.templates.add('query', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subQuery', { - pattern: '({queryBody})', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subUnionQuery', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('create', { - pattern: 'create table if not exists {table}({tableFields})', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'tableFields'); - hasArrayProp(type, params, 'tableFields'); - } - }); - - dialect.templates.add('index', { - pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'name') - hasRequiredProp(type, params, 'indexOn'); - hasMinPropLength(type, params, 'name', 1); - hasMinPropLength(type, params, 'indexOn', 1); - } - }) - - - dialect.templates.add('queriesCombination', { - pattern: '{with} {queries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queries'); - hasArrayProp(type, params, 'queries'); - hasMinPropLength(type, params, 'queries', 2); - } - }); - - dialect.templates.add('queriesUnionCombination', { - pattern: '{with} {unionqueries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'unionqueries'); - hasArrayProp(type, params, 'unionqueries'); - hasMinPropLength(type, params, 'unionqueries', 2); - } - }); - - - dialect.templates.add('insertValues', { - pattern: '({fields}) values {fieldValues}', - validate: function(type, params) { - hasRequiredProp('values', params, 'fields'); - hasArrayProp('values', params, 'fields'); - hasMinPropLength('values', params, 'fields', 1); - - hasRequiredProp('values', params, 'fieldValues'); - hasArrayProp('values', params, 'fieldValues'); - hasMinPropLength('values', params, 'fieldValues', 1); - } - }); - - - dialect.templates.add('joinItem', { - pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', - validate: function(type, params) { - hasOneOfProps('join', params, availableSourceProps); - - if (params.type) { - hasStringProp('join', params, 'type'); - - var splitType = _(params.type.toLowerCase().split(' ')).compact(); - if (_.difference(splitType, availableJoinTypes).length) { - throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); - } - } - } - }); - - - dialect.templates.add('withItem', { - pattern: '{name} {fields} as {query} {select} {expression}', - validate: function(type, params) { - hasRequiredProp('with', params, 'name'); - hasOneOfProps('with', params, ['query', 'select', 'expression']); - } - }); - - - dialect.templates.add('fromItem', { - pattern: '{table} {query} {select} {expression}', - validate: function(type, params) { - hasOneOfProps('from', params, availableSourceProps); - } - }); - - - // public templates - - dialect.templates.add('select', { - pattern: '{with} select {distinct} {fields} ' + - 'from {from} {table} {query} {select} {expression} {alias} ' + - '{join} {condition} {group} {sort} {limit} {offset}', - defaults: { - fields: {} - }, - validate: function(type, params) { - hasOneOfProps(type, params, availableSourceProps); - } - }); - - - dialect.templates.add('insert', { - pattern: '{with} insert {or} into {table} {values} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('insertornothing', { - pattern: '{with} insert {or} into {table} {values} on conflict do nothing {returning} {condition}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('insertorupdate', { - pattern: '{with} insert {or} into {table} {values} on conflict {conflictFields} do update {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasRequiredProp('conflictFields', params, 'conflictFields'); - hasArrayProp('conflictFields', params, 'conflictFields'); - hasMinPropLength('conflictFields', params, 'conflictFields', 1); - hasRequiredProp(type, params, 'modifier'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('update', { - pattern: '{with} update {or} {table} {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'modifier'); - hasRequiredProp(type, params, 'table'); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('remove', { - pattern: '{with} delete from {table} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - } - }); - - - dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); - - - dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('except', dialect.templates.get('queriesCombination')); - - - // validation helpers - - function hasRequiredProp(type, params, propName) { - if (!params[propName]) { - throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); - } - } - - function hasObjectProp(type, params, propName) { - if (!_.isObject(params[propName])) { - throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); - } - } - - function hasArrayProp(type, params, propName) { - if (!_.isArray(params[propName])) { - throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); - } - } - - function hasStringProp(type, params, propName) { - if (!_.isString(params.type)) { - throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); - } - } - - function hasMinPropLength(type, params, propName, length) { - if (params[propName].length < length) { - throw new Error('`' + propName + '` property should not have length less than ' + length + - ' in `' + type + '` clause'); - } - } - - function hasOneOfProps(type, params, expectedPropNames) { - var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); - - if (!propNames.length) { - throw new Error('Neither `' + expectedPropNames.join('`, `') + - '` properties are not set in `' + type + '` clause'); - } - - if (propNames.length > 1) { - throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + - type + '` clause'); - } - } - - function matchesRegExpProp(type, params, propName, regExp) { - if (!params[propName].match(regExp)) { - throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + - type + '` clause'); - } - } -}; diff --git a/legacy/json-sql/lib/dialects/sqlite/blocks.js b/legacy/json-sql/lib/dialects/sqlite/blocks.js deleted file mode 100644 index c3531c2a..00000000 --- a/legacy/json-sql/lib/dialects/sqlite/blocks.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - dialect.blocks.add('offset', function(params) { - var limit = ''; - - if (typeof params.limit === 'undefined') { - limit = this.buildBlock('limit', {limit: -1}) + ' '; - } - - return limit + 'offset ' + this._pushValue(params.offset); - }); - - dialect.blocks.add('unionqueries', function(params) { - var self = this; - - return _(params.unionqueries).map(function(query) { - return self.buildTemplate('subUnionQuery', {queryBody: query}); - }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); - }); -}; diff --git a/legacy/json-sql/lib/dialects/sqlite/index.js b/legacy/json-sql/lib/dialects/sqlite/index.js deleted file mode 100644 index 72549400..00000000 --- a/legacy/json-sql/lib/dialects/sqlite/index.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var BaseDialect = require('../base'); -var _ = require('underscore'); -var util = require('util'); -var blocksInit = require('./blocks'); -var templatesInit = require('./templates'); - -var Dialect = module.exports = function (builder) { - BaseDialect.call(this, builder); - blocksInit(this); - templatesInit(this); -}; - -util.inherits(Dialect, BaseDialect); - -Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); diff --git a/legacy/json-sql/lib/dialects/sqlite/templates.js b/legacy/json-sql/lib/dialects/sqlite/templates.js deleted file mode 100644 index ac6e382f..00000000 --- a/legacy/json-sql/lib/dialects/sqlite/templates.js +++ /dev/null @@ -1,236 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = function(dialect) { - var availableSourceProps = ['table', 'query', 'select', 'expression']; - var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; - var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; - - // private templates - - dialect.templates.add('query', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subQuery', { - pattern: '({queryBody})', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('subUnionQuery', { - pattern: '{queryBody}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queryBody'); - hasObjectProp(type, params, 'queryBody'); - } - }); - - dialect.templates.add('create', { - pattern: 'create table if not exists {table}({tableFields})', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'tableFields'); - hasArrayProp(type, params, 'tableFields'); - } - }); - - dialect.templates.add('index', { - pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', - validate: function (type, params) { - hasRequiredProp(type, params, 'table'); - hasRequiredProp(type, params, 'name') - hasRequiredProp(type, params, 'indexOn'); - hasMinPropLength(type, params, 'name', 1); - hasMinPropLength(type, params, 'indexOn', 1); - } - }) - - - dialect.templates.add('queriesCombination', { - pattern: '{with} {queries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'queries'); - hasArrayProp(type, params, 'queries'); - hasMinPropLength(type, params, 'queries', 2); - } - }); - - dialect.templates.add('queriesUnionCombination', { - pattern: '{with} {unionqueries} {sort} {limit} {offset}', - validate: function(type, params) { - hasRequiredProp(type, params, 'unionqueries'); - hasArrayProp(type, params, 'unionqueries'); - hasMinPropLength(type, params, 'unionqueries', 2); - } - }); - - - dialect.templates.add('insertValues', { - pattern: '({fields}) values {fieldValues}', - validate: function(type, params) { - hasRequiredProp('values', params, 'fields'); - hasArrayProp('values', params, 'fields'); - hasMinPropLength('values', params, 'fields', 1); - - hasRequiredProp('values', params, 'fieldValues'); - hasArrayProp('values', params, 'fieldValues'); - hasMinPropLength('values', params, 'fieldValues', 1); - } - }); - - - dialect.templates.add('joinItem', { - pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', - validate: function(type, params) { - hasOneOfProps('join', params, availableSourceProps); - - if (params.type) { - hasStringProp('join', params, 'type'); - - var splitType = _(params.type.toLowerCase().split(' ')).compact(); - if (_.difference(splitType, availableJoinTypes).length) { - throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); - } - } - } - }); - - - dialect.templates.add('withItem', { - pattern: '{name} {fields} as {query} {select} {expression}', - validate: function(type, params) { - hasRequiredProp('with', params, 'name'); - hasOneOfProps('with', params, ['query', 'select', 'expression']); - } - }); - - - dialect.templates.add('fromItem', { - pattern: '{table} {query} {select} {expression}', - validate: function(type, params) { - hasOneOfProps('from', params, availableSourceProps); - } - }); - - - // public templates - - dialect.templates.add('select', { - pattern: '{with} select {distinct} {fields} ' + - 'from {from} {table} {query} {select} {expression} {alias} ' + - '{join} {condition} {group} {sort} {limit} {offset}', - defaults: { - fields: {} - }, - validate: function(type, params) { - hasOneOfProps(type, params, availableSourceProps); - } - }); - - - dialect.templates.add('insert', { - pattern: '{with} insert {or} into {table} {values} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'values'); - hasObjectProp(type, params, 'values'); - hasOneOfProps(type, params, availableSourceProps); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('update', { - pattern: '{with} update {or} {table} {modifier} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'modifier'); - hasRequiredProp(type, params, 'table'); - if (params.or) { - hasStringProp(type, params, 'or'); - matchesRegExpProp(type, params, 'or', orRegExp); - } - } - }); - - - dialect.templates.add('remove', { - pattern: '{with} delete from {table} {condition} {returning}', - validate: function(type, params) { - hasRequiredProp(type, params, 'table'); - } - }); - - - dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); - - - dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); - - - dialect.templates.add('except', dialect.templates.get('queriesCombination')); - - - // validation helpers - - function hasRequiredProp(type, params, propName) { - if (!params[propName]) { - throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); - } - } - - function hasObjectProp(type, params, propName) { - if (!_.isObject(params[propName])) { - throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); - } - } - - function hasArrayProp(type, params, propName) { - if (!_.isArray(params[propName])) { - throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); - } - } - - function hasStringProp(type, params, propName) { - if (!_.isString(params.type)) { - throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); - } - } - - function hasMinPropLength(type, params, propName, length) { - if (params[propName].length < length) { - throw new Error('`' + propName + '` property should not have length less than ' + length + - ' in `' + type + '` clause'); - } - } - - function hasOneOfProps(type, params, expectedPropNames) { - var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); - - if (!propNames.length) { - throw new Error('Neither `' + expectedPropNames.join('`, `') + - '` properties are not set in `' + type + '` clause'); - } - - if (propNames.length > 1) { - throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + - type + '` clause'); - } - } - - function matchesRegExpProp(type, params, propName, regExp) { - if (!params[propName].match(regExp)) { - throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + - type + '` clause'); - } - } -}; diff --git a/legacy/json-sql/lib/index.js b/legacy/json-sql/lib/index.js deleted file mode 100644 index 55bbe02a..00000000 --- a/legacy/json-sql/lib/index.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -var Builder = require('./builder'); - -module.exports = function(params) { - return new Builder(params); -}; -module.exports.Builder = Builder; diff --git a/legacy/json-sql/lib/valuesStore.js b/legacy/json-sql/lib/valuesStore.js deleted file mode 100644 index 29b7d395..00000000 --- a/legacy/json-sql/lib/valuesStore.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var _ = require('underscore'); - -module.exports = ValuesStore; - -function ValuesStore(options) { - options = options || {}; - this.context = options.context || null; - this._values = options.values || {}; -} - -ValuesStore.prototype.add = ValuesStore.prototype.set = function(name, value) { - if (_.isFunction(value) && this.context) { - value = _(value).bind(this.context); - } - - this._values[name] = value; -}; - -ValuesStore.prototype.get = function(name) { - return this._values[name] || null; -}; - -ValuesStore.prototype.remove = function(name) { - delete this._values[name]; -}; - -ValuesStore.prototype.has = function(name) { - return this._values.hasOwnProperty(name); -}; diff --git a/legacy/json-sql/package.json b/legacy/json-sql/package.json deleted file mode 100644 index 73c407e3..00000000 --- a/legacy/json-sql/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "json-sql", - "description": "Node.js json to sql query mapper", - "version": "0.2.6", - "author": { - "name": "Artem Zhukov", - "email": "artzhuchka@gmail.com" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/2do2go/json-sql.git" - }, - "keywords": [ - "json-sql", - "json", - "sql", - "odm", - "mapper", - "db", - "database" - ], - "dependencies": { - "underscore": "=1.8.3" - }, - "devDependencies": { - "jshint": "=2.9.2", - "chai": "=3.5.0", - "gulp": "=3.9.1", - "gulp-jshint": "=2.0.0", - "gulp-mocha": "=2.2.0" - }, - "main": "./lib/index", - "readme": "# JSON-SQL\n\nLibrary for mapping mongo-style query objects to SQL queries.\n\n## Quick Start\n\nInstall it with NPM or add it to your package.json:\n\n``` bash\n$ npm install json-sql\n```\n\nThen:\n\n``` js\nvar jsonSql = require('json-sql')();\n\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tfields: ['name', 'age'],\n\tcondition: {name: 'Max', id: 6}\n});\n\nsql.query\n// sql string:\n// select name, age from users where name = $p1 && id = 6;\n\nsql.values\n// hash of values:\n// { p1: 'Max' }\n```\n\n## Documentation\n\nDocumentation is available at the [./docs directory](./docs).\n\n## Examples\n\n__Select with join:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tjoin: {\n\t\tdocuments: {\n\t\t\ton: {'user.id': 'documents.userId'}\n\t\t}\n\t}\n});\n\nsql.query\n// select * from users join documents on user.id = documents.userId;\n\nsql.values\n// {}\n```\n\n__Insert:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'insert',\n\ttable: 'users',\n\tvalues: {\n\t\tname: 'John',\n\t\tlastname: 'Snow',\n\t\tage: 24,\n\t\tgender: 'male'\n\t}\n});\n\nsql.query\n// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3);\n\nsql.values\n// { p1: 'John', p2: 'Snow', p3: 'male' }\n```\n\n__Update:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'update',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t},\n\tmodifier: {\n\t\trole: 'admin'\n\t\tage: 33\n\t}\n});\n\nsql.query\n// update users set role = $p1, age = 33 where id = 5;\n\nsql.values\n// { p1: 'admin' }\n```\n\n__Remove:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'remove',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t}\n});\n\nsql.query\n// delete from users where id = 5;\n\nsql.values\n// {}\n```\n\nFor more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests).\n\n## Tests\n\nClone repository from github, `cd` into cloned dir and install dev dependencies:\n\n``` bash\n$ npm install\n```\n\nThen run tests with command:\n\n``` bash\n$ gulp test\n```\n\nOr run tests coverage with command:\n\n``` bash\n$ gulp coverage\n```\n\n## License\n\n[MIT](./LICENSE)\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/2do2go/json-sql/issues" - }, - "homepage": "https://github.com/2do2go/json-sql#readme", - "_id": "json-sql@0.2.4", - "_shasum": "df8c5f345b72f421c6fc7da57116c47cae5b5216", - "_resolved": "https://github.com/LiskHQ/json-sql/tarball/master", - "_from": "https://github.com/LiskHQ/json-sql/tarball/master" -} diff --git a/legacy/json-sql/tests/0_base.js b/legacy/json-sql/tests/0_base.js deleted file mode 100644 index b46774be..00000000 --- a/legacy/json-sql/tests/0_base.js +++ /dev/null @@ -1,304 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var Builder = require('../lib').Builder; -var expect = require('chai').expect; - -describe('Builder', function() { - it('should have fields', function() { - expect(jsonSql).to.be.ok; - expect(jsonSql).to.be.an.instanceof(Builder); - - expect(jsonSql.dialect).to.be.ok; - - expect(jsonSql._query).to.be.equal(''); - expect(jsonSql._values).to.be.eql({}); - - expect(jsonSql.dialect.blocks).to.be.ok; - expect(jsonSql.dialect.templates).to.be.ok; - expect(jsonSql.dialect.conditions).to.be.ok; - expect(jsonSql.dialect.modifiers).to.be.ok; - expect(jsonSql.dialect.logicalOperators).to.be.ok; - }); - - it('should throw error with wrong `type` property', function() { - expect(function() { - jsonSql.build({ - type: 'wrong' - }); - }).to.throw('Unknown template type "wrong"'); - }); - - it('should throw error without `table`, `query` and `select` properties', function() { - expect(function() { - jsonSql.build({}); - }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + - 'are not set in `select` clause'); - }); - - it('should throw error with both `table` and `select` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - select: {table: 'payments'} - }); - }).to.throw('Wrong using `table`, `select` properties together in `select` clause'); - }); - - it('should throw error with both `table` and `query` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - query: {table: 'payments'} - }); - }).to.throw('Wrong using `table`, `query` properties together in `select` clause'); - }); - - it('should throw error with both `query` and `select` properties', function() { - expect(function() { - jsonSql.build({ - query: {table: 'payments'}, - select: {table: 'payments'} - }); - }).to.throw('Wrong using `query`, `select` properties together in `select` clause'); - }); - - it('should throw error without `name` property in `with` clause', function() { - expect(function() { - jsonSql.build({ - 'with': [{ - select: { - table: 'payments' - } - }], - table: 'users' - }); - }).to.throw('`name` property is not set in `with` clause'); - }); - - it('should throw error without `query` and `select` properties in `with` clause', function() { - expect(function() { - jsonSql.build({ - 'with': [{ - name: 'payments' - }], - table: 'users' - }); - }).to.throw('Neither `query`, `select`, `expression` properties are not set in `with` clause'); - }); - - it('should throw error with both `query` and `select` properties in `with` clause', function() { - expect(function() { - jsonSql.build({ - 'with': [{ - name: 'payments', - query: {table: 'table1'}, - select: {table: 'table2'} - }], - table: 'users' - }); - }).to.throw('Wrong using `query`, `select` properties together in `with` clause'); - }); - - it('should be ok with array in `with` clause', function() { - var result = jsonSql.build({ - 'with': [{ - name: 'payments', - select: { - table: 'payments' - } - }], - table: 'users' - }); - - expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + - '"users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object in `with` clause', function() { - var result = jsonSql.build({ - 'with': { - payments: { - select: { - table: 'payments' - } - } - }, - table: 'users' - }); - - expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + - '"users";'); - expect(result.values).to.be.eql({}); - }); - - it('should create array values with option `namedValues` = false', function() { - jsonSql.configure({ - namedValues: false - }); - - expect(jsonSql._values).to.be.eql([]); - - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); - expect(result.values).to.be.eql(['John']); - }); - - it('should use prefix `@` for values with option `valuesPrefix` = @', function() { - jsonSql.configure({ - valuesPrefix: '@' - }); - - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); - expect(result.values).to.be.eql({p1: 'John'}); - }); - - it('should return prefixed values with method `prefixValues`', function() { - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); - expect(result.values).to.be.eql({p1: 'John'}); - expect(result.prefixValues()).to.be.eql({'@{p1}': 'John'}); - }); - - it('should return array values with method `getValuesArray`', function() { - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); - expect(result.values).to.be.eql({p1: 'John'}); - expect(result.getValuesArray()).to.be.eql(['John']); - }); - - it('should return object values with method `getValuesObject`', function() { - jsonSql.configure({ - valuesPrefix: '$', - namedValues: false - }); - - expect(jsonSql._values).to.be.eql([]); - - var result = jsonSql.build({ - table: 'users', - condition: {name: 'John'} - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); - expect(result.values).to.be.eql(['John']); - expect(result.prefixValues()).to.be.eql({'${1}': 'John'}); - expect(result.getValuesObject()).to.be.eql({1: 'John'}); - }); - - it('should create query without values with option `separatedValues` = false', function() { - jsonSql.configure({ - separatedValues: false - }); - - expect(jsonSql._values).to.not.be.ok; - expect(jsonSql._placeholderId).to.not.be.ok; - - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: {name: 'John', surname: 'Doe'} - }); - - expect(result.query).to.be.equal('insert into "users" ("name", "surname") values ' + - '(\'John\', \'Doe\');'); - expect(result.values).to.not.be.ok; - }); - - it('should create query without wrapping identifiers with option `wrappedIdentifiers` = false', - function() { - jsonSql.configure({ - wrappedIdentifiers: false - }); - - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: {name: 'John'} - }); - - expect(result.query).to.be.equal('insert into users (name) values (${p1});'); - } - ); - - it('shouldn\'t wrap identifiers that already wrapped', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - type: 'insert', - table: '"users"', - values: { - '"name"': 'John', - '"users"."age"': 22 - } - }); - - expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); - }); - - it('shouldn\'t split identifiers by dots inside quotes', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - type: 'insert', - table: '"users"', - values: { - '"users.age"': 22 - } - }); - - expect(result.query).to.be.equal('insert into "users" ("users.age") values (22);'); - }); - - it('shouldn\'t wrap asterisk identifier parts', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - fields: ['users.*'], - table: '"users"' - }); - - expect(result.query).to.be.equal('select "users".* from "users";'); - }); - - it('should split identifiers by dots and wrap each part', function() { - jsonSql.configure({ - wrappedIdentifiers: true - }); - - var result = jsonSql.build({ - type: 'insert', - table: '"users"', - values: { - 'name': 'John', - 'users.age': 22 - } - }); - - expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); - }); -}); diff --git a/legacy/json-sql/tests/1_select.js b/legacy/json-sql/tests/1_select.js deleted file mode 100644 index 087a1afb..00000000 --- a/legacy/json-sql/tests/1_select.js +++ /dev/null @@ -1,1222 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Select', function() { - describe('type', function() { - it('should be ok without `type` property', function() { - var result = jsonSql.build({ - table: 'users' - }); - - expect(result.query).to.be.equal('select * from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with "select" value', function() { - var result = jsonSql.build({ - type: 'select', - table: 'users' - }); - - expect(result.query).to.be.equal('select * from "users";'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('distinct', function() { - it('should be ok with true value', function() { - var result = jsonSql.build({ - table: 'users', - distinct: true - }); - - expect(result.query).to.be.equal('select distinct * from "users";'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('fields', function() { - it('should be ok with string array', function() { - var result = jsonSql.build({ - table: 'users', - fields: ['name', 'type'] - }); - - expect(result.query).to.be.equal('select "name", "type" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`name`: `alias`, ...)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {userAge: 'age', userScore: 'score'} - }); - - expect(result.query).to.be.equal('select "userAge" as "age", "userScore" as "score" from ' + - '"users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array of objects(`name`: `alias`, ...)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{userAge: 'age'}] - }); - - expect(result.query).to.be.equal('select "userAge" as "age" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'address'}] - }); - - expect(result.query).to.be.equal('select "address" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `table`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'score', table: 'users'}] - }); - - expect(result.query).to.be.equal('select "users"."score" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `alias`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'zoneName', alias: 'zone'}] - }); - - expect(result.query).to.be.equal('select "zoneName" as "zone" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `table`, `alias`) array', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{field: 'zoneName', table: 'users', alias: 'zone'}] - }); - - expect(result.query).to.be.equal('select "users"."zoneName" as "zone" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`table`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {score: {table: 'users'}} - }); - - expect(result.query).to.be.equal('select "users"."score" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {zone: {field: 'zone_1', alias: 'zone'}} - }); - - expect(result.query).to.be.equal('select "zone_1" as "zone" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`table`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {score: {table: 'users', alias: 's'}} - }); - - expect(result.query).to.be.equal('select "users"."score" as "s" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`field`, `table`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: {name: {field: 'name_1', table: 'users', alias: 'name_2'}} - }); - - expect(result.query).to.be.equal('select "users"."name_1" as "name_2" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: 'count(*)' - }] - }); - - expect(result.query).to.be.equal('select count(*) from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: 'count(*)', - alias: 'count' - }] - }); - - expect(result.query).to.be.equal('select count(*) as "count" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`, `field`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: 'sum', - field: 'income', - alias: 'sum' - }] - }); - - expect(result.query).to.be.equal('select sum("income") as "sum" from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object(`expression`[], `field`, `alias`)', function() { - var result = jsonSql.build({ - table: 'users', - fields: [{ - expression: ['abs', 'sum'], - field: 'income', - alias: 'sum' - }] - }); - - expect(result.query).to.be.equal('select abs(sum("income")) as "sum" from "users";'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('alias', function() { - it('should be ok with string `alias` property', function() { - var result = jsonSql.build({ - table: 'users', - alias: 'u' - }); - - expect(result.query).to.be.equal('select * from "users" as "u";'); - expect(result.values).to.be.eql({}); - }); - - it('should throw error if object `alias` does not have `name` property', function() { - expect(function() { - jsonSql.build({ - table: 'users', - alias: {} - }); - }).to.throw('Alias `name` property is required'); - }); - - it('should be ok with object `alias`(`name`) property', function() { - var result = jsonSql.build({ - table: 'users', - alias: { - name: 'u' - } - }); - - expect(result.query).to.be.equal('select * from "users" as "u";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object `alias`(`name`, `columns`) property', function() { - var result = jsonSql.build({ - table: 'users', - alias: { - name: 'u', - columns: ['a', 'b'] - } - }); - - expect(result.query).to.be.equal('select * from "users" as "u"("a", "b");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('query', function() { - it('should be ok with `query` property', function() { - var result = jsonSql.build({ - query: { - type: 'select', - table: 't' - } - }); - - expect(result.query).to.be.equal('select * from (select * from "t");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('select', function() { - it('should be ok with `select` property', function() { - var result = jsonSql.build({ - select: { - table: 't' - } - }); - - expect(result.query).to.be.equal('select * from (select * from "t");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('expression', function() { - it('should be ok with `expression` property', function() { - var result = jsonSql.build({ - expression: 'function()' - }); - - expect(result.query).to.be.equal('select * from function();'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('join', function() { - it('should throw error without `table`, `query` and `select` properties', - function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{}] - }); - }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + - 'are not set in `join` clause'); - } - ); - - it('should throw error with both `table` and `select` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - table: 'a', - select: {table: 'b'} - }] - }); - }).to.throw('Wrong using `table`, `select` properties together in `join` clause'); - }); - - it('should throw error with both `table` and `query` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - table: 'a', - query: {table: 'b'} - }] - }); - }).to.throw('Wrong using `table`, `query` properties together in `join` clause'); - }); - - it('should throw error with both `query` and `select` properties', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - query: 'a', - select: {table: 'b'} - }] - }); - }).to.throw('Wrong using `query`, `select` properties together in `join` clause'); - }); - - it('should throw error with wrong `type` property', function() { - expect(function() { - jsonSql.build({ - table: 'users', - join: [{ - type: 'wrong', - table: 'payments' - }] - }); - }).to.throw('Invalid `type` property value "wrong" in `join` clause'); - }); - - it('should be ok with correct `type` property', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - type: 'left outer', - table: 'payments' - }] - }); - - expect(result.query).to.be.equal('select * from "users" left outer join "payments";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array `join`', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - table: 'payments' - }] - }); - - expect(result.query).to.be.equal('select * from "users" join "payments";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object `join`', function() { - var result = jsonSql.build({ - table: 'users', - join: { - payments: {} - } - }); - - expect(result.query).to.be.equal('select * from "users" join "payments";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `on` property', function() { - var result = jsonSql.build({ - table: 'users', - join: { - payments: { - on: {'users.name': 'payments.name'} - } - } - }); - - expect(result.query).to.be.equal('select * from "users" join "payments" on "users"."name" = ' + - '"payments"."name";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `query` property', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - query: { - table: 'payments' - }, - on: {'users.name': 'payments.name'} - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'join (select * from "payments") ' + - 'on "users"."name" = "payments"."name";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `select` property', function() { - var result = jsonSql.build({ - table: 'users', - join: [{ - select: { - table: 'payments' - }, - on: {'users.name': 'payments.name'} - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'join (select * from "payments") ' + - 'on "users"."name" = "payments"."name";' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('condition', function() { - describe('compare operators', function() { - it('should throw error with wrong operator', function() { - expect(function() { - jsonSql.build({ - table: 'users', - condition: { - name: {$wrong: 'John'} - } - }); - }).to.throw('Unknown operator "$wrong"'); - }); - - it('should be ok with default operator(=)', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: 'John' - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$eq` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$eq: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$ne` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$ne: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" != ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$gt` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$gt: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" > ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$lt` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$lt: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" < ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$gte` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$gte: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" >= ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$lte` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$lte: 'John'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" <= ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with `$is` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$is: null} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$isnot` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$isnot: null} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$like` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$like: 'John%'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" like ${p1};'); - expect(result.values).to.be.eql({ - p1: 'John%' - }); - }); - - it('should be ok with `$null`:true operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$null: true} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$null`:false operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$null: false} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$field` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$field: 'name_2'} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object `$field` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: {$field: {field: 'name_2'}} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: [12, 13, 14]} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (12, 13, 14);'); - expect(result.values).to.be.eql({}); - }); - - it('should add `null` value with empty array in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: []} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (null);'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$nin` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$nin: [12, 13, 14]} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" not in (12, 13, 14);'); - expect(result.values).to.be.eql({}); - }); - - it('should add `null` value with empty array in `$nin` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$nin: []} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" not in (null);'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object subquery in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: { - table: 'test' - }} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + - '"test");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `query` subquery in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: { - query: { - table: 'test' - } - }} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + - '(select * from "test"));'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `select` subquery in `$in` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$in: { - select: { - table: 'test' - } - }} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + - '(select * from "test"));'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$between` operator', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: {$between: [12, 14]} - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" between 12 and 14;'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('logical operators', function() { - it('should throw error with wrong logical operator', function() { - expect(function() { - jsonSql.build({ - table: 'users', - condition: { - $wrong: [ - {name: 'John'}, - {age: 12} - ] - } - }); - }).to.throw('Unknown logical operator "$wrong"'); - }); - - it('should be ok with default logical operator(`$and`)', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - name: 'John', - age: 12 - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with default logical operator(`$and`) for one field', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - age: { - $gt: 5, - $lt: 15, - $ne: 10 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where "age" > 5 and "age" < 15 and ' + - '"age" != 10;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array `$and`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [ - {name: 'John'}, - {age: 12} - ] - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object `$and`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: { - name: 'John', - age: 12 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with array `$or`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [ - {name: 'John'}, - {age: 12} - ] - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object `$or`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: { - name: 'John', - age: 12 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with array `$not`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $not: [ - {name: 'John'}, - {age: 12} - ] - } - }); - - expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + - '"age" = 12);'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object `$not`', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $not: { - name: 'John', - age: 12 - } - } - }); - - expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + - '"age" = 12);'); - expect(result.values).to.be.eql({ - p1: 'John' - }); - }); - - it('should be ok with object [`$or`, `$or`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: [{ - $or: { - name: 'John', - age: 12 - } - }, { - $or: { - name: 'Mark', - age: 14 - } - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} or "age" = 12) and ' + - '("name" = ${p2} or "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with object `$and`:[`$or`, `$or`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [{ - $or: { - name: 'John', - age: 12 - } - }, { - $or: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} or "age" = 12) and ' + - '("name" = ${p2} or "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with object `$or`:[{},{}]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [{ - name: 'John', - age: 12 - }, { - name: 'Mark', - age: 14 - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) or ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with object `$or`:[`$and`, `$and`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [{ - $and: { - name: 'John', - age: 12 - } - }, { - $and: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) or ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with [{}, {}]', function() { - var result = jsonSql.build({ - table: 'users', - condition: [{ - name: 'John', - age: 12 - }, { - name: 'Mark', - age: 14 - }] - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) and ' + - '("name" = ${p2} and "age" = 14);'); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with `$and`:[{}, {}]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [{ - name: 'John', - age: 12 - }, { - name: 'Mark', - age: 14 - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) and ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with `$and`:[`$and`, `$and`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $and: [{ - $and: { - name: 'John', - age: 12 - } - }, { - $and: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} and "age" = 12) and ' + - '("name" = ${p2} and "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - - it('should be ok with `$or`:[`$or`, `$or`]', function() { - var result = jsonSql.build({ - table: 'users', - condition: { - $or: [{ - $or: { - name: 'John', - age: 12 - } - }, { - $or: { - name: 'Mark', - age: 14 - } - }] - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" ' + - 'where ("name" = ${p1} or "age" = 12) or ' + - '("name" = ${p2} or "age" = 14);' - ); - expect(result.values).to.be.eql({ - p1: 'John', - p2: 'Mark' - }); - }); - }); - }); - - describe('group', function() { - it('should be ok with string value', function() { - var result = jsonSql.build({ - table: 'users', - group: 'age' - }); - - expect(result.query).to.be.equal( - 'select * from "users" group by "age";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array value', function() { - var result = jsonSql.build({ - table: 'users', - group: ['age', 'gender'] - }); - - expect(result.query).to.be.equal( - 'select * from "users" group by "age", "gender";' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('sort', function() { - it('should be ok with string value', function() { - var result = jsonSql.build({ - table: 'users', - sort: 'age' - }); - - expect(result.query).to.be.equal( - 'select * from "users" order by "age";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array value', function() { - var result = jsonSql.build({ - table: 'users', - sort: ['age', 'gender'] - }); - - expect(result.query).to.be.equal( - 'select * from "users" order by "age", "gender";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object value', function() { - var result = jsonSql.build({ - table: 'users', - sort: { - age: 1, - gender: -1 - } - }); - - expect(result.query).to.be.equal( - 'select * from "users" order by "age" asc, "gender" desc;' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('limit, offset', function() { - it('should be ok with `limit` property', function() { - var result = jsonSql.build({ - table: 'users', - limit: 5 - }); - - expect(result.query).to.be.equal( - 'select * from "users" limit 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `offset` property', function() { - var result = jsonSql.build({ - table: 'users', - offset: 5 - }); - - expect(result.query).to.be.equal( - 'select * from "users" offset 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `limit` and `offset` properties', function() { - var result = jsonSql.build({ - table: 'users', - limit: 10, - offset: 20 - }); - - expect(result.query).to.be.equal( - 'select * from "users" limit 10 offset 20;' - ); - expect(result.values).to.be.eql({}); - }); - }); -}); diff --git a/legacy/json-sql/tests/2_insert.js b/legacy/json-sql/tests/2_insert.js deleted file mode 100644 index fe3f1b9d..00000000 --- a/legacy/json-sql/tests/2_insert.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Insert', function() { - it('should throw error without `values` property', function() { - expect(function() { - jsonSql.build({ - type: 'insert', - table: 'users' - }); - }).to.throw('`values` property is not set in `insert` clause'); - }); - - it('should be ok with `values` property', function() { - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: { - name: 'Max' - } - }); - - expect(result.query).to.be.equal('insert into "users" ("name") values (${p1});'); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `with` property', function() { - var result = jsonSql.build({ - 'with': [{ - name: 't_1', - select: { - table: 't_1' - } - }], - type: 'insert', - table: 'users', - values: { - name: 'Max', - age: 17, - lastVisit: null, - active: true - } - }); - - expect(result.query).to.be.equal( - 'with "t_1" as (select * from "t_1") insert into "users" ' + - '("name", "age", "lastVisit", "active") values (${p1}, 17, null, true);' - ); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `returning` property', function() { - var result = jsonSql.build({ - type: 'insert', - table: 'users', - values: { - name: 'Max', - age: 17, - lastVisit: null, - active: true - }, - returning: ['users.*'] - }); - - expect(result.query).to.be.equal( - 'insert into "users" ("name", "age", "lastVisit", "active") ' + - 'values (${p1}, 17, null, true) returning "users".*;' - ); - expect(result.values).to.be.eql({p1: 'Max'}); - }); -}); diff --git a/legacy/json-sql/tests/3_update.js b/legacy/json-sql/tests/3_update.js deleted file mode 100644 index 688b4c1d..00000000 --- a/legacy/json-sql/tests/3_update.js +++ /dev/null @@ -1,123 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Update', function() { - describe('modifier', function() { - it('should throw error without `modifier` property', function() { - expect(function() { - jsonSql.build({ - type: 'update', - table: 'users' - }); - }).to.throw('`modifier` property is not set in `update` clause'); - }); - - it('should be ok with default(`$set`)', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - name: 'Max', - age: 16, - lastVisit: null, - active: false - } - }); - - expect(result.query).to.be.equal('update "users" set "name" = ${p1}, "age" = 16, ' + - '"lastVisit" = null, "active" = false;'); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `$set`', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $set: { - name: 'Max' - } - } - }); - - expect(result.query).to.be.equal('update "users" set "name" = ${p1};'); - expect(result.values).to.be.eql({p1: 'Max'}); - }); - - it('should be ok with `$inc`', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $inc: { - age: 4 - } - } - }); - - expect(result.query).to.be.equal('update "users" set "age" = "age" + 4;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `$dec`', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $dec: { - age: 2 - } - } - }); - - expect(result.query).to.be.equal('update "users" set "age" = "age" - 2;'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('with', function() { - it('should be ok', function() { - var result = jsonSql.build({ - 'with': [{ - name: 't_1', - select: { - table: 't_1' - } - }], - type: 'update', - table: 'users', - modifier: { - $dec: { - age: 3 - } - } - }); - - expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") update "users" ' + - 'set "age" = "age" - 3;'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('returning', function() { - it('should be ok', function() { - var result = jsonSql.build({ - type: 'update', - table: 'users', - modifier: { - $dec: { - age: 3 - } - }, - returning: ['users.*'] - }); - - expect(result.query).to.be.equal( - 'update "users" set "age" = "age" - 3 returning "users".*;' - ); - expect(result.values).to.be.eql({}); - }); - }); -}); diff --git a/legacy/json-sql/tests/4_delete.js b/legacy/json-sql/tests/4_delete.js deleted file mode 100644 index c90f19f2..00000000 --- a/legacy/json-sql/tests/4_delete.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Delete', function() { - it('should be ok without `condition` property', function() { - var result = jsonSql.build({ - type: 'remove', - table: 'users' - }); - - expect(result.query).to.be.equal('delete from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `condition` property', function() { - var result = jsonSql.build({ - type: 'remove', - table: 'users', - condition: { - a: 5 - } - }); - - expect(result.query).to.be.equal('delete from "users" where "a" = 5;'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `with` property', function() { - var result = jsonSql.build({ - 'with': [{ - name: 't_1', - select: { - table: 't_1' - } - }], - type: 'remove', - table: 'users' - }); - - expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") delete from "users";'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `returning` property', function() { - var result = jsonSql.build({ - type: 'remove', - table: 'users', - returning: ['users.*'] - }); - - expect(result.query).to.be.equal( - 'delete from "users" returning "users".*;' - ); - expect(result.values).to.be.eql({}); - }); -}); diff --git a/legacy/json-sql/tests/5_union.js b/legacy/json-sql/tests/5_union.js deleted file mode 100644 index a1e79fcf..00000000 --- a/legacy/json-sql/tests/5_union.js +++ /dev/null @@ -1,256 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Union, except, intersect', function() { - describe('queries', function() { - it('should throw error without `queries` property', function() { - expect(function() { - jsonSql.build({ - type: 'union' - }); - }).to.throw('`queries` property is not set in `union` clause'); - }); - - it('should throw error with non-array value', function() { - expect(function() { - jsonSql.build({ - type: 'union', - queries: 'wrong' - }); - }).to.throw('`queries` property should be an array in `union` clause'); - }); - - it('should throw error with value length < 2', function() { - expect(function() { - jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }] - }); - }).to.throw('`queries` property should not have length less than 2 in `union` clause'); - }); - - it('should be ok with value length = 2', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") union (select * from "vipUsers");'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('type & all combinations', function() { - it('should be ok with `type` = "union", `all` = true', function() { - var result = jsonSql.build({ - type: 'union', - all: true, - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") union all (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "except"', function() { - var result = jsonSql.build({ - type: 'except', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") except (select * from "vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "except", `all` = true', function() { - var result = jsonSql.build({ - type: 'except', - all: true, - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") except all (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "intersect"', function() { - var result = jsonSql.build({ - type: 'intersect', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") intersect (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "intersect", `all` = true', function() { - var result = jsonSql.build({ - type: 'intersect', - all: true, - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - }); - - expect(result.query).to.be.equal('(select * from "users") intersect all (select * from ' + - '"vipUsers");'); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `type` = "union" subquery', function() { - var result = jsonSql.build({ - query: { - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }] - } - }); - - expect(result.query).to.be.equal('select * from ((select * from "users") union (select * ' + - 'from "vipUsers"));'); - expect(result.values).to.be.eql({}); - }); - }); - - describe('sort', function() { - it('should be ok with string value', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - sort: 'age' - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") order by "age";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with array value', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - sort: ['age', 'gender'] - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") order by "age", "gender";' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with object value', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - sort: { - age: 1, - gender: -1 - } - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") order by "age" asc, "gender" desc;' - ); - expect(result.values).to.be.eql({}); - }); - }); - - describe('limit, offset', function() { - it('should be ok with `limit` property', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - limit: 5 - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") limit 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `offset` property', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - offset: 5 - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") offset 5;' - ); - expect(result.values).to.be.eql({}); - }); - - it('should be ok with `limit` and `offset` properties', function() { - var result = jsonSql.build({ - type: 'union', - queries: [{ - table: 'users' - }, { - table: 'vipUsers' - }], - limit: 10, - offset: 20 - }); - - expect(result.query).to.be.equal( - '(select * from "users") union (select * from "vipUsers") limit 10 offset 20;' - ); - expect(result.values).to.be.eql({}); - }); - }); -}); diff --git a/legacy/json-sql/tests/6_postgresDialect.js b/legacy/json-sql/tests/6_postgresDialect.js deleted file mode 100644 index 790c0ec2..00000000 --- a/legacy/json-sql/tests/6_postgresDialect.js +++ /dev/null @@ -1,140 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')({ - dialect: 'postgresql', - namedValues: false -}); -var expect = require('chai').expect; - -describe('PostgreSQL dialect', function() { - describe('json', function() { - it('should correctly wrap each part of json path', function() { - var result = jsonSql.build({ - table: 'test', - fields: ['params->a->>b'], - condition: { - 'params->>c': {$like: '7%'} - } - }); - - expect(result.query).to.be.equal( - 'select "params"->\'a\'->>\'b\' from "test" ' + - 'where "params"->>\'c\' like ${1};' - ); - }); - - it('should be ok with `$jsonContains` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - 'params->a': { - $jsonContains: {b: 1} - } - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params"->\'a\' @> ${1};' - ); - expect(result.values).to.be.eql(['{"b":1}']); - }); - - it('should be ok with `$jsonIn` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - 'params->a': { - $jsonIn: {$field: 'data->b'} - } - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params"->\'a\' <@ "data"->\'b\';' - ); - expect(result.values).to.be.eql([]); - }); - - it('should be ok with `$jsonHas` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$jsonHas: 'account'} - } - }); - - expect(result.query).to.be.equal('select * from "test" where "params" ? ${1};'); - expect(result.values).to.be.eql(['account']); - }); - - it('should be ok with `$jsonHasAny` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$jsonHasAny: ['a', 'b']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params" ?| array[${1}, ${2}];' - ); - expect(result.values).to.be.eql(['a', 'b']); - }); - - it('should be ok with `$jsonHasAll` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$jsonHasAll: ['a', 'b']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params" ?& array[${1}, ${2}];' - ); - expect(result.values).to.be.eql(['a', 'b']); - }); - - it('should be ok with `$upper` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$upper: ['params', '3498862814541110459l']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where upper("params") = upper(${1});' - ); - expect(result.values).to.be.eql(['3498862814541110459l']); - }); - - it('should be ok with `$lower` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$lower: ['params', '3498862814541110459L']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where lower("params") = lower(${1});' - ); - expect(result.values).to.be.eql(['3498862814541110459L']); - }); - - it('should be ok with `$decode` conditional operator', function() { - var result = jsonSql.build({ - table: 'test', - condition: { - params: {$decode: ['params', '3498862814541110459L', 'hex']} - } - }); - - expect(result.query).to.be.equal( - 'select * from "test" where "params" = decode(${1}, ${2});' - ); - expect(result.values).to.be.eql(['3498862814541110459L', 'hex']); - }); - }); -}); diff --git a/legacy/json-sql/tests/7_create.js b/legacy/json-sql/tests/7_create.js deleted file mode 100644 index a93ca702..00000000 --- a/legacy/json-sql/tests/7_create.js +++ /dev/null @@ -1,279 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -describe('Create', function () { - it('should throw error without `tableFields` property', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users' - }); - }).to.throw('`tableFields` property is not set in `create` clause'); - }); - - it('should throw error with incorrect field type', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "age", - type: "NotNumber" - } - ] - }); - }).to.throw('Invalid type of field: NotNumber'); - }); - - it('should be ok with `tableFields` property', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "age", - type: "Number" - } - ] - }); - - - expect(result.query).to.be.equal('create table if not exists "users"("age" int);'); - }); - - it('should throw error when length property for string field not provided', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String" - } - ] - }); - }).to.throw('Field length can\'t be less or equal 0'); - }); - - it('should throw error with empty name', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: " ", - type: "String" - } - ] - }); - }).to.throw('Name most contains characters'); - }); - - it('should be ok with string field and length', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16 - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16));'); - }); - - it('should be ok with string field not null', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL);'); - }); - - it('should be ok with string field not null primary key', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY);'); - }); - - - it('should be ok with string field not null unique', function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - unique: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL UNIQUE);'); - }); - - - it('should be allow only one primary key field', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "secondname", - type: "String", - length: 16, - not_null: true, - primary_key: true - } - ] - }) - }).to.throw("Too much primary key 'secondname' in table"); - }); - - it('should be allow only unique field name', function () { - expect(function () { - jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "name", - type: "String", - length: 16, - not_null: true - } - ] - }) - }).to.throw("Two parameters with same name: name"); - }); - - it("should allow few fields", function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL);'); - }); - - it("should allow few fields", function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ], - foreignKeys: [ - { - field: "name", - table: "person", - table_field: "id" - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); - }); - - it("should allow few fields", function () { - var result = jsonSql.build({ - type: 'create', - table: 'users', - tableFields: [ - { - name: "name", - type: "String", - length: 16, - not_null: true, - primary_key: true - }, - { - name: "age", - type: "Number", - not_null: true - } - ], - foreignKeys: [ - { - field: "name", - table: "person", - table_field: "id" - } - ] - }); - - expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); - }); -}); diff --git a/legacy/json-sql/tests/8_index.js b/legacy/json-sql/tests/8_index.js deleted file mode 100644 index 464a85b3..00000000 --- a/legacy/json-sql/tests/8_index.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -var jsonSql = require('../lib')(); -var expect = require('chai').expect; - -/* - jsonSql.build({ - type: 'index', - table: 'users', - index: { - name: "user_id", - field: "id" - } - }); - */ - -describe('Index', function() { - it('should throw error without name property', function() { - expect(function() { - jsonSql.build({ - type: 'index', - table: 'users' - }); - }).to.throw('`name` property is not set in `index` clause'); - }); - - it('should throw error without indexOn property', function() { - expect(function() { - jsonSql.build({ - type: 'index', - table: 'users', - name: 'index_id' - }); - }).to.throw('`indexOn` property is not set in `index` clause'); - }); - - it('should be ok with name and field property', function () { - var result = jsonSql.build({ - type: "index", - table: "users", - name: "index_id", - indexOn: "id" - }); - }); -}); diff --git a/logic/account.js b/logic/account.js index 1b690863..fc511475 100644 --- a/logic/account.js +++ b/logic/account.js @@ -598,31 +598,15 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; + // Do case insensitive address comparison -> where "address" ilike $1; [ 'U16455322533504200665' ] + // In json-sql v0.2.6 it was $upper: ['address', filter.address] if (typeof filter.address === 'string') { filter.address = { - // $ilike: ['address', filter.address] - $ilike: filter.address + $ilike: ['address', filter.address] + // $ilike: filter.address }; } - // it('should be ok with `$ilike` conditional operator', function() { - // var result = jsonSql.build({ - // table: 'test', - // condition: { - // params: {$ilike: 'hello%'} - // } - // }); - - // expect(result.query).to.be.equal( - // 'select * from "test" where "params" ilike $1;' - // ); - // expect(result.values).to.be.eql(['hello%']); - // }); - - console.log('!!!!') - console.log('!!!!', filter.address) - - var sql = jsonSql.build({ type: 'select', table: this.table, @@ -634,8 +618,6 @@ Account.prototype.getAll = function (filter, fields, cb) { fields: realFields }); - console.log('!!!!', sql.query, sql.values) - this.scope.db.query(sql.query, sql.values).then(function (rows) { return setImmediate(cb, null, rows); }).catch(function (err) { From cf1b28528d870e16984974611e4778140bc68af5 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 14:27:12 +0400 Subject: [PATCH 18/33] debug: json-sql --- logic/account.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/logic/account.js b/logic/account.js index fc511475..ed89a3ac 100644 --- a/logic/account.js +++ b/logic/account.js @@ -618,6 +618,8 @@ Account.prototype.getAll = function (filter, fields, cb) { fields: realFields }); + console.log('!!!', sql.query, sql.values) + this.scope.db.query(sql.query, sql.values).then(function (rows) { return setImmediate(cb, null, rows); }).catch(function (err) { From a4a41a5c5067fe8d24ab2e3abc211aab881b4869 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 15:00:08 +0400 Subject: [PATCH 19/33] debug: json-sql --- logic/account.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/logic/account.js b/logic/account.js index ed89a3ac..7d3089eb 100644 --- a/logic/account.js +++ b/logic/account.js @@ -598,14 +598,17 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; + + console.log('!!!', filter.address); + // Do case insensitive address comparison -> where "address" ilike $1; [ 'U16455322533504200665' ] // In json-sql v0.2.6 it was $upper: ['address', filter.address] - if (typeof filter.address === 'string') { - filter.address = { - $ilike: ['address', filter.address] - // $ilike: filter.address - }; - } + // if (typeof filter.address === 'string') { + // filter.address = { + // $ilike: ['address', filter.address] + // // $ilike: filter.address + // }; + // } var sql = jsonSql.build({ type: 'select', From d2136e489617c5a6325b717b87254d2451262c13 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 15:05:26 +0400 Subject: [PATCH 20/33] debug: json-sql --- logic/account.js | 21 +++++++++++++++------ modules/sql.js | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/logic/account.js b/logic/account.js index 7d3089eb..612a86a5 100644 --- a/logic/account.js +++ b/logic/account.js @@ -603,12 +603,11 @@ Account.prototype.getAll = function (filter, fields, cb) { // Do case insensitive address comparison -> where "address" ilike $1; [ 'U16455322533504200665' ] // In json-sql v0.2.6 it was $upper: ['address', filter.address] - // if (typeof filter.address === 'string') { - // filter.address = { - // $ilike: ['address', filter.address] - // // $ilike: filter.address - // }; - // } + if (typeof filter.address === 'string') { + filter.address = { + $ilike: filter.address + }; + } var sql = jsonSql.build({ type: 'select', @@ -805,6 +804,8 @@ Account.prototype.merge = function (address, diff, cb) { var sqles = []; + console.log('2-!!!', remove[el]) + if (Object.keys(remove).length) { Object.keys(remove).forEach(function (el) { var sql = jsonSql.build({ @@ -815,6 +816,8 @@ Account.prototype.merge = function (address, diff, cb) { accountId: address } }); + console.log('2-!!!', sql.query, sql.values) + sqles.push(sql); }); } @@ -843,6 +846,8 @@ Account.prototype.merge = function (address, diff, cb) { table: self.table + '2' + el, condition: remove_object[el] }); + console.log('3-!!!', sql.query, sql.values) + sqles.push(sql); }); } @@ -870,6 +875,8 @@ Account.prototype.merge = function (address, diff, cb) { address: address } }); + console.log('4-!!!', sql.query, sql.values) + sqles.push(sql); } @@ -918,6 +925,8 @@ Account.prototype.remove = function (address, cb) { address: address } }); + console.log('5-!!!', sql.query, sql.values) + this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb, null, address); }).catch(function (err) { diff --git a/modules/sql.js b/modules/sql.js index e4a3a957..fcf9acea 100644 --- a/modules/sql.js +++ b/modules/sql.js @@ -2,7 +2,7 @@ var async = require('async'); var extend = require('extend'); -var jsonSql = require('json-sql')(); +var jsonSql = require('json-sql')({namedValues: false}); jsonSql.setDialect('postgresql'); var sandboxHelper = require('../helpers/sandbox.js'); From ccc5d430cacc32cc5b96a88ef75a81bdcb53c54f Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 15:09:13 +0400 Subject: [PATCH 21/33] debug: json-sql --- logic/account.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logic/account.js b/logic/account.js index 612a86a5..b9ed733c 100644 --- a/logic/account.js +++ b/logic/account.js @@ -804,10 +804,10 @@ Account.prototype.merge = function (address, diff, cb) { var sqles = []; - console.log('2-!!!', remove[el]) - if (Object.keys(remove).length) { Object.keys(remove).forEach(function (el) { + console.log('2-!!!', remove[el]) + var sql = jsonSql.build({ type: 'remove', table: self.table + '2' + el, From 2b4d7aae4271a6f29af84a50be7a955bbd24c2a8 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 16:14:31 +0400 Subject: [PATCH 22/33] debug: json-sql --- legacy/json-sql/.gitignore | 1 + legacy/json-sql/.jshintrc | 19 + legacy/json-sql/.npmignore | 2 + legacy/json-sql/LICENSE | 22 + legacy/json-sql/README.md | 145 ++ legacy/json-sql/docs/README.md | 1180 ++++++++++++++++ legacy/json-sql/gulpfile.js | 27 + legacy/json-sql/lib/builder.js | 233 ++++ legacy/json-sql/lib/dialects/base/blocks.js | 441 ++++++ .../json-sql/lib/dialects/base/conditions.js | 122 ++ legacy/json-sql/lib/dialects/base/index.js | 58 + .../lib/dialects/base/logicalOperators.js | 37 + .../json-sql/lib/dialects/base/modifiers.js | 37 + legacy/json-sql/lib/dialects/base/scheme.js | 146 ++ .../json-sql/lib/dialects/base/templates.js | 220 +++ legacy/json-sql/lib/dialects/mssql/index.js | 13 + .../lib/dialects/postgresql/blocks.js | 33 + .../lib/dialects/postgresql/conditions.js | 87 ++ .../json-sql/lib/dialects/postgresql/index.js | 39 + .../lib/dialects/postgresql/templates.js | 269 ++++ legacy/json-sql/lib/dialects/sqlite/blocks.js | 23 + legacy/json-sql/lib/dialects/sqlite/index.js | 17 + .../json-sql/lib/dialects/sqlite/templates.js | 236 ++++ legacy/json-sql/lib/index.js | 8 + legacy/json-sql/lib/valuesStore.js | 31 + legacy/json-sql/package.json | 44 + legacy/json-sql/tests/0_base.js | 304 ++++ legacy/json-sql/tests/1_select.js | 1222 +++++++++++++++++ legacy/json-sql/tests/2_insert.js | 73 + legacy/json-sql/tests/3_update.js | 123 ++ legacy/json-sql/tests/4_delete.js | 58 + legacy/json-sql/tests/5_union.js | 256 ++++ legacy/json-sql/tests/6_postgresDialect.js | 140 ++ legacy/json-sql/tests/7_create.js | 279 ++++ legacy/json-sql/tests/8_index.js | 45 + logic/account.js | 2 + 36 files changed, 5992 insertions(+) create mode 100644 legacy/json-sql/.gitignore create mode 100644 legacy/json-sql/.jshintrc create mode 100644 legacy/json-sql/.npmignore create mode 100644 legacy/json-sql/LICENSE create mode 100644 legacy/json-sql/README.md create mode 100644 legacy/json-sql/docs/README.md create mode 100644 legacy/json-sql/gulpfile.js create mode 100644 legacy/json-sql/lib/builder.js create mode 100644 legacy/json-sql/lib/dialects/base/blocks.js create mode 100644 legacy/json-sql/lib/dialects/base/conditions.js create mode 100644 legacy/json-sql/lib/dialects/base/index.js create mode 100644 legacy/json-sql/lib/dialects/base/logicalOperators.js create mode 100644 legacy/json-sql/lib/dialects/base/modifiers.js create mode 100644 legacy/json-sql/lib/dialects/base/scheme.js create mode 100644 legacy/json-sql/lib/dialects/base/templates.js create mode 100644 legacy/json-sql/lib/dialects/mssql/index.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/blocks.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/conditions.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/index.js create mode 100644 legacy/json-sql/lib/dialects/postgresql/templates.js create mode 100644 legacy/json-sql/lib/dialects/sqlite/blocks.js create mode 100644 legacy/json-sql/lib/dialects/sqlite/index.js create mode 100644 legacy/json-sql/lib/dialects/sqlite/templates.js create mode 100644 legacy/json-sql/lib/index.js create mode 100644 legacy/json-sql/lib/valuesStore.js create mode 100644 legacy/json-sql/package.json create mode 100644 legacy/json-sql/tests/0_base.js create mode 100644 legacy/json-sql/tests/1_select.js create mode 100644 legacy/json-sql/tests/2_insert.js create mode 100644 legacy/json-sql/tests/3_update.js create mode 100644 legacy/json-sql/tests/4_delete.js create mode 100644 legacy/json-sql/tests/5_union.js create mode 100644 legacy/json-sql/tests/6_postgresDialect.js create mode 100644 legacy/json-sql/tests/7_create.js create mode 100644 legacy/json-sql/tests/8_index.js diff --git a/legacy/json-sql/.gitignore b/legacy/json-sql/.gitignore new file mode 100644 index 00000000..c2658d7d --- /dev/null +++ b/legacy/json-sql/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/legacy/json-sql/.jshintrc b/legacy/json-sql/.jshintrc new file mode 100644 index 00000000..fe7c5b09 --- /dev/null +++ b/legacy/json-sql/.jshintrc @@ -0,0 +1,19 @@ +{ + "node": true, + "freeze": true, + "maxlen": 100, + "smarttabs": true, + "indent": 1, + "quotmark": "single", + "strict": true, + "globalstrict": true, + // use es3 to get 'extra comma' at object literal error + "es3": true, + "undef": true, + "unused": true, + "immed": true, + "eqeqeq": true, + "globals": { + "JSON": false + } +} diff --git a/legacy/json-sql/.npmignore b/legacy/json-sql/.npmignore new file mode 100644 index 00000000..3796175f --- /dev/null +++ b/legacy/json-sql/.npmignore @@ -0,0 +1,2 @@ +.npmignore +test \ No newline at end of file diff --git a/legacy/json-sql/LICENSE b/legacy/json-sql/LICENSE new file mode 100644 index 00000000..2ac98523 --- /dev/null +++ b/legacy/json-sql/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2013-2014 Oleg Korobenko + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/legacy/json-sql/README.md b/legacy/json-sql/README.md new file mode 100644 index 00000000..7891eaed --- /dev/null +++ b/legacy/json-sql/README.md @@ -0,0 +1,145 @@ +# JSON-SQL + +Library for mapping mongo-style query objects to SQL queries. + +## Quick Start + +Install it with NPM or add it to your package.json: + +``` bash +$ npm install json-sql +``` + +Then: + +``` js +var jsonSql = require('json-sql')(); + +var sql = jsonSql.build({ + type: 'select', + table: 'users', + fields: ['name', 'age'], + condition: {name: 'Max', id: 6} +}); + +sql.query +// sql string: +// select name, age from users where name = $p1 && id = 6; + +sql.values +// hash of values: +// { p1: 'Max' } +``` + +## Documentation + +Documentation is available at the [./docs directory](./docs). + +## Examples + +__Select with join:__ + +``` js +var sql = jsonSql.build({ + type: 'select', + table: 'users', + join: { + documents: { + on: {'user.id': 'documents.userId'} + } + } +}); + +sql.query +// select * from users join documents on user.id = documents.userId; + +sql.values +// {} +``` + +__Insert:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'users', + values: { + name: 'John', + lastname: 'Snow', + age: 24, + gender: 'male' + } +}); + +sql.query +// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3); + +sql.values +// { p1: 'John', p2: 'Snow', p3: 'male' } +``` + +__Update:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'users', + condition: { + id: 5 + }, + modifier: { + role: 'admin' + age: 33 + } +}); + +sql.query +// update users set role = $p1, age = 33 where id = 5; + +sql.values +// { p1: 'admin' } +``` + +__Remove:__ + +``` js +var sql = jsonSql.build({ + type: 'remove', + table: 'users', + condition: { + id: 5 + } +}); + +sql.query +// delete from users where id = 5; + +sql.values +// {} +``` + +For more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests). + +## Tests + +Clone repository from github, `cd` into cloned dir and install dev dependencies: + +``` bash +$ npm install +``` + +Then run tests with command: + +``` bash +$ gulp test +``` + +Or run tests coverage with command: + +``` bash +$ gulp coverage +``` + +## License + +[MIT](./LICENSE) diff --git a/legacy/json-sql/docs/README.md b/legacy/json-sql/docs/README.md new file mode 100644 index 00000000..8752f703 --- /dev/null +++ b/legacy/json-sql/docs/README.md @@ -0,0 +1,1180 @@ +# Documentation + +## Table of contents + +* [API](#api) + - [Initialization](#initialization) + - __[build(query)](#buildquery)__ + - [configure(options)](#configureoptions) + - [setDialect(name)](#setdialectname) +* __[Queries](#queries)__ + - [type: 'create'](#type-create) + - [type: 'select'](#type-select) + - [type: 'insert'](#type-insert) + - [type: 'update'](#type-update) + - [type: 'remove'](#type-remove) + - [type: 'union' | 'intersect' | 'except'](#type-union--intersect--except) +* __[Blocks](#blocks)__ +* __[Condition operators](#condition-operators)__ + +--- + +## API + +### Initialization + +To create new instance of json-sql builder you can use factory function: + +``` js +var jsonSql = require('json-sql')(options); +``` + +or create instance by class constructor: + +``` js +var jsonSql = new (require('json-sql').Builder)(options); +``` + +`options` are similar to [configure method options](#available-options). + +--- + +### build(query) + +Create sql query from mongo-style query object. + +`query` is a json object that has required property `type` and a set of query-specific properties. `type` property determines the type of query. List of available values of `type` property you can see at [Queries section](#queries). + +Returns object with properties: + +| Property | Description | +| -------- | ----------- | +| `query` | SQL query string | +| `value` | Array or object with values.
Exists only if `separatedValues = true`. | +| `prefixValues()` | Method to get values with `valuesPrefix`.
Exists only if `separatedValues = true`. | +| `getValuesArray()` | Method to get values as array.
Exists only if `separatedValues = true`. | +| `getValuesObject()` | Method to get values as object.
Exists only if `separatedValues = true`. | + +--- + +### configure(options) + +Set options of json-sql builder instance. + +#### Available options + +| Option name | Default value | Description | +| ----------- | ------------- | ----------- | +| `separatedValues` | `true` | If `true` - create placeholder for each string value and put it value to result `values`.
If `false` - put string values into sql query without placeholder (potential threat of sql injection). | +| `namedValues` | `true` | If `true` - create hash of values with placeholders p1, p2, ...
If `false` - put all values into array.
Option is used if `separatedValues = true`. | +| `valuesPrefix` | `'$'` | Prefix for values placeholders
Option is used if `namedValues = true`. | +| `dialect` | `'base'` | Active dialect. See setDialect for dialects list. | +| `wrappedIdentifiers` | `true` | If `true` - wrap all identifiers with dialect wrapper (name -> "name"). | + +--- + +### setDialect(name) + +Set active dialect, name can has value `'base'`, `'mssql'`, `'mysql'`, `'postrgresql'` or `'sqlite'`. + +--- + +## Queries + +### type: 'create' + +>[ [tableFields](#tableFields) ]
+>[ [table](#table) ]
+>[ [foreignKeys](#foreignKeys) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + unique: true, + default: "empty" + } + ] +}); + +sql.query +// create table if not exists "users"("name" varchar(16) NOT NULL default "empty" UNIQUE); +``` + +--- + +### type: 'select' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[ [distinct](#distinct) ]
+>[ [fields](#fields) ]
+>[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
+>[ [alias](#alias) ]
+>[ [join](#join) ]
+>[ [condition](#condition) ]
+>[ [group](#group) ]
+>[ [sort](#sort) ]
+>[ [limit](#limit) ]
+>[ [offset](#offset) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'select', + fields: ['a', 'b'] + table: 'table' +}); + +sql.query +// select "a", "b" from "table"; +``` + +If `fields` is not specified in query, result fields is `*` (all columns of the selected rows). + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'select', + table: 'table' +}); + +sql.query +// select * from "table"; +``` + +--- + +### type: 'insert' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[ [or](#or) ]
+>[table](#table)
+>[values](#values)
+>[ [condition](#condition) ]
+>[ [returning](#returning) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: {a: 4} +}); + +sql.query +// insert into "table" ("a") values (4); +``` + +--- + +### type: 'update' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[ [or](#or) ]
+>[table](#table)
+>[modifier](#modifier)
+>[ [condition](#condition) ]
+>[ [returning](#returning) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'table', + modifier: {a: 5} +}); + +sql.query +// update "table" set a = 5; +``` + +--- + +### type: 'remove' + +>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[table](#table)
+>[ [condition](#condition) ]
+>[ [returning](#returning) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'remove', + table: 'table' +}); + +sql.query +// delete from "table"; +``` + +--- + +### type: 'union' | 'intersect' | 'except' + +>[ [all](#all) ]
+>[ [with](#with-withrecursive) | [withRecursive](#with-withrecursive) ]
+>[queries](#queries)
+>[ [sort](#sort) ]
+>[ [limit](#limit) ]
+>[ [offset](#offset) ] + +__`type: 'union'` example:__ + +``` js +var sql = jsonSql.build({ + type: 'union', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") union (select * from "table2"); +``` + +__`type: 'intersect'` example:__ + +``` js +var sql = jsonSql.build({ + type: 'intersect', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") intersect (select * from "table2"); +``` + +__`type: 'except'` example:__ + +``` js +var sql = jsonSql.build({ + type: 'except', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") except (select * from "table2"); +``` + +--- + +## Blocks + +Blocks are small chunks of query. + +### with, withRecursive + +Should be an `array` or an `object`. + +If value is an `array`, each item of array should be an `object` and should conform the scheme: + +>name
+>[ [fields](#fields) ]
+>[query](#query) | [select](#select) | [expression](#expression) + +__Example:__ + +``` js +var sql = jsonSql.build({ + 'with': [{ + name: 'table', + select: {table: 'withTable'} + }], + table: 'table' +}); + +sql.query +// with "table" as (select * from "withTable") select * from "table"; +``` + +If value is an `object`, keys of object interpret as names and each value should be an `object` and should conform the scheme: + +>[ name ]
+>[ [fields](#fields) ]
+>[query](#query) | [select](#select) | [expression](#expression) + +__Example:__ + +``` js +var sql = jsonSql.build({ + 'with': { + table: { + select: {table: 'withTable'} + } + }, + table: 'table' +}); + +sql.query +// with "table" as (select * from "withTable") select * from "table"; +``` + +--- + +### distinct + +Should be a `boolean`: + +``` +distinct: true +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + distinct: true, + table: 'table' +}); + +sql.query +// select distinct * from "table"; +``` + +--- + +### tableFields + +Should be an `array` + +Contains array of table`s fields + +--- + +### foreignKeys + +Should be an `array` + +__Example:__ + +``` js +var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ], + foreignKeys: [ + { + field: "name", + table: "person", + table_field: "id" + } + ] +}); + +sql.query +// create table "users"(name varchar(16) NOT NULL PRIMARY KEY,age int NOT NULL, FOREIGN KEY (name) REFERENCES person(id)); +``` + +--- + +### fields + +Should be an `array` or an `object`. + +If value is an `array`, each item interprets as [term block](#term). + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [ + 'a', + {b: 'c'}, + {table: 'd', name: 'e', alias: 'f'}, + ['g'] + ], + table: 'table' +}); + +sql.query +// select "a", "b" as "c", "d"."e" as "f", "g" from "table"; +``` + +If value is an `object`, keys of object interpret as field names and each value should be an `object` and should conform the scheme: + +>[ name ]
+>[ [table](#table) ]
+>[ cast ]
+>[ [alias](#alias) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: { + a: 'b', + d: {table: 'c', alias: 'e'} + }, + table: 'table' +}); + +sql.query +// select "a" as "b", "c"."d" as "e" from "table"; +``` + +--- + +### term + +Should be: +* a `string` - interprets as field name; +* another simple type or an `array` - interprets as value; +* an `object` - should conform the scheme: + +>[query](#query) | [select](#select) | [field](#field) | [value](#value) | [func](#func) | [expression](#expression)
+>[ cast ]
+>[ [alias](#alias) ] + +--- + +### field + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +field: 'fieldName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{field: 'a'}], + table: 'table' +}); + +sql.query +// select "a" from "table"; +``` + +If value is an `object` it should conform the scheme: + +>name
+>[ [table](#table) ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{field: {name: 'a', table: 'table'}}], + table: 'table' +}); + +sql.query +// select "table"."a" from "table"; +``` + +--- + +### value + +Can have any type. + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [ + {value: 5}, + {value: 'test'} + ], + table: 'table' +}); + +sql.query +// select 5, $p1 from "table"; + +sql.values +// {p1: 'test'} +``` + +--- + +### table + +Should be a `string`: + +``` +table: 'tableName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table' +}); + +sql.query +// select * from "table"; +``` + +--- + +### query + +Should be an `object`. Value interprets as sub-query and process recursively with [build(query)](#buildquery) method. + +__Example:__ + +``` js +var sql = jsonSql.build({ + query: {type: 'select', table: 'table'} +}); + +sql.query +// select * from (select * from "table"); +``` + +--- + +### select + +Should be an `object`. Value interprets as sub-select and process recursively with [build(query)](#buildquery) method. + +__Example:__ + +``` js +var sql = jsonSql.build({ + select: {table: 'table'} +}); + +sql.query +// select * from (select * from "table"); +``` + +--- + +### func + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +func: 'random' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{func: 'random'}], + table: 'table' +}); + +sql.query +// select random() from "table"; +``` + +If value is an `object` it should conform the scheme: + +>name
+>[ args ] + +where `name` is a `string` name of function, `args` is an `array` that contains it arguments. + +__Example:__ + +``` js +var sql = jsonSql.build({ + fields: [{ + func: { + name: 'sum', + args: [{field: 'a'}] + } + }], + table: 'table' +}); + +sql.query +// select sum("a") from table; +``` + +--- + +### expression + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +expression: 'random()' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + expression: 'generate_series(2, 4)' +}); + +sql.query +// select * from generate_series(2, 4); +``` + +If value is an `object` it should conform the scheme: + +>pattern
+>[ values ] + +where `pattern` is a `string` pattern with placeholders `{placeholderName}`, `values` is a hash that contains values for each `placeholderName`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + expression: { + pattern: 'generate_series({start}, {stop})', + values: {start: 2, stop: 4} + } +}); + +sql.query +// select * from generate_series(2, 4); +``` + +--- + +### alias + +Should be a `string` or an `object`. + +If value is a `string`: + +``` +alias: 'aliasName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + alias: 'alias' +}); + +sql.query +// select * from "table" as "alias"; +``` + +If value is an `object` it should conform the scheme: + +>name
+>[ columns ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + alias: {name: 'alias'} +}); + +sql.query +// select * from "table" as "alias"; +``` + +--- + +### join + +Should be an `array` or an `object`. + +If value is an `array`, each item of array should be an `object` and should conform the scheme: + +>[ type ]
+>[table](#table) | [query](#query) | [select](#select) | [expression](#expression)
+>[ [alias](#alias) ]
+>[ on ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + join: [{ + type: 'right', + table: 'joinTable', + on: {'table.a': 'joinTable.b'} + }] +}); + +sql.query +// select * from "table" right join "joinTable" on "table"."a" = "joinTable"."b"; +``` + +If value is an `object`, keys of object interpret as table names and each value should be an `object` and should conform the scheme: + +>[ type ]
+>[ [table](#table) | [query](#query) | [select](#select) | [expression](#expression) ]
+>[ [alias](#alias) ]
+>[ on ] + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + join: { + joinTable: { + type: 'inner', + on: {'table.a': 'joinTable.b'} + } + }] +}); + +sql.query +// select * from "table" inner join "joinTable" on "table"."a" = "joinTable"."b"; +``` + +__Join with sub-select example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + join: [{ + select: {table: 'joinTable'}, + alias: 'joinTable', + on: {'table.a': 'joinTable.b'} + }] +}); + +sql.query +// select * from "table" join (select * from "joinTable") as "joinTable" on "table"."a" = "joinTable"."b"; +``` + +--- + +### condition + +Should be an `array` or an `object`. + +__`array` example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + condition: [ + {a: {$gt: 1}}, + {b: {$lt: 10}} + ] +}); + +sql.query +// select * from "table" where "a" > 1 and "b" < 10; +``` + +__`object` example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + condition: { + a: {$gt: 1}, + b: {$lt: 10} + } +}); + +sql.query +// select * from "table" where "a" > 1 and "b" < 10; +``` + +--- + +### group + +Should be a `string` or an `array`. + +If value is a `string`: + +``` +group: 'fieldName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + group: 'a' +}); + +sql.query +// select * from "table" group by "a"; +``` + +If value is an `array`: + +``` +group: ['fieldName1', 'fieldName2'] +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + group: ['a', 'b'] +}); + +sql.query +// select * from "table" group by "a", "b"; +``` + +--- + +### sort + +Should be a `string`, an `array` or an `object`. + +If value is a `string`: + +``` +sort: 'fieldName' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + sort: 'a' +}); + +sql.query +// select * from "table" order by "a"; +``` + +If value is an `array`: + +``` +sort: ['fieldName1', 'fieldName2'] +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + sort: ['a', 'b'] +}); + +sql.query +// select * from "table" order by "a", "b"; +``` + +If value is an `object`: + +``` +sort: { + fieldName1: 1, + fieldName2: -1 +} +``` + +__Example__: + +``` js +var sql = jsonSql.build({ + table: 'table', + sort: {a: 1, b: -1} +}); + +sql.query +// select * from "table" order by "a" asc, "b" desc; +``` + +--- + +### limit + +Should be a `number`. + +``` +limit: limitValue +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + limit: 5 +}); + +sql.query +// select * from "table" limit 5; +``` + +--- + +### offset + +Should be a `number`. + +``` +offset: offsetValue +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + table: 'table', + offset: 5 +}); + +sql.query +// select * from "table" offset 5; +``` + +--- + +### or + +Should be a `string`. + +Available values: 'rollback', 'abort', 'replace', 'fail', 'ignore'. + +``` +or: 'orValue' +``` + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + or: 'replace', + table: 'table', + values: {a: 5} +}); + +sql.query +// insert or replace into "table" ("a") values (5); +``` + +--- + +### values + +Should be an `array` or an `object`. + +If value is an `array`, each item should be an `object` and interprets as single inserted row where keys are field names and corresponding values are field values. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: [ + {a: 5, b: 'text1'}, + {a: 6, b: 'text2'} + ] +}); + +sql.query +// insert into "table" ("a", "b") values (5, $p1), (6, $p2); + +sql.values +// {p1: 'text1', p2: 'text2'} +``` + +If value is an `object`, it interprets as single inserted row where keys are field names and corresponding values are field values. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: {a: 5, b: 'text'} +}); + +sql.query +// insert into "table" ("a", "b") values (5, $p1); + +sql.values +// {p1: 'text'} +``` + +Also you can specify fields array. If there no key in value object it value is `null`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + fields: ['a', 'b', 'c'], + values: {c: 'text', b: 5} +}); + +sql.query +// insert into "table" ("a", "b", "c") values (null, 5, $p1); + +sql.values +// {p1: 'text'} +``` + +--- + +### modifier + +Should be an `object`. + +You can specify modifier operator. +Available operators: `$set`, `$inc`, `$dec`, `$mul`, `$div`, `$default`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'table', + modifier: { + $set: {a: 5}, + $default: {b: true}, + $inc: {c: 10} + } +}); + +sql.query +// update "table" set "a" = 5, "b" = default, "c" = "c" + 10; +``` + +If modifier operator is not specified it uses default operator `$set`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'update', + table: 'table', + modifier: {a: 5} +}); + +sql.query +// update "table" set "a" = 5; +``` + +--- + +### returning + +Format is similar to [fields](#fields) block. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'insert', + table: 'table', + values: {a: 5}, + returning: ['a'] +}); + +sql.query +// insert into "table" ("a") values (5) returning "a"; +``` + +--- + +### all + +Should be a `boolean`. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'union', + all: true, + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") union all (select * from "table2"); +``` + +--- + +### queries + +Should be an `array` with minimum 2 items. Each item interprets as sub-query and process recursively with [build(query)](#buildquery) method. + +__Example:__ + +``` js +var sql = jsonSql.build({ + type: 'union', + queries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// (select * from "table1") union (select * from "table2"); + +//or for sqlite3 +jsonSql.setDialect("sqlite"); +var sql = jsonSql.build({ + type: 'union', + unionqueries: [ + {type: 'select', table: 'table1'}, + {type: 'select', table: 'table2'} + ] +}); + +sql.query +// select * from "table1" union select * from "table2"; +``` + +--- + +## Condition operators + +TODO: write this section \ No newline at end of file diff --git a/legacy/json-sql/gulpfile.js b/legacy/json-sql/gulpfile.js new file mode 100644 index 00000000..c7bbf59b --- /dev/null +++ b/legacy/json-sql/gulpfile.js @@ -0,0 +1,27 @@ +'use strict'; + +var gulp = require('gulp'); +var mocha = require('gulp-mocha'); +var jshint = require('gulp-jshint'); + +gulp.task('test', function() { + return gulp.src(['./tests/*.js'], {read: false}) + .pipe( + mocha({ + reporter: 'spec', + bail: true + }) + ); +}); + +gulp.task('lint', function() { + return gulp.src('./lib/**/*.js') + .pipe(jshint()) + .pipe(jshint.reporter('unix')); +}); + +gulp.task('lintTests', function() { + return gulp.src('./tests/*.js') + .pipe(jshint()) + .pipe(jshint.reporter('unix')); +}); \ No newline at end of file diff --git a/legacy/json-sql/lib/builder.js b/legacy/json-sql/lib/builder.js new file mode 100644 index 00000000..fa0d2a0d --- /dev/null +++ b/legacy/json-sql/lib/builder.js @@ -0,0 +1,233 @@ +'use strict'; + +var _ = require('underscore'); + +var dialects = { + base: require('./dialects/base'), + mssql: require('./dialects/mssql'), + postgresql: require('./dialects/postgresql'), + sqlite: require('./dialects/sqlite') +}; + +var blockRegExp = /\{([a-z0-9]+)\}(.|$)/ig; + +var Builder = module.exports = function (options) { + this.configure(options); +}; + +Builder.prototype._reset = function () { + if (this.options.separatedValues) { + this._placeholderId = 1; + this._values = this.options.namedValues ? {} : []; + } else { + delete this._placeholderId; + delete this._values; + } + + this._query = ''; +}; + +Builder.prototype._getPlaceholder = function () { + return (this.options.namedValues ? 'p' : '') + (this._placeholderId++); +}; + +Builder.prototype._wrapPlaceholder = function (name) { + return this.options.valuesPrefix + '{' + name + '}'; +}; + +Builder.prototype._pushValue = function (value) { + var valueType = typeof value; + + if (valueType === 'undefined') { + return 'null'; + } else if (value === null || valueType === 'number' || + valueType === 'boolean') { + return String(value); + } else if (valueType === 'string') { + if (this.options.separatedValues) { + var placeholder = this._getPlaceholder(); + if (this.options.namedValues) { + this._values[placeholder] = value; + } else { + this._values.push(value); + } + return this._wrapPlaceholder(placeholder); + } else { + return '\'' + value + '\''; + } + } else if (valueType == 'object') { + if (Object.prototype.toString.call(value) == "[object Array]") { + if (value.length > 0) { + return value.join(",") + } else { + return 'null'; + } + } + else if (Buffer.isBuffer(value)) { + var placeholder = this._getPlaceholder(); + this._values[placeholder] = value; + return this._wrapPlaceholder(placeholder); + //return "X'" + value.toString('hex') + "'"; + } else { + var placeholder = this._getPlaceholder(); + this._values[placeholder] = value; + return this._wrapPlaceholder(placeholder); + //return ("'" + JSON.stringify(value).replace(/'/g, "''") + "'"); + } + } else { + throw new Error('Wrong value type "' + valueType + '"'); + } +}; + +Builder.prototype.configure = function (options) { + options = options || {}; + + this.options = _(options).defaults({ + separatedValues: true, + namedValues: true, + valuesPrefix: '$', + dialect: 'base', + wrappedIdentifiers: true + }); + + this.setDialect(this.options.dialect); + + this._reset(); +}; + +Builder.prototype.buildCondition = function (params) { + var self = this; + var result = ''; + + var condition = params.condition; + var logicalOperator = params.logicalOperator || + this.dialect.config.defaultLogicalOperator; + + if (_.isObject(condition) && !_.isEmpty(condition)) { + // if condition is array: [{a: 1}, {b: 2}] + if (_.isArray(condition)) { + result = _(condition).map(function (item) { + return self.buildCondition({ + condition: item, + operator: params.operator + }); + }); + + // if condition is object + } else { + result = _(condition).map(function (value, field) { + // if field name is operator + if (field[0] === '$') { + // if field name is logical operator: {$logicalOperator: {a: 1}} + if (!self.dialect.logicalOperators.has(field)) { + throw new Error('Unknown logical operator "' + field + '"'); + } + + return self.buildCondition({ + condition: value, + logicalOperator: field, + operator: params.operator + }); + + // if condition item is object: {a: {$operator: 4}} + } else if (_.isObject(value)) { + var logicalOperatorFn = self.dialect.logicalOperators + .get(self.dialect.config.defaultLogicalOperator); + + return logicalOperatorFn(_(value).map(function (operatorValue, operator) { + var operatorFn = self.dialect.conditions.get(operator); + if (!operatorFn) { + throw new Error('Unknown operator "' + operator + '"'); + } + return operatorFn(field, operator, operatorValue); + })); + + // if condition item type is simple: {a: 1, b: 2} + } else { + var operatorFn = self.dialect.conditions.get(params.operator); + if (!operatorFn) { + throw new Error('Unknown operator "' + params.operator + '"'); + } + return operatorFn(field, params.operator, value); + } + }); + } + + result = this.dialect.logicalOperators + .get(logicalOperator)(_(result).compact()); + } + + return result; +}; + +Builder.prototype.buildBlock = function (block, params) { + var blockFn = this.dialect.blocks.get(block); + + if (!blockFn) { + throw new Error('Unknown block "' + block + '"'); + } + + return blockFn(params); +}; + +Builder.prototype.buildTemplate = function (type, params) { + var self = this; + + var template = this.dialect.templates.get(type); + if (!template) { + throw new Error('Unknown template type "' + type + '"'); + } + + params = _({}).defaults(params, template.defaults); + + if (template.validate) { + template.validate(type, params); + } + + return template.pattern.replace(blockRegExp, function (fullMatch, block, space) { + if (typeof params[block] !== 'undefined') { + return self.buildBlock(block, params) + space; + } else { + return ''; + } + }).trim(); +}; + +Builder.prototype.build = function (params) { + var builder = this; + + this._reset(); + + this._query = this.buildTemplate('query', {queryBody: params}) + ';'; + + if (this.options.separatedValues) { + return { + query: this._query, + values: this._values, + prefixValues: function () { + var values = {}; + _(this.getValuesObject()).each(function (value, name) { + values[builder._wrapPlaceholder(name)] = value; + }); + return values; + }, + getValuesArray: function () { + return _.isArray(this.values) ? this.values : _(this.values).values(); + }, + getValuesObject: function () { + return _.isArray(this.values) ? _(_.range(1, this.values.length + 1)).object(this.values) : + this.values; + } + }; + } else { + return {query: this._query}; + } +}; + +Builder.prototype.setDialect = function (name) { + if (!dialects[name]) { + throw new Error('Unknown dialect "' + name + '"'); + } + + this.dialect = new (dialects[name])(this); +}; diff --git a/legacy/json-sql/lib/dialects/base/blocks.js b/legacy/json-sql/lib/dialects/base/blocks.js new file mode 100644 index 00000000..809c5fd6 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/blocks.js @@ -0,0 +1,441 @@ +'use strict'; + +var _ = require('underscore'); +var scheme = require('./scheme.js'); + +function removeTopBrackets(condition) { + if (condition.length && condition[0] === '(' && + condition[condition.length - 1] === ')') { + condition = condition.slice(1, condition.length - 1); + } + + return condition; +} + +module.exports = function(dialect) { + dialect.blocks.add('distinct', function() { + return 'distinct'; + }); + + + dialect.blocks.add('field', function(params) { + var field = params.field || params.$field; + var expression = params.expression || params.$expression; + + if (!field && !expression) { + throw new Error('Neither `field` nor `expression` properties aren\'t set'); + } + + if (field) { + field = this.dialect._wrapIdentifier(field); + + var table = params.table || params.$table; + if (table) { + field = this.buildBlock('table', {table: table}) + '.' + field; + } + + if (expression) { + if (!_.isArray(expression)) expression = [expression]; + var exprSuffix = (new Array(expression.length + 1)).join(')'); + field = expression.join('(') + '(' + field + exprSuffix; + } + } else { + field = expression; + } + + var cast = params.cast || params.$cast; + if (cast) { + field = 'cast(' + field + ' as ' + cast + ')'; + } + + var alias = params.alias || params.$alias; + if (alias) { + field += ' ' + this.buildBlock('alias', {alias: alias}); + } + + return field; + }); + + + dialect.blocks.add('fields', function(params) { + var self = this; + + var fields = params.fields || {}; + var result = ''; + + if (!_.isObject(fields) && !_.isString(fields)) { + throw new Error('Invalid `fields` property type "' + (typeof fields) + '"'); + } + + if (_.isObject(fields)) { + if (_.isEmpty(fields)) { + result = '*'; + } else { + // If fields is array: ['a', {b: 'c'}, {field: '', table: 't', alias: 'r'}] + if (_.isArray(fields)) { + result = _(fields).map(function(item) { + + if (_.isObject(item)) { + // if field is field object: {field: '', table: 't', alias: 'r'} + if (item.field || item.expression) { + return self.buildBlock('field', item); + + // if field is non-field object: {b: 'c'} + } else { + return self.buildBlock('fields', {fields: item}); + } + + // if field is string: 'a' + } else if (_.isString(item)) { + return self.buildBlock('field', {field: item}); + } + }); + + // If fields is object: {a: 'u', b: {table: 't', alias: 'c'}} + } else { + // use keys as field names + result = _(fields).map(function(item, field) { + // b: {table: 't', alias: 'c'} + if (_.isObject(item)) { + if (!item.field) { + item = _.clone(item); + item.field = field; + } + + return self.buildBlock('field', item); + + // a: 'u' + } else if (_.isString(item)) { + return self.buildBlock('field', { + field: field, + alias: item + }); + } + + return ''; + }); + } + + result = result.join(', '); + } + } else { + result = fields; + } + + return result; + }); + + dialect.blocks.add('table', function(params) { + return this.dialect._wrapIdentifier(params.table); + }); + + dialect.blocks.add('expression', function(params) { + return params.expression; + }); + + dialect.blocks.add('name', function(params) { + return this.dialect._wrapIdentifier(params.name); + }); + + dialect.blocks.add('alias', function(params) { + var self = this; + + var result; + + if (_.isObject(params.alias)) { + if (!params.alias.name) { + throw new Error('Alias `name` property is required'); + } + + result = this.dialect._wrapIdentifier(params.alias.name); + + if (_.isArray(params.alias.columns)) { + result += '(' + _(params.alias.columns).map(function(column) { + return self.dialect._wrapIdentifier(column); + }).join(', ') + ')'; + } + } else { + result = this.dialect._wrapIdentifier(params.alias); + } + + return 'as ' + result; + }); + + dialect.blocks.add('condition', function(params) { + var condition = params.condition; + var result = ''; + + if (_.isObject(condition)) { + result = this.buildCondition({ + condition: condition, + operator: '$eq' + }); + } else if (_.isString(condition)) { + result = condition; + } + + if (result) { + result = 'where ' + removeTopBrackets(result); + } + + return result; + }); + + dialect.blocks.add('modifier', function(params) { + var self = this; + + var modifier = params.modifier; + var result = ''; + + // if modifier is object -> call method for each operator + if (_.isObject(modifier)) { + result = _(modifier).map(function(values, field) { + var modifierFn = self.dialect.modifiers.get(field); + var methodParams = values; + + if (!modifierFn) { + modifierFn = self.dialect.modifiers.get(self.dialect.config.defaultModifier); + methodParams = {}; + methodParams[field] = values; + } + + return modifierFn.call(self, methodParams); + }).join(', '); + + // if modifier is string -> not process it + } else if (_.isString(modifier)) { + result = modifier; + } + + if (result) { + result = 'set ' + result; + } + + return result; + }); + + dialect.blocks.add('join', function(params) { + var self = this; + + var join = params.join; + var result = ''; + + // if join is array -> make each joinItem + if (_.isArray(join)) { + result = _(join).map(function(joinItem) { + return self.buildTemplate('joinItem', joinItem); + }).join(' '); + + // if join is object -> set table name from key and make each joinItem + } else if (_.isObject(join)) { + result = _(join).map(function(joinItem, table) { + if (!joinItem.table && !joinItem.query && !joinItem.select) { + joinItem = _.clone(joinItem); + joinItem.table = table; + } + + return self.buildTemplate('joinItem', joinItem); + }).join(' '); + + // if join is string -> not process + } else if (_.isString(join)) { + result = join; + } + + return result; + }); + + dialect.blocks.add('type', function(params) { + return params.type.toLowerCase(); + }); + + + dialect.blocks.add('on', function(params) { + var on = params.on; + var result = ''; + + // `on` block is use `$field` as default compare operator + // because it most used case + if (_.isObject(on)) { + result = this.buildCondition({ + condition: on, + operator: '$field' + }); + } else if (_.isString(on)) { + result = on; + } + + if (result) { + result = 'on ' + removeTopBrackets(result); + } + + return result; + }); + + + dialect.blocks.add('group', function(params) { + var result = ''; + var group = params.group; + + if (_.isArray(group)) { + var self = this; + result = _(group).map(function(field) { + return self.dialect._wrapIdentifier(field); + }).join(', '); + } else if (_.isString(group)) { + result = this.dialect._wrapIdentifier(group); + } else if (_.isObject(group)) { + result = group.expression + (group.having ? " having " + this.buildCondition({ + condition: group.having + }) : ""); + } + + if (result) { + result = 'group by ' + result; + } + + return result; + }); + + + dialect.blocks.add('sort', function(params) { + var result = ''; + var sort = params.sort; + + // if sort is array -> field1, field2, ... + var self = this; + if (_.isArray(sort)) { + result = _(sort).map(function(sortField) { + return self.dialect._wrapIdentifier(sortField); + }).join(', '); + + // if sort is object -> field1 asc, field2 desc, ... + } else if (_.isObject(sort)) { + result = _(sort).map(function(direction, field) { + return self.dialect._wrapIdentifier(field) + ' ' + + (direction > 0 ? 'asc' : 'desc'); + }).join(', '); + + // if sort is string -> not process + } else if (_.isString(sort)) { + result = this.dialect._wrapIdentifier(sort); + } + + if (result) { + result = 'order by ' + result; + } + + return result; + }); + + + dialect.blocks.add('limit', function(params) { + return 'limit ' + this._pushValue(params.limit); + }); + + + dialect.blocks.add('offset', function(params) { + return 'offset ' + this._pushValue(params.offset); + }); + + + dialect.blocks.add('or', function(params) { + return 'or ' + params.or; + }); + + + dialect.blocks.add('values', function(params) { + var self = this; + + var fieldValues = params.values; + + if (!_.isArray(fieldValues)) { + fieldValues = [fieldValues]; + } + + var fields = params.fields || _(fieldValues).chain().map(function(values) { + return _(values).keys(); + }).flatten().uniq().value(); + + fieldValues = _(fieldValues).map(function(values) { + return _(fields).map(function(field) { + return self._pushValue(values[field]); + }); + }); + + return this.buildTemplate('insertValues', { + fields: fields, + fieldValues: fieldValues + }); + }); + + dialect.blocks.add('fieldValues', function(params) { + return _(params.fieldValues).map(function(values) { + return '(' + values.join(', ') + ')'; + }).join(', '); + }); + + dialect.blocks.add('queryBody', function(params) { + var query = params.queryBody || {}; + + return this.buildTemplate(query.type || 'select', query); + }); + + dialect.blocks.add('query', function(params) { + return this.buildTemplate('subQuery', {queryBody: params.query}); + }); + + dialect.blocks.add('select', function(params) { + return this.buildTemplate('subQuery', {queryBody: params.select}); + }); + + dialect.blocks.add('tableFields', function (params) { + return scheme.parse.call(this, params.tableFields, params.foreignKeys); + }); + + dialect.blocks.add('queries', function(params) { + var self = this; + + return _(params.queries).map(function(query) { + return self.buildTemplate('subQuery', {queryBody: query}); + }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); + }); + + dialect.blocks.add('indexOn', function (params) { + return params.indexOn; + }); + + dialect.blocks.add('with', function(params) { + var self = this; + + var withList = params['with']; + var result = ''; + + // if with clause is array -> make each withItem + if (_.isArray(withList)) { + result = _(withList).map(function(withItem) { + return self.buildTemplate('withItem', withItem); + }).join(', '); + + // if with clause is object -> set name from key and make each withItem + } else if (_.isObject(withList)) { + result = _(withList).map(function(withItem, name) { + if (!withItem.name) { + withItem = _.clone(withItem); + withItem.name = name; + } + return self.buildTemplate('withItem', withItem); + }).join(', '); + + // if with clause is string -> not process + } else if (_.isString(withList)) { + result = withList; + } + + return 'with ' + result; + }); + + dialect.blocks.add('returning', function(params) { + return 'returning ' + this.buildBlock('fields', {fields: params.returning}); + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/conditions.js b/legacy/json-sql/lib/dialects/base/conditions.js new file mode 100644 index 00000000..c46be4f3 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/conditions.js @@ -0,0 +1,122 @@ +'use strict'; + +var _ = require('underscore'); + +// Compare conditions (e.g. $eq, $gt) +function buildCompareCondition(builder, field, operator, value) { + var placeholder; + + // if value is object, than make field block from it + if (value && _.isObject(value)) { + placeholder = builder.buildBlock('field', value); + } else { + // if value is simple - create placeholder for it + placeholder = builder._pushValue(value); + } + + field = builder.dialect._wrapIdentifier(field); + return [field, operator, placeholder].join(' '); +} + +// Contain conditions ($in/$nin) +function buildContainsCondition(builder, field, operator, value) { + var newValue; + + if (_.isArray(value)) { + if (!value.length) value = [null]; + + newValue = '(' + _(value).map(function(item) { + return builder._pushValue(item); + }).join(', ') + ')'; + } else if (_.isObject(value)) { + newValue = builder.buildTemplate('subQuery', {queryBody: value}); + } else { + throw new Error('Invalid `' + operator + '` value type "' + (typeof value) + '"'); + } + + field = builder.dialect._wrapIdentifier(field); + return [field, operator, newValue].join(' '); +} + +module.exports = function(dialect) { + dialect.conditions.add('$eq', function(field, operator, value) { + return buildCompareCondition(this, field, '=', value); + }); + + dialect.conditions.add('$ne', function(field, operator, value) { + return buildCompareCondition(this, field, '!=', value); + }); + + dialect.conditions.add('$gt', function(field, operator, value) { + return buildCompareCondition(this, field, '>', value); + }); + + dialect.conditions.add('$lt', function(field, operator, value) { + return buildCompareCondition(this, field, '<', value); + }); + + dialect.conditions.add('$gte', function(field, operator, value) { + return buildCompareCondition(this, field, '>=', value); + }); + + dialect.conditions.add('$lte', function(field, operator, value) { + return buildCompareCondition(this, field, '<=', value); + }); + + dialect.conditions.add('$is', function(field, operator, value) { + return buildCompareCondition(this, field, 'is', value); + }); + + dialect.conditions.add('$isnot', function(field, operator, value) { + return buildCompareCondition(this, field, 'is not', value); + }); + + dialect.conditions.add('$like', function(field, operator, value) { + return buildCompareCondition(this, field, 'like', value); + }); + + dialect.conditions.add('$null', function(field, operator, value) { + return buildCompareCondition(this, field, 'is' + (value ? '' : ' not'), null); + }); + + dialect.conditions.add('$field', function(field, operator, value) { + var placeholder; + + // if value is object, than make field block from it + if (_.isObject(value)) { + placeholder = this.buildBlock('field', value); + } else { + // $field - special operator, that not make placeholder for value + placeholder = this.dialect._wrapIdentifier(value); + } + + return [this.dialect._wrapIdentifier(field), '=', placeholder].join(' '); + }); + + + dialect.conditions.add('$in', function(field, operator, value) { + return buildContainsCondition(this, field, 'in', value); + }); + + dialect.conditions.add('$nin', function(field, operator, value) { + return buildContainsCondition(this, field, 'not in', value); + }); + + dialect.conditions.add('$between', function(field, operator, value) { + if (!_.isArray(value)) { + throw new Error('Invalid `$between` value type "' + (typeof value) + '"'); + } + + if (value.length < 2) { + throw new Error('`$between` array length should be 2 or greater'); + } + + return [ + this.dialect._wrapIdentifier(field), + 'between', + this._pushValue(value[0]), + 'and', + this._pushValue(value[1]) + ].join(' '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/index.js b/legacy/json-sql/lib/dialects/base/index.js new file mode 100644 index 00000000..2a0e2c7a --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/index.js @@ -0,0 +1,58 @@ +'use strict'; + +var _ = require('underscore'); +var ValuesStore = require('../../valuesStore'); + +var templatesInit = require('./templates'); +var blocksInit = require('./blocks'); +var conditionsInit = require('./conditions'); +var logicalOperatorsInit = require('./logicalOperators'); +var modifiersInit = require('./modifiers'); + +var Dialect = module.exports = function(builder) { + this.builder = builder; + + this.blocks = new ValuesStore({context: builder}); + this.modifiers = new ValuesStore({context: builder}); + this.conditions = new ValuesStore({context: builder}); + this.logicalOperators = new ValuesStore({context: builder}); + this.templates = new ValuesStore({context: builder}); + + templatesInit(this); + blocksInit(this); + conditionsInit(this); + modifiersInit(this); + logicalOperatorsInit(this); + + this.identifierPartsRegexp = new RegExp( + '(\\' + this.config.identifierPrefix + '[^\\' + this.config.identifierSuffix + ']*\\' + + this.config.identifierSuffix + '|[^\\.]+)', 'g' + ); + this.wrappedIdentifierPartRegexp = new RegExp( + '^\\' + this.config.identifierPrefix + '.*\\' + this.config.identifierSuffix + '$' + ); +}; + +Dialect.prototype.config = { + identifierPrefix: '"', + identifierSuffix: '"', + defaultLogicalOperator: '$and', + defaultModifier: '$set' +}; + +Dialect.prototype._wrapIdentifier = function(name) { + if (this.builder.options.wrappedIdentifiers) { + var self = this; + var nameParts = name.match(this.identifierPartsRegexp); + + return _(nameParts).map(function(namePart) { + if (namePart !== '*' && !self.wrappedIdentifierPartRegexp.test(namePart)) { + namePart = self.config.identifierPrefix + namePart + self.config.identifierSuffix; + } + + return namePart; + }).join('.'); + } + + return name; +}; diff --git a/legacy/json-sql/lib/dialects/base/logicalOperators.js b/legacy/json-sql/lib/dialects/base/logicalOperators.js new file mode 100644 index 00000000..fb2879c1 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/logicalOperators.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('underscore'); + +function buildJoinOperator(conditions, operator) { + var isBracketsNeeded = conditions.length > 1; + var result = conditions.join(' ' + operator + ' '); + + if (result && isBracketsNeeded) result = '(' + result + ')'; + + return result; +} + +module.exports = function(dialect) { + dialect.logicalOperators.add('$and', function(conditions) { + return buildJoinOperator(conditions, 'and'); + }); + + dialect.logicalOperators.add('$or', function(conditions) { + return buildJoinOperator(conditions, 'or'); + }); + + dialect.logicalOperators.add('$not', function(conditions) { + var result = ''; + + if (_.isArray(conditions)) { + result = dialect.logicalOperators + .get(dialect.config.defaultLogicalOperator)(conditions); + } else if (_.isString(conditions)) { + result = conditions; + } + + if (result) result = 'not ' + result; + + return result; + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/modifiers.js b/legacy/json-sql/lib/dialects/base/modifiers.js new file mode 100644 index 00000000..3be42aa6 --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/modifiers.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + dialect.modifiers.add('$set', function(values) { + var self = this; + + return _(values).map(function(value, field) { + var placeholder = self._pushValue(value); + + return [self.dialect._wrapIdentifier(field), '=', placeholder].join(' '); + }).join(', '); + }); + + dialect.modifiers.add('$inc', function(values) { + var self = this; + + return _(values).map(function(value, field) { + var placeholder = self._pushValue(value); + field = self.dialect._wrapIdentifier(field); + + return [field, '=', field, '+', placeholder].join(' '); + }).join(', '); + }); + + dialect.modifiers.add('$dec', function(values) { + var self = this; + + return _(values).map(function(value, field) { + var placeholder = self._pushValue(value); + field = self.dialect._wrapIdentifier(field); + + return [field, '=', field, '-', placeholder].join(' '); + }).join(', '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/base/scheme.js b/legacy/json-sql/lib/dialects/base/scheme.js new file mode 100644 index 00000000..686e16ed --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/scheme.js @@ -0,0 +1,146 @@ +var SchemeParser = function () { +} + +var types = { + "Number": {syntax: "int"}, + "BigInt": {syntax: "bigint"}, + "SmallInt": {syntax: "smallint"}, + "String": {syntax: "varchar", length: true}, + "Text": {syntax: "text"}, + "Real": {syntax: "real"}, + "Boolean": {syntax: "boolean"}, + "Blob": {syntax: "blob"}, + "Binary": {syntax: "bytea"} +} + +// for future +var onDeleteTrigger = { + "set_null": "SET NULL", + "cascade": "CASCADE" +} + +function getType(field) { + var s = ""; + var type = types[field.type]; + + if (!type) { + throw new Error("Invalid type of field: " + field.type); + } + + s += type.syntax; + + if (type.length) { + if (!field.length || field.length <= 0) { + throw new Error("Field length can't be less or equal 0"); + } + + s += "(" + field.length + ")"; + } else if (type.default_length) { + s += "(" + type.default_length + ")"; + } + + return s; +} + +function foreignkeys(fields, keys) { + if (!keys || keys.length == 0) { + return ""; + } + + var s = ", "; + var names = []; + fields.forEach(function (field) { + names.push(field.name) + }); + + keys.forEach(function (key, i) { + if (!key.field) { + throw new Error("Provide field for foreign key"); + } + + if (names.indexOf(key.field) < 0) { + throw new Error("Not exists field to make foreign key: " + key.field); + } + + if (!key.table || key.table.trim().length == 0) { + throw new Error("Invalid reference table name"); + } + + if (!key.table_field || key.table_field.trim().length == 0) { + throw new Error("Invalid reference table filed"); + } + + s += "FOREIGN KEY (\"" + key.field + "\") REFERENCES " + key.table + "(\"" + key.table_field + "\")" + (key.on_delete ? " ON DELETE " + key.on_delete : ""); + if (i != keys.length - 1) { + s += ","; + } + }); + + return s; +} + +function parse(fields, fkeys) { + var sql_fields = ""; + var checkPrimaryKey = false; + var names = []; + + fields.forEach(function (field, i) { + if (!field.name) { + throw new Error("Name of field most be provided"); + } + + if (field.name.trim().length == 0) { + throw new Error("Name most contains characters"); + } + + if (names.indexOf(field.name) >= 0) { + throw new Error("Two parameters with same name: " + field.name); + } + + var line = this.dialect._wrapIdentifier(field.name) + " " + getType(field); + + if (field.not_null) { + line += " NOT NULL"; + } + + if (field.default !== undefined) { + var _type = typeof field.default; + var _default = field.default; + if (_type === "string") { + _default = "'" + field.default + "'"; + } + line += " default " + _default; + } + + if (field.unique) { + line += " UNIQUE"; + } + + + if (field.primary_key) { + if (checkPrimaryKey) { + throw new Error("Too much primary key '" + field.name + "' in table"); + } else { + checkPrimaryKey = true; + } + + line += " PRIMARY KEY"; + } + + sql_fields += line; + + names.push(field.name); + + if (i != fields.length - 1) { + sql_fields += ","; + } + }.bind(this)); + + sql_fields += foreignkeys(fields, fkeys); + return sql_fields; +} + +module.exports = { + parse: parse, + foreignkeys: foreignkeys +} diff --git a/legacy/json-sql/lib/dialects/base/templates.js b/legacy/json-sql/lib/dialects/base/templates.js new file mode 100644 index 00000000..81c60cdb --- /dev/null +++ b/legacy/json-sql/lib/dialects/base/templates.js @@ -0,0 +1,220 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + var availableSourceProps = ['table', 'query', 'select', 'expression']; + var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; + var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; + + // private templates + + dialect.templates.add('query', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + + dialect.templates.add('subQuery', { + pattern: '({queryBody})', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('create', { + pattern: 'create table if not exists {table}({tableFields})', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'tableFields'); + hasArrayProp(type, params, 'tableFields'); + } + }); + + dialect.templates.add('index', { + pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'name') + hasRequiredProp(type, params, 'indexOn'); + hasMinPropLength(type, params, 'name', 1); + hasMinPropLength(type, params, 'indexOn', 1); + } + }) + + + dialect.templates.add('queriesCombination', { + pattern: '{with} {queries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queries'); + hasArrayProp(type, params, 'queries'); + hasMinPropLength(type, params, 'queries', 2); + } + }); + + + dialect.templates.add('insertValues', { + pattern: '({fields}) values {fieldValues}', + validate: function(type, params) { + hasRequiredProp('values', params, 'fields'); + hasArrayProp('values', params, 'fields'); + hasMinPropLength('values', params, 'fields', 1); + + hasRequiredProp('values', params, 'fieldValues'); + hasArrayProp('values', params, 'fieldValues'); + hasMinPropLength('values', params, 'fieldValues', 1); + } + }); + + + dialect.templates.add('joinItem', { + pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', + validate: function(type, params) { + hasOneOfProps('join', params, availableSourceProps); + + if (params.type) { + hasStringProp('join', params, 'type'); + + var splitType = _(params.type.toLowerCase().split(' ')).compact(); + if (_.difference(splitType, availableJoinTypes).length) { + throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); + } + } + } + }); + + + dialect.templates.add('withItem', { + pattern: '{name} {fields} as {query} {select} {expression}', + validate: function(type, params) { + hasRequiredProp('with', params, 'name'); + hasOneOfProps('with', params, ['query', 'select', 'expression']); + } + }); + + + dialect.templates.add('fromItem', { + pattern: '{table} {query} {select} {expression}', + validate: function(type, params) { + hasOneOfProps('from', params, availableSourceProps); + } + }); + + + // public templates + + dialect.templates.add('select', { + pattern: '{with} select {distinct} {fields} ' + + 'from {from} {table} {query} {select} {expression} {alias} ' + + '{join} {condition} {group} {sort} {limit} {offset}', + defaults: { + fields: {} + }, + validate: function(type, params) { + hasOneOfProps(type, params, availableSourceProps); + } + }); + + + dialect.templates.add('insert', { + pattern: '{with} insert {or} into {table} {values} {returning} {condition}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('update', { + pattern: '{with} update {or} {table} {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'modifier'); + hasRequiredProp(type, params, 'table'); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('remove', { + pattern: '{with} delete from {table} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + } + }); + + + dialect.templates.add('union', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('except', dialect.templates.get('queriesCombination')); + + + // validation helpers + + function hasRequiredProp(type, params, propName) { + if (!params[propName]) { + throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); + } + } + + function hasObjectProp(type, params, propName) { + if (!_.isObject(params[propName])) { + throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); + } + } + + function hasArrayProp(type, params, propName) { + if (!_.isArray(params[propName])) { + throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); + } + } + + function hasStringProp(type, params, propName) { + if (!_.isString(params.type)) { + throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); + } + } + + function hasMinPropLength(type, params, propName, length) { + if (params[propName].length < length) { + throw new Error('`' + propName + '` property should not have length less than ' + length + + ' in `' + type + '` clause'); + } + } + + function hasOneOfProps(type, params, expectedPropNames) { + var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); + + if (!propNames.length) { + throw new Error('Neither `' + expectedPropNames.join('`, `') + + '` properties are not set in `' + type + '` clause'); + } + + if (propNames.length > 1) { + throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + + type + '` clause'); + } + } + + function matchesRegExpProp(type, params, propName, regExp) { + if (!params[propName].match(regExp)) { + throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + + type + '` clause'); + } + } +}; diff --git a/legacy/json-sql/lib/dialects/mssql/index.js b/legacy/json-sql/lib/dialects/mssql/index.js new file mode 100644 index 00000000..8374184f --- /dev/null +++ b/legacy/json-sql/lib/dialects/mssql/index.js @@ -0,0 +1,13 @@ +'use strict'; + +var BaseDialect = require('../base'); +var _ = require('underscore'); +var util = require('util'); + +var Dialect = module.exports = function(builder) { + BaseDialect.call(this, builder); +}; + +util.inherits(Dialect, BaseDialect); + +Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config, {}); diff --git a/legacy/json-sql/lib/dialects/postgresql/blocks.js b/legacy/json-sql/lib/dialects/postgresql/blocks.js new file mode 100644 index 00000000..e6f0066d --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/blocks.js @@ -0,0 +1,33 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + dialect.blocks.add('offset', function(params) { + var limit = ''; + + if (typeof params.limit === 'undefined') { + limit = this.buildBlock('limit', {limit: -1}) + ' '; + } + + return limit + 'offset ' + this._pushValue(params.offset); + }); + + dialect.blocks.add('unionqueries', function(params) { + var self = this; + + return _(params.unionqueries).map(function(query) { + return self.buildTemplate('subUnionQuery', {queryBody: query}); + }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); + }); + + dialect.blocks.add('conflictFields', function(params) { + var self = this; + + var fields = _(params.conflictFields).map(function(field) { + return self.dialect._wrapIdentifier(field); + }); + + return '(' + fields.join(',') + ')'; + }); +}; diff --git a/legacy/json-sql/lib/dialects/postgresql/conditions.js b/legacy/json-sql/lib/dialects/postgresql/conditions.js new file mode 100644 index 00000000..dddba22a --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/conditions.js @@ -0,0 +1,87 @@ +'use strict'; + +var _ = require('underscore'); + +var buildJsonInCondition = function(builder, field, operator, value) { + field = builder.dialect._wrapIdentifier(field); + + var placeholder; + try { + placeholder = builder.buildBlock('field', value); + } catch (e) { + placeholder = builder._pushValue(JSON.stringify(value)); + } + + return [field, operator, placeholder].join(' '); +}; + +var buildJsonHasCondition = function(builder, field, operator, value) { + field = builder.dialect._wrapIdentifier(field); + + var placeholder; + if (_(value).isArray()) { + placeholder = 'array[' + _(value).map(function(item) { + return builder._pushValue(item); + }).join(', ') + ']'; + } else { + placeholder = builder.buildBlock('field', value); + } + + return [field, operator, placeholder].join(' '); +}; + +module.exports = function(dialect) { + dialect.conditions.add('$jsonContains', function(field, operator, value) { + return buildJsonInCondition(this, field, '@>', value); + }); + + dialect.conditions.add('$jsonIn', function(field, operator, value) { + return buildJsonInCondition(this, field, '<@', value); + }); + + dialect.conditions.add('$jsonHas', function(field, operator, value) { + field = this.dialect._wrapIdentifier(field); + + var placeholder = value; + if (_(placeholder).isObject()) { + placeholder = this.buildBlock('field', placeholder); + } else { + placeholder = this._pushValue(value.toString()); + } + + return [field, '?', placeholder].join(' '); + }); + + dialect.conditions.add('$jsonHasAny', function(field, operator, value) { + return buildJsonHasCondition(this, field, '?|', value); + }); + + dialect.conditions.add('$jsonHasAll', function(field, operator, value) { + return buildJsonHasCondition(this, field, '?&', value); + }); + + dialect.conditions.add('$upper', function(field, operator, value) { + return [ + 'upper(' + this.dialect._wrapIdentifier(field) + ')', + '=', + 'upper(' + this._pushValue(value[1]) + ')' + ].join(' '); + }); + + dialect.conditions.add('$lower', function(field, operator, value) { + return [ + 'lower(' + this.dialect._wrapIdentifier(field) + ')', + '=', + 'lower(' + this._pushValue(value[1]) + ')' + ].join(' '); + }); + + dialect.conditions.add('$decode', function (field, operator, value) { + return [ + this.dialect._wrapIdentifier(field), + '=', + 'decode(' + this._pushValue(value[1]) + ',', + this._pushValue(value[2]) + ')' + ].join(' '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/postgresql/index.js b/legacy/json-sql/lib/dialects/postgresql/index.js new file mode 100644 index 00000000..755dc00d --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/index.js @@ -0,0 +1,39 @@ +'use strict'; + +var BaseDialect = require('../base'); +var _ = require('underscore'); +var util = require('util'); +var blocksInit = require('./blocks'); +var conditionsInit = require('./conditions'); +var templatesInit = require('./templates'); + +var Dialect = module.exports = function (builder) { + BaseDialect.call(this, builder); + blocksInit(this) + conditionsInit(this); + templatesInit(this); +}; + +Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); + +util.inherits(Dialect, BaseDialect); + +Dialect.prototype.config = _({ + jsonSeparatorRegexp: /->>?/g +}).extend(BaseDialect.prototype.config); + +Dialect.prototype._wrapIdentifier = function(name) { + // split by json separator + var nameParts = name.split(this.config.jsonSeparatorRegexp); + var separators = name.match(this.config.jsonSeparatorRegexp); + + // wrap base identifier + var identifier = BaseDialect.prototype._wrapIdentifier.call(this, nameParts[0]); + + // wrap all json identifier and join them with separators + identifier += _(separators).reduce(function(memo, separator, index) { + return memo + separator + '\'' + nameParts[index + 1] + '\''; + }, ''); + + return identifier; +}; diff --git a/legacy/json-sql/lib/dialects/postgresql/templates.js b/legacy/json-sql/lib/dialects/postgresql/templates.js new file mode 100644 index 00000000..297ed8b0 --- /dev/null +++ b/legacy/json-sql/lib/dialects/postgresql/templates.js @@ -0,0 +1,269 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + var availableSourceProps = ['table', 'query', 'select', 'expression']; + var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; + var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; + + // private templates + + dialect.templates.add('query', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subQuery', { + pattern: '({queryBody})', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subUnionQuery', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('create', { + pattern: 'create table if not exists {table}({tableFields})', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'tableFields'); + hasArrayProp(type, params, 'tableFields'); + } + }); + + dialect.templates.add('index', { + pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'name') + hasRequiredProp(type, params, 'indexOn'); + hasMinPropLength(type, params, 'name', 1); + hasMinPropLength(type, params, 'indexOn', 1); + } + }) + + + dialect.templates.add('queriesCombination', { + pattern: '{with} {queries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queries'); + hasArrayProp(type, params, 'queries'); + hasMinPropLength(type, params, 'queries', 2); + } + }); + + dialect.templates.add('queriesUnionCombination', { + pattern: '{with} {unionqueries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'unionqueries'); + hasArrayProp(type, params, 'unionqueries'); + hasMinPropLength(type, params, 'unionqueries', 2); + } + }); + + + dialect.templates.add('insertValues', { + pattern: '({fields}) values {fieldValues}', + validate: function(type, params) { + hasRequiredProp('values', params, 'fields'); + hasArrayProp('values', params, 'fields'); + hasMinPropLength('values', params, 'fields', 1); + + hasRequiredProp('values', params, 'fieldValues'); + hasArrayProp('values', params, 'fieldValues'); + hasMinPropLength('values', params, 'fieldValues', 1); + } + }); + + + dialect.templates.add('joinItem', { + pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', + validate: function(type, params) { + hasOneOfProps('join', params, availableSourceProps); + + if (params.type) { + hasStringProp('join', params, 'type'); + + var splitType = _(params.type.toLowerCase().split(' ')).compact(); + if (_.difference(splitType, availableJoinTypes).length) { + throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); + } + } + } + }); + + + dialect.templates.add('withItem', { + pattern: '{name} {fields} as {query} {select} {expression}', + validate: function(type, params) { + hasRequiredProp('with', params, 'name'); + hasOneOfProps('with', params, ['query', 'select', 'expression']); + } + }); + + + dialect.templates.add('fromItem', { + pattern: '{table} {query} {select} {expression}', + validate: function(type, params) { + hasOneOfProps('from', params, availableSourceProps); + } + }); + + + // public templates + + dialect.templates.add('select', { + pattern: '{with} select {distinct} {fields} ' + + 'from {from} {table} {query} {select} {expression} {alias} ' + + '{join} {condition} {group} {sort} {limit} {offset}', + defaults: { + fields: {} + }, + validate: function(type, params) { + hasOneOfProps(type, params, availableSourceProps); + } + }); + + + dialect.templates.add('insert', { + pattern: '{with} insert {or} into {table} {values} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('insertornothing', { + pattern: '{with} insert {or} into {table} {values} on conflict do nothing {returning} {condition}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('insertorupdate', { + pattern: '{with} insert {or} into {table} {values} on conflict {conflictFields} do update {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasRequiredProp('conflictFields', params, 'conflictFields'); + hasArrayProp('conflictFields', params, 'conflictFields'); + hasMinPropLength('conflictFields', params, 'conflictFields', 1); + hasRequiredProp(type, params, 'modifier'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('update', { + pattern: '{with} update {or} {table} {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'modifier'); + hasRequiredProp(type, params, 'table'); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('remove', { + pattern: '{with} delete from {table} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + } + }); + + + dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); + + + dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('except', dialect.templates.get('queriesCombination')); + + + // validation helpers + + function hasRequiredProp(type, params, propName) { + if (!params[propName]) { + throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); + } + } + + function hasObjectProp(type, params, propName) { + if (!_.isObject(params[propName])) { + throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); + } + } + + function hasArrayProp(type, params, propName) { + if (!_.isArray(params[propName])) { + throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); + } + } + + function hasStringProp(type, params, propName) { + if (!_.isString(params.type)) { + throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); + } + } + + function hasMinPropLength(type, params, propName, length) { + if (params[propName].length < length) { + throw new Error('`' + propName + '` property should not have length less than ' + length + + ' in `' + type + '` clause'); + } + } + + function hasOneOfProps(type, params, expectedPropNames) { + var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); + + if (!propNames.length) { + throw new Error('Neither `' + expectedPropNames.join('`, `') + + '` properties are not set in `' + type + '` clause'); + } + + if (propNames.length > 1) { + throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + + type + '` clause'); + } + } + + function matchesRegExpProp(type, params, propName, regExp) { + if (!params[propName].match(regExp)) { + throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + + type + '` clause'); + } + } +}; diff --git a/legacy/json-sql/lib/dialects/sqlite/blocks.js b/legacy/json-sql/lib/dialects/sqlite/blocks.js new file mode 100644 index 00000000..c3531c2a --- /dev/null +++ b/legacy/json-sql/lib/dialects/sqlite/blocks.js @@ -0,0 +1,23 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + dialect.blocks.add('offset', function(params) { + var limit = ''; + + if (typeof params.limit === 'undefined') { + limit = this.buildBlock('limit', {limit: -1}) + ' '; + } + + return limit + 'offset ' + this._pushValue(params.offset); + }); + + dialect.blocks.add('unionqueries', function(params) { + var self = this; + + return _(params.unionqueries).map(function(query) { + return self.buildTemplate('subUnionQuery', {queryBody: query}); + }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); + }); +}; diff --git a/legacy/json-sql/lib/dialects/sqlite/index.js b/legacy/json-sql/lib/dialects/sqlite/index.js new file mode 100644 index 00000000..72549400 --- /dev/null +++ b/legacy/json-sql/lib/dialects/sqlite/index.js @@ -0,0 +1,17 @@ +'use strict'; + +var BaseDialect = require('../base'); +var _ = require('underscore'); +var util = require('util'); +var blocksInit = require('./blocks'); +var templatesInit = require('./templates'); + +var Dialect = module.exports = function (builder) { + BaseDialect.call(this, builder); + blocksInit(this); + templatesInit(this); +}; + +util.inherits(Dialect, BaseDialect); + +Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config); diff --git a/legacy/json-sql/lib/dialects/sqlite/templates.js b/legacy/json-sql/lib/dialects/sqlite/templates.js new file mode 100644 index 00000000..ac6e382f --- /dev/null +++ b/legacy/json-sql/lib/dialects/sqlite/templates.js @@ -0,0 +1,236 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = function(dialect) { + var availableSourceProps = ['table', 'query', 'select', 'expression']; + var availableJoinTypes = ['natural', 'cross', 'inner', 'outer', 'left', 'right', 'full', 'self']; + var orRegExp = /^(rollback|abort|replace|fail|ignore)$/i; + + // private templates + + dialect.templates.add('query', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subQuery', { + pattern: '({queryBody})', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('subUnionQuery', { + pattern: '{queryBody}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queryBody'); + hasObjectProp(type, params, 'queryBody'); + } + }); + + dialect.templates.add('create', { + pattern: 'create table if not exists {table}({tableFields})', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'tableFields'); + hasArrayProp(type, params, 'tableFields'); + } + }); + + dialect.templates.add('index', { + pattern: 'create index if not exists {name} ON {table}({indexOn}) {condition}', + validate: function (type, params) { + hasRequiredProp(type, params, 'table'); + hasRequiredProp(type, params, 'name') + hasRequiredProp(type, params, 'indexOn'); + hasMinPropLength(type, params, 'name', 1); + hasMinPropLength(type, params, 'indexOn', 1); + } + }) + + + dialect.templates.add('queriesCombination', { + pattern: '{with} {queries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'queries'); + hasArrayProp(type, params, 'queries'); + hasMinPropLength(type, params, 'queries', 2); + } + }); + + dialect.templates.add('queriesUnionCombination', { + pattern: '{with} {unionqueries} {sort} {limit} {offset}', + validate: function(type, params) { + hasRequiredProp(type, params, 'unionqueries'); + hasArrayProp(type, params, 'unionqueries'); + hasMinPropLength(type, params, 'unionqueries', 2); + } + }); + + + dialect.templates.add('insertValues', { + pattern: '({fields}) values {fieldValues}', + validate: function(type, params) { + hasRequiredProp('values', params, 'fields'); + hasArrayProp('values', params, 'fields'); + hasMinPropLength('values', params, 'fields', 1); + + hasRequiredProp('values', params, 'fieldValues'); + hasArrayProp('values', params, 'fieldValues'); + hasMinPropLength('values', params, 'fieldValues', 1); + } + }); + + + dialect.templates.add('joinItem', { + pattern: '{type} join {table} {query} {select} {expression} {alias} {on}', + validate: function(type, params) { + hasOneOfProps('join', params, availableSourceProps); + + if (params.type) { + hasStringProp('join', params, 'type'); + + var splitType = _(params.type.toLowerCase().split(' ')).compact(); + if (_.difference(splitType, availableJoinTypes).length) { + throw new Error('Invalid `type` property value "' + params.type + '" in `join` clause'); + } + } + } + }); + + + dialect.templates.add('withItem', { + pattern: '{name} {fields} as {query} {select} {expression}', + validate: function(type, params) { + hasRequiredProp('with', params, 'name'); + hasOneOfProps('with', params, ['query', 'select', 'expression']); + } + }); + + + dialect.templates.add('fromItem', { + pattern: '{table} {query} {select} {expression}', + validate: function(type, params) { + hasOneOfProps('from', params, availableSourceProps); + } + }); + + + // public templates + + dialect.templates.add('select', { + pattern: '{with} select {distinct} {fields} ' + + 'from {from} {table} {query} {select} {expression} {alias} ' + + '{join} {condition} {group} {sort} {limit} {offset}', + defaults: { + fields: {} + }, + validate: function(type, params) { + hasOneOfProps(type, params, availableSourceProps); + } + }); + + + dialect.templates.add('insert', { + pattern: '{with} insert {or} into {table} {values} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'values'); + hasObjectProp(type, params, 'values'); + hasOneOfProps(type, params, availableSourceProps); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('update', { + pattern: '{with} update {or} {table} {modifier} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'modifier'); + hasRequiredProp(type, params, 'table'); + if (params.or) { + hasStringProp(type, params, 'or'); + matchesRegExpProp(type, params, 'or', orRegExp); + } + } + }); + + + dialect.templates.add('remove', { + pattern: '{with} delete from {table} {condition} {returning}', + validate: function(type, params) { + hasRequiredProp(type, params, 'table'); + } + }); + + + dialect.templates.add('union', dialect.templates.get('queriesUnionCombination')); + + + dialect.templates.add('intersect', dialect.templates.get('queriesCombination')); + + + dialect.templates.add('except', dialect.templates.get('queriesCombination')); + + + // validation helpers + + function hasRequiredProp(type, params, propName) { + if (!params[propName]) { + throw new Error('`' + propName + '` property is not set in `' + type + '` clause'); + } + } + + function hasObjectProp(type, params, propName) { + if (!_.isObject(params[propName])) { + throw new Error('`' + propName + '` property should be an object in `' + type + '` clause'); + } + } + + function hasArrayProp(type, params, propName) { + if (!_.isArray(params[propName])) { + throw new Error('`' + propName + '` property should be an array in `' + type + '` clause'); + } + } + + function hasStringProp(type, params, propName) { + if (!_.isString(params.type)) { + throw new Error('`' + propName + '` property should be a string in `' + type + '` clause'); + } + } + + function hasMinPropLength(type, params, propName, length) { + if (params[propName].length < length) { + throw new Error('`' + propName + '` property should not have length less than ' + length + + ' in `' + type + '` clause'); + } + } + + function hasOneOfProps(type, params, expectedPropNames) { + var propNames = _(params).chain().keys().intersection(expectedPropNames).value(); + + if (!propNames.length) { + throw new Error('Neither `' + expectedPropNames.join('`, `') + + '` properties are not set in `' + type + '` clause'); + } + + if (propNames.length > 1) { + throw new Error('Wrong using `' + propNames.join('`, `') + '` properties together in `' + + type + '` clause'); + } + } + + function matchesRegExpProp(type, params, propName, regExp) { + if (!params[propName].match(regExp)) { + throw new Error('Invalid `' + propName + '` property value "' + params[propName] + '" in `' + + type + '` clause'); + } + } +}; diff --git a/legacy/json-sql/lib/index.js b/legacy/json-sql/lib/index.js new file mode 100644 index 00000000..55bbe02a --- /dev/null +++ b/legacy/json-sql/lib/index.js @@ -0,0 +1,8 @@ +'use strict'; + +var Builder = require('./builder'); + +module.exports = function(params) { + return new Builder(params); +}; +module.exports.Builder = Builder; diff --git a/legacy/json-sql/lib/valuesStore.js b/legacy/json-sql/lib/valuesStore.js new file mode 100644 index 00000000..29b7d395 --- /dev/null +++ b/legacy/json-sql/lib/valuesStore.js @@ -0,0 +1,31 @@ +'use strict'; + +var _ = require('underscore'); + +module.exports = ValuesStore; + +function ValuesStore(options) { + options = options || {}; + this.context = options.context || null; + this._values = options.values || {}; +} + +ValuesStore.prototype.add = ValuesStore.prototype.set = function(name, value) { + if (_.isFunction(value) && this.context) { + value = _(value).bind(this.context); + } + + this._values[name] = value; +}; + +ValuesStore.prototype.get = function(name) { + return this._values[name] || null; +}; + +ValuesStore.prototype.remove = function(name) { + delete this._values[name]; +}; + +ValuesStore.prototype.has = function(name) { + return this._values.hasOwnProperty(name); +}; diff --git a/legacy/json-sql/package.json b/legacy/json-sql/package.json new file mode 100644 index 00000000..73c407e3 --- /dev/null +++ b/legacy/json-sql/package.json @@ -0,0 +1,44 @@ +{ + "name": "json-sql", + "description": "Node.js json to sql query mapper", + "version": "0.2.6", + "author": { + "name": "Artem Zhukov", + "email": "artzhuchka@gmail.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/2do2go/json-sql.git" + }, + "keywords": [ + "json-sql", + "json", + "sql", + "odm", + "mapper", + "db", + "database" + ], + "dependencies": { + "underscore": "=1.8.3" + }, + "devDependencies": { + "jshint": "=2.9.2", + "chai": "=3.5.0", + "gulp": "=3.9.1", + "gulp-jshint": "=2.0.0", + "gulp-mocha": "=2.2.0" + }, + "main": "./lib/index", + "readme": "# JSON-SQL\n\nLibrary for mapping mongo-style query objects to SQL queries.\n\n## Quick Start\n\nInstall it with NPM or add it to your package.json:\n\n``` bash\n$ npm install json-sql\n```\n\nThen:\n\n``` js\nvar jsonSql = require('json-sql')();\n\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tfields: ['name', 'age'],\n\tcondition: {name: 'Max', id: 6}\n});\n\nsql.query\n// sql string:\n// select name, age from users where name = $p1 && id = 6;\n\nsql.values\n// hash of values:\n// { p1: 'Max' }\n```\n\n## Documentation\n\nDocumentation is available at the [./docs directory](./docs).\n\n## Examples\n\n__Select with join:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'select',\n\ttable: 'users',\n\tjoin: {\n\t\tdocuments: {\n\t\t\ton: {'user.id': 'documents.userId'}\n\t\t}\n\t}\n});\n\nsql.query\n// select * from users join documents on user.id = documents.userId;\n\nsql.values\n// {}\n```\n\n__Insert:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'insert',\n\ttable: 'users',\n\tvalues: {\n\t\tname: 'John',\n\t\tlastname: 'Snow',\n\t\tage: 24,\n\t\tgender: 'male'\n\t}\n});\n\nsql.query\n// insert into users (name, lastname, age, gender) values ($p1, $p2, 24, $p3);\n\nsql.values\n// { p1: 'John', p2: 'Snow', p3: 'male' }\n```\n\n__Update:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'update',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t},\n\tmodifier: {\n\t\trole: 'admin'\n\t\tage: 33\n\t}\n});\n\nsql.query\n// update users set role = $p1, age = 33 where id = 5;\n\nsql.values\n// { p1: 'admin' }\n```\n\n__Remove:__\n\n``` js\nvar sql = jsonSql.build({\n\ttype: 'remove',\n\ttable: 'users',\n\tcondition: {\n\t\tid: 5\n\t}\n});\n\nsql.query\n// delete from users where id = 5;\n\nsql.values\n// {}\n```\n\nFor more examples, take a look at the [./docs directory](./docs) or [./tests directory](./tests).\n\n## Tests\n\nClone repository from github, `cd` into cloned dir and install dev dependencies:\n\n``` bash\n$ npm install\n```\n\nThen run tests with command:\n\n``` bash\n$ gulp test\n```\n\nOr run tests coverage with command:\n\n``` bash\n$ gulp coverage\n```\n\n## License\n\n[MIT](./LICENSE)\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/2do2go/json-sql/issues" + }, + "homepage": "https://github.com/2do2go/json-sql#readme", + "_id": "json-sql@0.2.4", + "_shasum": "df8c5f345b72f421c6fc7da57116c47cae5b5216", + "_resolved": "https://github.com/LiskHQ/json-sql/tarball/master", + "_from": "https://github.com/LiskHQ/json-sql/tarball/master" +} diff --git a/legacy/json-sql/tests/0_base.js b/legacy/json-sql/tests/0_base.js new file mode 100644 index 00000000..b46774be --- /dev/null +++ b/legacy/json-sql/tests/0_base.js @@ -0,0 +1,304 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var Builder = require('../lib').Builder; +var expect = require('chai').expect; + +describe('Builder', function() { + it('should have fields', function() { + expect(jsonSql).to.be.ok; + expect(jsonSql).to.be.an.instanceof(Builder); + + expect(jsonSql.dialect).to.be.ok; + + expect(jsonSql._query).to.be.equal(''); + expect(jsonSql._values).to.be.eql({}); + + expect(jsonSql.dialect.blocks).to.be.ok; + expect(jsonSql.dialect.templates).to.be.ok; + expect(jsonSql.dialect.conditions).to.be.ok; + expect(jsonSql.dialect.modifiers).to.be.ok; + expect(jsonSql.dialect.logicalOperators).to.be.ok; + }); + + it('should throw error with wrong `type` property', function() { + expect(function() { + jsonSql.build({ + type: 'wrong' + }); + }).to.throw('Unknown template type "wrong"'); + }); + + it('should throw error without `table`, `query` and `select` properties', function() { + expect(function() { + jsonSql.build({}); + }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + + 'are not set in `select` clause'); + }); + + it('should throw error with both `table` and `select` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + select: {table: 'payments'} + }); + }).to.throw('Wrong using `table`, `select` properties together in `select` clause'); + }); + + it('should throw error with both `table` and `query` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + query: {table: 'payments'} + }); + }).to.throw('Wrong using `table`, `query` properties together in `select` clause'); + }); + + it('should throw error with both `query` and `select` properties', function() { + expect(function() { + jsonSql.build({ + query: {table: 'payments'}, + select: {table: 'payments'} + }); + }).to.throw('Wrong using `query`, `select` properties together in `select` clause'); + }); + + it('should throw error without `name` property in `with` clause', function() { + expect(function() { + jsonSql.build({ + 'with': [{ + select: { + table: 'payments' + } + }], + table: 'users' + }); + }).to.throw('`name` property is not set in `with` clause'); + }); + + it('should throw error without `query` and `select` properties in `with` clause', function() { + expect(function() { + jsonSql.build({ + 'with': [{ + name: 'payments' + }], + table: 'users' + }); + }).to.throw('Neither `query`, `select`, `expression` properties are not set in `with` clause'); + }); + + it('should throw error with both `query` and `select` properties in `with` clause', function() { + expect(function() { + jsonSql.build({ + 'with': [{ + name: 'payments', + query: {table: 'table1'}, + select: {table: 'table2'} + }], + table: 'users' + }); + }).to.throw('Wrong using `query`, `select` properties together in `with` clause'); + }); + + it('should be ok with array in `with` clause', function() { + var result = jsonSql.build({ + 'with': [{ + name: 'payments', + select: { + table: 'payments' + } + }], + table: 'users' + }); + + expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + + '"users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object in `with` clause', function() { + var result = jsonSql.build({ + 'with': { + payments: { + select: { + table: 'payments' + } + } + }, + table: 'users' + }); + + expect(result.query).to.be.equal('with "payments" as (select * from "payments") select * from ' + + '"users";'); + expect(result.values).to.be.eql({}); + }); + + it('should create array values with option `namedValues` = false', function() { + jsonSql.configure({ + namedValues: false + }); + + expect(jsonSql._values).to.be.eql([]); + + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); + expect(result.values).to.be.eql(['John']); + }); + + it('should use prefix `@` for values with option `valuesPrefix` = @', function() { + jsonSql.configure({ + valuesPrefix: '@' + }); + + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); + expect(result.values).to.be.eql({p1: 'John'}); + }); + + it('should return prefixed values with method `prefixValues`', function() { + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); + expect(result.values).to.be.eql({p1: 'John'}); + expect(result.prefixValues()).to.be.eql({'@{p1}': 'John'}); + }); + + it('should return array values with method `getValuesArray`', function() { + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = @{p1};'); + expect(result.values).to.be.eql({p1: 'John'}); + expect(result.getValuesArray()).to.be.eql(['John']); + }); + + it('should return object values with method `getValuesObject`', function() { + jsonSql.configure({ + valuesPrefix: '$', + namedValues: false + }); + + expect(jsonSql._values).to.be.eql([]); + + var result = jsonSql.build({ + table: 'users', + condition: {name: 'John'} + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${1};'); + expect(result.values).to.be.eql(['John']); + expect(result.prefixValues()).to.be.eql({'${1}': 'John'}); + expect(result.getValuesObject()).to.be.eql({1: 'John'}); + }); + + it('should create query without values with option `separatedValues` = false', function() { + jsonSql.configure({ + separatedValues: false + }); + + expect(jsonSql._values).to.not.be.ok; + expect(jsonSql._placeholderId).to.not.be.ok; + + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: {name: 'John', surname: 'Doe'} + }); + + expect(result.query).to.be.equal('insert into "users" ("name", "surname") values ' + + '(\'John\', \'Doe\');'); + expect(result.values).to.not.be.ok; + }); + + it('should create query without wrapping identifiers with option `wrappedIdentifiers` = false', + function() { + jsonSql.configure({ + wrappedIdentifiers: false + }); + + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: {name: 'John'} + }); + + expect(result.query).to.be.equal('insert into users (name) values (${p1});'); + } + ); + + it('shouldn\'t wrap identifiers that already wrapped', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + type: 'insert', + table: '"users"', + values: { + '"name"': 'John', + '"users"."age"': 22 + } + }); + + expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); + }); + + it('shouldn\'t split identifiers by dots inside quotes', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + type: 'insert', + table: '"users"', + values: { + '"users.age"': 22 + } + }); + + expect(result.query).to.be.equal('insert into "users" ("users.age") values (22);'); + }); + + it('shouldn\'t wrap asterisk identifier parts', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + fields: ['users.*'], + table: '"users"' + }); + + expect(result.query).to.be.equal('select "users".* from "users";'); + }); + + it('should split identifiers by dots and wrap each part', function() { + jsonSql.configure({ + wrappedIdentifiers: true + }); + + var result = jsonSql.build({ + type: 'insert', + table: '"users"', + values: { + 'name': 'John', + 'users.age': 22 + } + }); + + expect(result.query).to.be.equal('insert into "users" ("name", "users"."age") values (${p1}, 22);'); + }); +}); diff --git a/legacy/json-sql/tests/1_select.js b/legacy/json-sql/tests/1_select.js new file mode 100644 index 00000000..087a1afb --- /dev/null +++ b/legacy/json-sql/tests/1_select.js @@ -0,0 +1,1222 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Select', function() { + describe('type', function() { + it('should be ok without `type` property', function() { + var result = jsonSql.build({ + table: 'users' + }); + + expect(result.query).to.be.equal('select * from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with "select" value', function() { + var result = jsonSql.build({ + type: 'select', + table: 'users' + }); + + expect(result.query).to.be.equal('select * from "users";'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('distinct', function() { + it('should be ok with true value', function() { + var result = jsonSql.build({ + table: 'users', + distinct: true + }); + + expect(result.query).to.be.equal('select distinct * from "users";'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('fields', function() { + it('should be ok with string array', function() { + var result = jsonSql.build({ + table: 'users', + fields: ['name', 'type'] + }); + + expect(result.query).to.be.equal('select "name", "type" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`name`: `alias`, ...)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {userAge: 'age', userScore: 'score'} + }); + + expect(result.query).to.be.equal('select "userAge" as "age", "userScore" as "score" from ' + + '"users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array of objects(`name`: `alias`, ...)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{userAge: 'age'}] + }); + + expect(result.query).to.be.equal('select "userAge" as "age" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'address'}] + }); + + expect(result.query).to.be.equal('select "address" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `table`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'score', table: 'users'}] + }); + + expect(result.query).to.be.equal('select "users"."score" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `alias`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'zoneName', alias: 'zone'}] + }); + + expect(result.query).to.be.equal('select "zoneName" as "zone" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `table`, `alias`) array', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{field: 'zoneName', table: 'users', alias: 'zone'}] + }); + + expect(result.query).to.be.equal('select "users"."zoneName" as "zone" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`table`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {score: {table: 'users'}} + }); + + expect(result.query).to.be.equal('select "users"."score" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {zone: {field: 'zone_1', alias: 'zone'}} + }); + + expect(result.query).to.be.equal('select "zone_1" as "zone" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`table`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {score: {table: 'users', alias: 's'}} + }); + + expect(result.query).to.be.equal('select "users"."score" as "s" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`field`, `table`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: {name: {field: 'name_1', table: 'users', alias: 'name_2'}} + }); + + expect(result.query).to.be.equal('select "users"."name_1" as "name_2" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: 'count(*)' + }] + }); + + expect(result.query).to.be.equal('select count(*) from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: 'count(*)', + alias: 'count' + }] + }); + + expect(result.query).to.be.equal('select count(*) as "count" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`, `field`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: 'sum', + field: 'income', + alias: 'sum' + }] + }); + + expect(result.query).to.be.equal('select sum("income") as "sum" from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object(`expression`[], `field`, `alias`)', function() { + var result = jsonSql.build({ + table: 'users', + fields: [{ + expression: ['abs', 'sum'], + field: 'income', + alias: 'sum' + }] + }); + + expect(result.query).to.be.equal('select abs(sum("income")) as "sum" from "users";'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('alias', function() { + it('should be ok with string `alias` property', function() { + var result = jsonSql.build({ + table: 'users', + alias: 'u' + }); + + expect(result.query).to.be.equal('select * from "users" as "u";'); + expect(result.values).to.be.eql({}); + }); + + it('should throw error if object `alias` does not have `name` property', function() { + expect(function() { + jsonSql.build({ + table: 'users', + alias: {} + }); + }).to.throw('Alias `name` property is required'); + }); + + it('should be ok with object `alias`(`name`) property', function() { + var result = jsonSql.build({ + table: 'users', + alias: { + name: 'u' + } + }); + + expect(result.query).to.be.equal('select * from "users" as "u";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object `alias`(`name`, `columns`) property', function() { + var result = jsonSql.build({ + table: 'users', + alias: { + name: 'u', + columns: ['a', 'b'] + } + }); + + expect(result.query).to.be.equal('select * from "users" as "u"("a", "b");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('query', function() { + it('should be ok with `query` property', function() { + var result = jsonSql.build({ + query: { + type: 'select', + table: 't' + } + }); + + expect(result.query).to.be.equal('select * from (select * from "t");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('select', function() { + it('should be ok with `select` property', function() { + var result = jsonSql.build({ + select: { + table: 't' + } + }); + + expect(result.query).to.be.equal('select * from (select * from "t");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('expression', function() { + it('should be ok with `expression` property', function() { + var result = jsonSql.build({ + expression: 'function()' + }); + + expect(result.query).to.be.equal('select * from function();'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('join', function() { + it('should throw error without `table`, `query` and `select` properties', + function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{}] + }); + }).to.throw('Neither `table`, `query`, `select`, `expression` properties ' + + 'are not set in `join` clause'); + } + ); + + it('should throw error with both `table` and `select` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + table: 'a', + select: {table: 'b'} + }] + }); + }).to.throw('Wrong using `table`, `select` properties together in `join` clause'); + }); + + it('should throw error with both `table` and `query` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + table: 'a', + query: {table: 'b'} + }] + }); + }).to.throw('Wrong using `table`, `query` properties together in `join` clause'); + }); + + it('should throw error with both `query` and `select` properties', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + query: 'a', + select: {table: 'b'} + }] + }); + }).to.throw('Wrong using `query`, `select` properties together in `join` clause'); + }); + + it('should throw error with wrong `type` property', function() { + expect(function() { + jsonSql.build({ + table: 'users', + join: [{ + type: 'wrong', + table: 'payments' + }] + }); + }).to.throw('Invalid `type` property value "wrong" in `join` clause'); + }); + + it('should be ok with correct `type` property', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + type: 'left outer', + table: 'payments' + }] + }); + + expect(result.query).to.be.equal('select * from "users" left outer join "payments";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array `join`', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + table: 'payments' + }] + }); + + expect(result.query).to.be.equal('select * from "users" join "payments";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object `join`', function() { + var result = jsonSql.build({ + table: 'users', + join: { + payments: {} + } + }); + + expect(result.query).to.be.equal('select * from "users" join "payments";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `on` property', function() { + var result = jsonSql.build({ + table: 'users', + join: { + payments: { + on: {'users.name': 'payments.name'} + } + } + }); + + expect(result.query).to.be.equal('select * from "users" join "payments" on "users"."name" = ' + + '"payments"."name";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `query` property', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + query: { + table: 'payments' + }, + on: {'users.name': 'payments.name'} + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'join (select * from "payments") ' + + 'on "users"."name" = "payments"."name";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `select` property', function() { + var result = jsonSql.build({ + table: 'users', + join: [{ + select: { + table: 'payments' + }, + on: {'users.name': 'payments.name'} + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'join (select * from "payments") ' + + 'on "users"."name" = "payments"."name";' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('condition', function() { + describe('compare operators', function() { + it('should throw error with wrong operator', function() { + expect(function() { + jsonSql.build({ + table: 'users', + condition: { + name: {$wrong: 'John'} + } + }); + }).to.throw('Unknown operator "$wrong"'); + }); + + it('should be ok with default operator(=)', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: 'John' + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$eq` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$eq: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$ne` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$ne: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" != ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$gt` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$gt: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" > ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$lt` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$lt: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" < ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$gte` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$gte: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" >= ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$lte` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$lte: 'John'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" <= ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with `$is` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$is: null} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$isnot` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$isnot: null} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$like` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$like: 'John%'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" like ${p1};'); + expect(result.values).to.be.eql({ + p1: 'John%' + }); + }); + + it('should be ok with `$null`:true operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$null: true} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$null`:false operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$null: false} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" is not null;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$field` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$field: 'name_2'} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object `$field` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: {$field: {field: 'name_2'}} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = "name_2";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: [12, 13, 14]} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (12, 13, 14);'); + expect(result.values).to.be.eql({}); + }); + + it('should add `null` value with empty array in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: []} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (null);'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$nin` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$nin: [12, 13, 14]} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" not in (12, 13, 14);'); + expect(result.values).to.be.eql({}); + }); + + it('should add `null` value with empty array in `$nin` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$nin: []} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" not in (null);'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object subquery in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: { + table: 'test' + }} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + + '"test");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `query` subquery in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: { + query: { + table: 'test' + } + }} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + + '(select * from "test"));'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `select` subquery in `$in` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$in: { + select: { + table: 'test' + } + }} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" in (select * from ' + + '(select * from "test"));'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$between` operator', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: {$between: [12, 14]} + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" between 12 and 14;'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('logical operators', function() { + it('should throw error with wrong logical operator', function() { + expect(function() { + jsonSql.build({ + table: 'users', + condition: { + $wrong: [ + {name: 'John'}, + {age: 12} + ] + } + }); + }).to.throw('Unknown logical operator "$wrong"'); + }); + + it('should be ok with default logical operator(`$and`)', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + name: 'John', + age: 12 + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with default logical operator(`$and`) for one field', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + age: { + $gt: 5, + $lt: 15, + $ne: 10 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where "age" > 5 and "age" < 15 and ' + + '"age" != 10;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array `$and`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [ + {name: 'John'}, + {age: 12} + ] + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object `$and`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: { + name: 'John', + age: 12 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} and "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with array `$or`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [ + {name: 'John'}, + {age: 12} + ] + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object `$or`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: { + name: 'John', + age: 12 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where "name" = ${p1} or "age" = 12;'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with array `$not`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $not: [ + {name: 'John'}, + {age: 12} + ] + } + }); + + expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + + '"age" = 12);'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object `$not`', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $not: { + name: 'John', + age: 12 + } + } + }); + + expect(result.query).to.be.equal('select * from "users" where not ("name" = ${p1} and ' + + '"age" = 12);'); + expect(result.values).to.be.eql({ + p1: 'John' + }); + }); + + it('should be ok with object [`$or`, `$or`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: [{ + $or: { + name: 'John', + age: 12 + } + }, { + $or: { + name: 'Mark', + age: 14 + } + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} or "age" = 12) and ' + + '("name" = ${p2} or "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with object `$and`:[`$or`, `$or`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [{ + $or: { + name: 'John', + age: 12 + } + }, { + $or: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} or "age" = 12) and ' + + '("name" = ${p2} or "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with object `$or`:[{},{}]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [{ + name: 'John', + age: 12 + }, { + name: 'Mark', + age: 14 + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) or ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with object `$or`:[`$and`, `$and`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [{ + $and: { + name: 'John', + age: 12 + } + }, { + $and: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) or ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with [{}, {}]', function() { + var result = jsonSql.build({ + table: 'users', + condition: [{ + name: 'John', + age: 12 + }, { + name: 'Mark', + age: 14 + }] + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) and ' + + '("name" = ${p2} and "age" = 14);'); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with `$and`:[{}, {}]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [{ + name: 'John', + age: 12 + }, { + name: 'Mark', + age: 14 + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) and ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with `$and`:[`$and`, `$and`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $and: [{ + $and: { + name: 'John', + age: 12 + } + }, { + $and: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} and "age" = 12) and ' + + '("name" = ${p2} and "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + + it('should be ok with `$or`:[`$or`, `$or`]', function() { + var result = jsonSql.build({ + table: 'users', + condition: { + $or: [{ + $or: { + name: 'John', + age: 12 + } + }, { + $or: { + name: 'Mark', + age: 14 + } + }] + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" ' + + 'where ("name" = ${p1} or "age" = 12) or ' + + '("name" = ${p2} or "age" = 14);' + ); + expect(result.values).to.be.eql({ + p1: 'John', + p2: 'Mark' + }); + }); + }); + }); + + describe('group', function() { + it('should be ok with string value', function() { + var result = jsonSql.build({ + table: 'users', + group: 'age' + }); + + expect(result.query).to.be.equal( + 'select * from "users" group by "age";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array value', function() { + var result = jsonSql.build({ + table: 'users', + group: ['age', 'gender'] + }); + + expect(result.query).to.be.equal( + 'select * from "users" group by "age", "gender";' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('sort', function() { + it('should be ok with string value', function() { + var result = jsonSql.build({ + table: 'users', + sort: 'age' + }); + + expect(result.query).to.be.equal( + 'select * from "users" order by "age";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array value', function() { + var result = jsonSql.build({ + table: 'users', + sort: ['age', 'gender'] + }); + + expect(result.query).to.be.equal( + 'select * from "users" order by "age", "gender";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object value', function() { + var result = jsonSql.build({ + table: 'users', + sort: { + age: 1, + gender: -1 + } + }); + + expect(result.query).to.be.equal( + 'select * from "users" order by "age" asc, "gender" desc;' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('limit, offset', function() { + it('should be ok with `limit` property', function() { + var result = jsonSql.build({ + table: 'users', + limit: 5 + }); + + expect(result.query).to.be.equal( + 'select * from "users" limit 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `offset` property', function() { + var result = jsonSql.build({ + table: 'users', + offset: 5 + }); + + expect(result.query).to.be.equal( + 'select * from "users" offset 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `limit` and `offset` properties', function() { + var result = jsonSql.build({ + table: 'users', + limit: 10, + offset: 20 + }); + + expect(result.query).to.be.equal( + 'select * from "users" limit 10 offset 20;' + ); + expect(result.values).to.be.eql({}); + }); + }); +}); diff --git a/legacy/json-sql/tests/2_insert.js b/legacy/json-sql/tests/2_insert.js new file mode 100644 index 00000000..fe3f1b9d --- /dev/null +++ b/legacy/json-sql/tests/2_insert.js @@ -0,0 +1,73 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Insert', function() { + it('should throw error without `values` property', function() { + expect(function() { + jsonSql.build({ + type: 'insert', + table: 'users' + }); + }).to.throw('`values` property is not set in `insert` clause'); + }); + + it('should be ok with `values` property', function() { + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: { + name: 'Max' + } + }); + + expect(result.query).to.be.equal('insert into "users" ("name") values (${p1});'); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `with` property', function() { + var result = jsonSql.build({ + 'with': [{ + name: 't_1', + select: { + table: 't_1' + } + }], + type: 'insert', + table: 'users', + values: { + name: 'Max', + age: 17, + lastVisit: null, + active: true + } + }); + + expect(result.query).to.be.equal( + 'with "t_1" as (select * from "t_1") insert into "users" ' + + '("name", "age", "lastVisit", "active") values (${p1}, 17, null, true);' + ); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `returning` property', function() { + var result = jsonSql.build({ + type: 'insert', + table: 'users', + values: { + name: 'Max', + age: 17, + lastVisit: null, + active: true + }, + returning: ['users.*'] + }); + + expect(result.query).to.be.equal( + 'insert into "users" ("name", "age", "lastVisit", "active") ' + + 'values (${p1}, 17, null, true) returning "users".*;' + ); + expect(result.values).to.be.eql({p1: 'Max'}); + }); +}); diff --git a/legacy/json-sql/tests/3_update.js b/legacy/json-sql/tests/3_update.js new file mode 100644 index 00000000..688b4c1d --- /dev/null +++ b/legacy/json-sql/tests/3_update.js @@ -0,0 +1,123 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Update', function() { + describe('modifier', function() { + it('should throw error without `modifier` property', function() { + expect(function() { + jsonSql.build({ + type: 'update', + table: 'users' + }); + }).to.throw('`modifier` property is not set in `update` clause'); + }); + + it('should be ok with default(`$set`)', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + name: 'Max', + age: 16, + lastVisit: null, + active: false + } + }); + + expect(result.query).to.be.equal('update "users" set "name" = ${p1}, "age" = 16, ' + + '"lastVisit" = null, "active" = false;'); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `$set`', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $set: { + name: 'Max' + } + } + }); + + expect(result.query).to.be.equal('update "users" set "name" = ${p1};'); + expect(result.values).to.be.eql({p1: 'Max'}); + }); + + it('should be ok with `$inc`', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $inc: { + age: 4 + } + } + }); + + expect(result.query).to.be.equal('update "users" set "age" = "age" + 4;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `$dec`', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $dec: { + age: 2 + } + } + }); + + expect(result.query).to.be.equal('update "users" set "age" = "age" - 2;'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('with', function() { + it('should be ok', function() { + var result = jsonSql.build({ + 'with': [{ + name: 't_1', + select: { + table: 't_1' + } + }], + type: 'update', + table: 'users', + modifier: { + $dec: { + age: 3 + } + } + }); + + expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") update "users" ' + + 'set "age" = "age" - 3;'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('returning', function() { + it('should be ok', function() { + var result = jsonSql.build({ + type: 'update', + table: 'users', + modifier: { + $dec: { + age: 3 + } + }, + returning: ['users.*'] + }); + + expect(result.query).to.be.equal( + 'update "users" set "age" = "age" - 3 returning "users".*;' + ); + expect(result.values).to.be.eql({}); + }); + }); +}); diff --git a/legacy/json-sql/tests/4_delete.js b/legacy/json-sql/tests/4_delete.js new file mode 100644 index 00000000..c90f19f2 --- /dev/null +++ b/legacy/json-sql/tests/4_delete.js @@ -0,0 +1,58 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Delete', function() { + it('should be ok without `condition` property', function() { + var result = jsonSql.build({ + type: 'remove', + table: 'users' + }); + + expect(result.query).to.be.equal('delete from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `condition` property', function() { + var result = jsonSql.build({ + type: 'remove', + table: 'users', + condition: { + a: 5 + } + }); + + expect(result.query).to.be.equal('delete from "users" where "a" = 5;'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `with` property', function() { + var result = jsonSql.build({ + 'with': [{ + name: 't_1', + select: { + table: 't_1' + } + }], + type: 'remove', + table: 'users' + }); + + expect(result.query).to.be.equal('with "t_1" as (select * from "t_1") delete from "users";'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `returning` property', function() { + var result = jsonSql.build({ + type: 'remove', + table: 'users', + returning: ['users.*'] + }); + + expect(result.query).to.be.equal( + 'delete from "users" returning "users".*;' + ); + expect(result.values).to.be.eql({}); + }); +}); diff --git a/legacy/json-sql/tests/5_union.js b/legacy/json-sql/tests/5_union.js new file mode 100644 index 00000000..a1e79fcf --- /dev/null +++ b/legacy/json-sql/tests/5_union.js @@ -0,0 +1,256 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Union, except, intersect', function() { + describe('queries', function() { + it('should throw error without `queries` property', function() { + expect(function() { + jsonSql.build({ + type: 'union' + }); + }).to.throw('`queries` property is not set in `union` clause'); + }); + + it('should throw error with non-array value', function() { + expect(function() { + jsonSql.build({ + type: 'union', + queries: 'wrong' + }); + }).to.throw('`queries` property should be an array in `union` clause'); + }); + + it('should throw error with value length < 2', function() { + expect(function() { + jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }] + }); + }).to.throw('`queries` property should not have length less than 2 in `union` clause'); + }); + + it('should be ok with value length = 2', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") union (select * from "vipUsers");'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('type & all combinations', function() { + it('should be ok with `type` = "union", `all` = true', function() { + var result = jsonSql.build({ + type: 'union', + all: true, + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") union all (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "except"', function() { + var result = jsonSql.build({ + type: 'except', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") except (select * from "vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "except", `all` = true', function() { + var result = jsonSql.build({ + type: 'except', + all: true, + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") except all (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "intersect"', function() { + var result = jsonSql.build({ + type: 'intersect', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") intersect (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "intersect", `all` = true', function() { + var result = jsonSql.build({ + type: 'intersect', + all: true, + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + }); + + expect(result.query).to.be.equal('(select * from "users") intersect all (select * from ' + + '"vipUsers");'); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `type` = "union" subquery', function() { + var result = jsonSql.build({ + query: { + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }] + } + }); + + expect(result.query).to.be.equal('select * from ((select * from "users") union (select * ' + + 'from "vipUsers"));'); + expect(result.values).to.be.eql({}); + }); + }); + + describe('sort', function() { + it('should be ok with string value', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + sort: 'age' + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") order by "age";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with array value', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + sort: ['age', 'gender'] + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") order by "age", "gender";' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with object value', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + sort: { + age: 1, + gender: -1 + } + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") order by "age" asc, "gender" desc;' + ); + expect(result.values).to.be.eql({}); + }); + }); + + describe('limit, offset', function() { + it('should be ok with `limit` property', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + limit: 5 + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") limit 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `offset` property', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + offset: 5 + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") offset 5;' + ); + expect(result.values).to.be.eql({}); + }); + + it('should be ok with `limit` and `offset` properties', function() { + var result = jsonSql.build({ + type: 'union', + queries: [{ + table: 'users' + }, { + table: 'vipUsers' + }], + limit: 10, + offset: 20 + }); + + expect(result.query).to.be.equal( + '(select * from "users") union (select * from "vipUsers") limit 10 offset 20;' + ); + expect(result.values).to.be.eql({}); + }); + }); +}); diff --git a/legacy/json-sql/tests/6_postgresDialect.js b/legacy/json-sql/tests/6_postgresDialect.js new file mode 100644 index 00000000..790c0ec2 --- /dev/null +++ b/legacy/json-sql/tests/6_postgresDialect.js @@ -0,0 +1,140 @@ +'use strict'; + +var jsonSql = require('../lib')({ + dialect: 'postgresql', + namedValues: false +}); +var expect = require('chai').expect; + +describe('PostgreSQL dialect', function() { + describe('json', function() { + it('should correctly wrap each part of json path', function() { + var result = jsonSql.build({ + table: 'test', + fields: ['params->a->>b'], + condition: { + 'params->>c': {$like: '7%'} + } + }); + + expect(result.query).to.be.equal( + 'select "params"->\'a\'->>\'b\' from "test" ' + + 'where "params"->>\'c\' like ${1};' + ); + }); + + it('should be ok with `$jsonContains` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + 'params->a': { + $jsonContains: {b: 1} + } + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params"->\'a\' @> ${1};' + ); + expect(result.values).to.be.eql(['{"b":1}']); + }); + + it('should be ok with `$jsonIn` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + 'params->a': { + $jsonIn: {$field: 'data->b'} + } + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params"->\'a\' <@ "data"->\'b\';' + ); + expect(result.values).to.be.eql([]); + }); + + it('should be ok with `$jsonHas` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$jsonHas: 'account'} + } + }); + + expect(result.query).to.be.equal('select * from "test" where "params" ? ${1};'); + expect(result.values).to.be.eql(['account']); + }); + + it('should be ok with `$jsonHasAny` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$jsonHasAny: ['a', 'b']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params" ?| array[${1}, ${2}];' + ); + expect(result.values).to.be.eql(['a', 'b']); + }); + + it('should be ok with `$jsonHasAll` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$jsonHasAll: ['a', 'b']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params" ?& array[${1}, ${2}];' + ); + expect(result.values).to.be.eql(['a', 'b']); + }); + + it('should be ok with `$upper` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$upper: ['params', '3498862814541110459l']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where upper("params") = upper(${1});' + ); + expect(result.values).to.be.eql(['3498862814541110459l']); + }); + + it('should be ok with `$lower` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$lower: ['params', '3498862814541110459L']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where lower("params") = lower(${1});' + ); + expect(result.values).to.be.eql(['3498862814541110459L']); + }); + + it('should be ok with `$decode` conditional operator', function() { + var result = jsonSql.build({ + table: 'test', + condition: { + params: {$decode: ['params', '3498862814541110459L', 'hex']} + } + }); + + expect(result.query).to.be.equal( + 'select * from "test" where "params" = decode(${1}, ${2});' + ); + expect(result.values).to.be.eql(['3498862814541110459L', 'hex']); + }); + }); +}); diff --git a/legacy/json-sql/tests/7_create.js b/legacy/json-sql/tests/7_create.js new file mode 100644 index 00000000..a93ca702 --- /dev/null +++ b/legacy/json-sql/tests/7_create.js @@ -0,0 +1,279 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +describe('Create', function () { + it('should throw error without `tableFields` property', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users' + }); + }).to.throw('`tableFields` property is not set in `create` clause'); + }); + + it('should throw error with incorrect field type', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "age", + type: "NotNumber" + } + ] + }); + }).to.throw('Invalid type of field: NotNumber'); + }); + + it('should be ok with `tableFields` property', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "age", + type: "Number" + } + ] + }); + + + expect(result.query).to.be.equal('create table if not exists "users"("age" int);'); + }); + + it('should throw error when length property for string field not provided', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String" + } + ] + }); + }).to.throw('Field length can\'t be less or equal 0'); + }); + + it('should throw error with empty name', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: " ", + type: "String" + } + ] + }); + }).to.throw('Name most contains characters'); + }); + + it('should be ok with string field and length', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16 + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16));'); + }); + + it('should be ok with string field not null', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL);'); + }); + + it('should be ok with string field not null primary key', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY);'); + }); + + + it('should be ok with string field not null unique', function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + unique: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL UNIQUE);'); + }); + + + it('should be allow only one primary key field', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "secondname", + type: "String", + length: 16, + not_null: true, + primary_key: true + } + ] + }) + }).to.throw("Too much primary key 'secondname' in table"); + }); + + it('should be allow only unique field name', function () { + expect(function () { + jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "name", + type: "String", + length: 16, + not_null: true + } + ] + }) + }).to.throw("Two parameters with same name: name"); + }); + + it("should allow few fields", function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL);'); + }); + + it("should allow few fields", function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ], + foreignKeys: [ + { + field: "name", + table: "person", + table_field: "id" + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); + }); + + it("should allow few fields", function () { + var result = jsonSql.build({ + type: 'create', + table: 'users', + tableFields: [ + { + name: "name", + type: "String", + length: 16, + not_null: true, + primary_key: true + }, + { + name: "age", + type: "Number", + not_null: true + } + ], + foreignKeys: [ + { + field: "name", + table: "person", + table_field: "id" + } + ] + }); + + expect(result.query).to.be.equal('create table if not exists "users"("name" varchar(16) NOT NULL PRIMARY KEY,"age" int NOT NULL, FOREIGN KEY ("name") REFERENCES person("id"));'); + }); +}); diff --git a/legacy/json-sql/tests/8_index.js b/legacy/json-sql/tests/8_index.js new file mode 100644 index 00000000..464a85b3 --- /dev/null +++ b/legacy/json-sql/tests/8_index.js @@ -0,0 +1,45 @@ +'use strict'; + +var jsonSql = require('../lib')(); +var expect = require('chai').expect; + +/* + jsonSql.build({ + type: 'index', + table: 'users', + index: { + name: "user_id", + field: "id" + } + }); + */ + +describe('Index', function() { + it('should throw error without name property', function() { + expect(function() { + jsonSql.build({ + type: 'index', + table: 'users' + }); + }).to.throw('`name` property is not set in `index` clause'); + }); + + it('should throw error without indexOn property', function() { + expect(function() { + jsonSql.build({ + type: 'index', + table: 'users', + name: 'index_id' + }); + }).to.throw('`indexOn` property is not set in `index` clause'); + }); + + it('should be ok with name and field property', function () { + var result = jsonSql.build({ + type: "index", + table: "users", + name: "index_id", + indexOn: "id" + }); + }); +}); diff --git a/logic/account.js b/logic/account.js index b9ed733c..51e7622e 100644 --- a/logic/account.js +++ b/logic/account.js @@ -653,6 +653,8 @@ Account.prototype.set = function (address, fields, cb) { modifier: this.toDB(fields) }); + console.log('x-!!!', sql.query, sql.values) + this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb); }).catch(function (err) { From aba639ba4bc4c1e5db02f3afcde9014db40cd749 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 16:14:40 +0400 Subject: [PATCH 23/33] debug: json-sql --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa97b268..bd85f3fc 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ip": "=1.1.8", "js-nacl": "^1.4.0", "json-schema": "=0.4.0", - "json-sql": "^0.5.0", + "json-sql": "./legacy/json-sql", "lodash": "=4.17.21", "method-override": "=3.0.0", "npm": "=8.17.0", From 02c935e5a8f060bd1d9c4c1dd0705a3ab2c9165f Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 16:34:45 +0400 Subject: [PATCH 24/33] debug: json-sql --- logic/account.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/logic/account.js b/logic/account.js index 51e7622e..45d47d53 100644 --- a/logic/account.js +++ b/logic/account.js @@ -645,15 +645,28 @@ Account.prototype.set = function (address, fields, cb) { address = String(address).toUpperCase(); fields.address = address; + // + // In json-sql v0.2.6 it was type: 'insertorupdate', which is removed in v0.5.0 + // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) on conflict ("address") do update set "publicKey" = ${3}, "address" = ${4}; + // The workaround is building an 'insert' request and manually adding 'on conflict ("address") do update set' + var sql = jsonSql.build({ - type: 'insertorupdate', + type: 'insert', table: this.table, - conflictFields: ['address'], values: this.toDB(fields), - modifier: this.toDB(fields) }); - console.log('x-!!!', sql.query, sql.values) + console.log('x-!!!-1', sql.query, sql.values) + + const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) + const columns = insertQuery.match(/\("(.+?)"\)/)[1].split('", "'); + const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => `"${col}" = $${index + 1}`).join(', '); + + sql.query = insertQuery + updateQuery; + console.log('x-!!!', insertQuery, sql.updateQuery) + + console.log('x-!!!-2', sql.query, sql.values) + this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb); From bb1086a0da913f53a0f203fabbcdb4c20be4b854 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 16:38:45 +0400 Subject: [PATCH 25/33] debug: json-sql --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bd85f3fc..fa97b268 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ip": "=1.1.8", "js-nacl": "^1.4.0", "json-schema": "=0.4.0", - "json-sql": "./legacy/json-sql", + "json-sql": "^0.5.0", "lodash": "=4.17.21", "method-override": "=3.0.0", "npm": "=8.17.0", From 70363da976210de4c1a0d6c2cf057b785277b06b Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 16:53:21 +0400 Subject: [PATCH 26/33] debug: json-sql --- logic/account.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/logic/account.js b/logic/account.js index 45d47d53..f185ed7f 100644 --- a/logic/account.js +++ b/logic/account.js @@ -659,11 +659,12 @@ Account.prototype.set = function (address, fields, cb) { console.log('x-!!!-1', sql.query, sql.values) const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) - const columns = insertQuery.match(/\("(.+?)"\)/)[1].split('", "'); + const columnPart = insertQuery.split('values')[0]; + const columns = columnPart.match(/"(\w+)"/g).map(col => col.replace(/"/g, '')); const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => `"${col}" = $${index + 1}`).join(', '); - sql.query = insertQuery + updateQuery; - console.log('x-!!!', insertQuery, sql.updateQuery) + sql.query = insertQuery + updateQuery + ';'; + console.log('x-!!!', insertQuery, updateQuery) console.log('x-!!!-2', sql.query, sql.values) From 0c47873b08febf3c344c968c75295686ebae8663 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 16:59:22 +0400 Subject: [PATCH 27/33] debug: json-sql --- logic/account.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index f185ed7f..dff81402 100644 --- a/logic/account.js +++ b/logic/account.js @@ -659,7 +659,10 @@ Account.prototype.set = function (address, fields, cb) { console.log('x-!!!-1', sql.query, sql.values) const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) - const columnPart = insertQuery.split('values')[0]; + const columnPart = insertString.substring( + insertString.indexOf('(') + 1, + insertString.indexOf(') values') + ); const columns = columnPart.match(/"(\w+)"/g).map(col => col.replace(/"/g, '')); const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => `"${col}" = $${index + 1}`).join(', '); From f175881ee069ddcf91362e86cdb935c814438f5d Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 17:00:38 +0400 Subject: [PATCH 28/33] debug: json-sql --- logic/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index dff81402..cca6fedf 100644 --- a/logic/account.js +++ b/logic/account.js @@ -659,7 +659,7 @@ Account.prototype.set = function (address, fields, cb) { console.log('x-!!!-1', sql.query, sql.values) const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) - const columnPart = insertString.substring( + const columnPart = insertQuery.substring( insertString.indexOf('(') + 1, insertString.indexOf(') values') ); From 5a73ed9ef51c3e9099b1e8ada3b5388f4a3194f8 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 17:01:23 +0400 Subject: [PATCH 29/33] debug: json-sql --- logic/account.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logic/account.js b/logic/account.js index cca6fedf..41cd045c 100644 --- a/logic/account.js +++ b/logic/account.js @@ -660,8 +660,8 @@ Account.prototype.set = function (address, fields, cb) { const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) const columnPart = insertQuery.substring( - insertString.indexOf('(') + 1, - insertString.indexOf(') values') + insertQuery.indexOf('(') + 1, + insertQuery.indexOf(') values') ); const columns = columnPart.match(/"(\w+)"/g).map(col => col.replace(/"/g, '')); const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => `"${col}" = $${index + 1}`).join(', '); From 42b922e2bc64ff6a82a91dd57d7c9e89e61f3c18 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 17:05:47 +0400 Subject: [PATCH 30/33] debug: json-sql --- logic/account.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/logic/account.js b/logic/account.js index 41cd045c..a1b47f90 100644 --- a/logic/account.js +++ b/logic/account.js @@ -664,7 +664,12 @@ Account.prototype.set = function (address, fields, cb) { insertQuery.indexOf(') values') ); const columns = columnPart.match(/"(\w+)"/g).map(col => col.replace(/"/g, '')); - const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => `"${col}" = $${index + 1}`).join(', '); + const valuesPart = insertQuery.split('values')[1].trim().slice(1, -1); + const values = valuesPart.split(',').map(val => val.trim()); + const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => { + const value = values[index].startsWith('$') ? values[index] : values[index]; + return `"${col}" = ${value}`; + }).join(', '); sql.query = insertQuery + updateQuery + ';'; console.log('x-!!!', insertQuery, updateQuery) From 02e994f0a99b10b927f5ea78f5ac212f5024282e Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 18:14:56 +0400 Subject: [PATCH 31/33] debug: json-sql --- logic/account.js | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/logic/account.js b/logic/account.js index a1b47f90..2b64eab2 100644 --- a/logic/account.js +++ b/logic/account.js @@ -598,9 +598,6 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; - - console.log('!!!', filter.address); - // Do case insensitive address comparison -> where "address" ilike $1; [ 'U16455322533504200665' ] // In json-sql v0.2.6 it was $upper: ['address', filter.address] if (typeof filter.address === 'string') { @@ -620,8 +617,6 @@ Account.prototype.getAll = function (filter, fields, cb) { fields: realFields }); - console.log('!!!', sql.query, sql.values) - this.scope.db.query(sql.query, sql.values).then(function (rows) { return setImmediate(cb, null, rows); }).catch(function (err) { @@ -645,9 +640,9 @@ Account.prototype.set = function (address, fields, cb) { address = String(address).toUpperCase(); fields.address = address; - // // In json-sql v0.2.6 it was type: 'insertorupdate', which is removed in v0.5.0 - // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) on conflict ("address") do update set "publicKey" = ${3}, "address" = ${4}; + // insert into "mem_accounts" ("address", "u_isDelegate", "isDelegate", "username", "u_username") values ($1, 1, 0, null, $2) + // on conflict ("address") do update set "address" = $1, "u_isDelegate" = 1, "isDelegate" = 0, "username" = null, "u_username" = $2 // The workaround is building an 'insert' request and manually adding 'on conflict ("address") do update set' var sql = jsonSql.build({ @@ -656,8 +651,6 @@ Account.prototype.set = function (address, fields, cb) { values: this.toDB(fields), }); - console.log('x-!!!-1', sql.query, sql.values) - const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) const columnPart = insertQuery.substring( insertQuery.indexOf('(') + 1, @@ -672,10 +665,6 @@ Account.prototype.set = function (address, fields, cb) { }).join(', '); sql.query = insertQuery + updateQuery + ';'; - console.log('x-!!!', insertQuery, updateQuery) - - console.log('x-!!!-2', sql.query, sql.values) - this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb); @@ -714,8 +703,7 @@ Account.prototype.merge = function (address, diff, cb) { break; case Number: if (isNaN(trueValue) || trueValue === Infinity) { - console.log(diff); - return setImmediate(cb, 'Encountered unsane number: ' + trueValue); + return setImmediate(cb, 'Encountered unsafe number: ' + trueValue); } else if (Math.abs(trueValue) === trueValue && trueValue !== 0) { update.$inc = update.$inc || {}; update.$inc[value] = Math.floor(trueValue); @@ -830,8 +818,6 @@ Account.prototype.merge = function (address, diff, cb) { if (Object.keys(remove).length) { Object.keys(remove).forEach(function (el) { - console.log('2-!!!', remove[el]) - var sql = jsonSql.build({ type: 'remove', table: self.table + '2' + el, @@ -840,7 +826,6 @@ Account.prototype.merge = function (address, diff, cb) { accountId: address } }); - console.log('2-!!!', sql.query, sql.values) sqles.push(sql); }); @@ -870,7 +855,6 @@ Account.prototype.merge = function (address, diff, cb) { table: self.table + '2' + el, condition: remove_object[el] }); - console.log('3-!!!', sql.query, sql.values) sqles.push(sql); }); @@ -899,7 +883,6 @@ Account.prototype.merge = function (address, diff, cb) { address: address } }); - console.log('4-!!!', sql.query, sql.values) sqles.push(sql); } @@ -949,7 +932,6 @@ Account.prototype.remove = function (address, cb) { address: address } }); - console.log('5-!!!', sql.query, sql.values) this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb, null, address); From abadffe00de636480e20f941af9b0720101df508 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 18:19:04 +0400 Subject: [PATCH 32/33] revert: back to old json-sql --- logic/account.js | 37 +++++++------------------------------ modules/sql.js | 2 +- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/logic/account.js b/logic/account.js index 2b64eab2..3640730b 100644 --- a/logic/account.js +++ b/logic/account.js @@ -3,7 +3,7 @@ var async = require('async'); var pgp = require('pg-promise'); var path = require('path'); -var jsonSql = require('json-sql')({namedValues: false}); +var jsonSql = require('json-sql')(); jsonSql.setDialect('postgresql'); var constants = require('../helpers/constants.js'); var slots = require('../helpers/slots.js'); @@ -598,11 +598,9 @@ Account.prototype.getAll = function (filter, fields, cb) { } delete filter.sort; - // Do case insensitive address comparison -> where "address" ilike $1; [ 'U16455322533504200665' ] - // In json-sql v0.2.6 it was $upper: ['address', filter.address] if (typeof filter.address === 'string') { filter.address = { - $ilike: filter.address + $upper: ['address', filter.address] }; } @@ -640,32 +638,14 @@ Account.prototype.set = function (address, fields, cb) { address = String(address).toUpperCase(); fields.address = address; - // In json-sql v0.2.6 it was type: 'insertorupdate', which is removed in v0.5.0 - // insert into "mem_accounts" ("address", "u_isDelegate", "isDelegate", "username", "u_username") values ($1, 1, 0, null, $2) - // on conflict ("address") do update set "address" = $1, "u_isDelegate" = 1, "isDelegate" = 0, "username" = null, "u_username" = $2 - // The workaround is building an 'insert' request and manually adding 'on conflict ("address") do update set' - var sql = jsonSql.build({ - type: 'insert', + type: 'insertorupdate', table: this.table, + conflictFields: ['address'], values: this.toDB(fields), + modifier: this.toDB(fields) }); - const insertQuery = sql.query.slice(0, -1); // insert into "mem_accounts" ("publicKey", "address") values (${1}, ${2}) - const columnPart = insertQuery.substring( - insertQuery.indexOf('(') + 1, - insertQuery.indexOf(') values') - ); - const columns = columnPart.match(/"(\w+)"/g).map(col => col.replace(/"/g, '')); - const valuesPart = insertQuery.split('values')[1].trim().slice(1, -1); - const values = valuesPart.split(',').map(val => val.trim()); - const updateQuery = ' on conflict ("address") do update set ' + columns.map((col, index) => { - const value = values[index].startsWith('$') ? values[index] : values[index]; - return `"${col}" = ${value}`; - }).join(', '); - - sql.query = insertQuery + updateQuery + ';'; - this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb); }).catch(function (err) { @@ -703,7 +683,8 @@ Account.prototype.merge = function (address, diff, cb) { break; case Number: if (isNaN(trueValue) || trueValue === Infinity) { - return setImmediate(cb, 'Encountered unsafe number: ' + trueValue); + console.log(diff); + return setImmediate(cb, 'Encountered unsane number: ' + trueValue); } else if (Math.abs(trueValue) === trueValue && trueValue !== 0) { update.$inc = update.$inc || {}; update.$inc[value] = Math.floor(trueValue); @@ -826,7 +807,6 @@ Account.prototype.merge = function (address, diff, cb) { accountId: address } }); - sqles.push(sql); }); } @@ -855,7 +835,6 @@ Account.prototype.merge = function (address, diff, cb) { table: self.table + '2' + el, condition: remove_object[el] }); - sqles.push(sql); }); } @@ -883,7 +862,6 @@ Account.prototype.merge = function (address, diff, cb) { address: address } }); - sqles.push(sql); } @@ -932,7 +910,6 @@ Account.prototype.remove = function (address, cb) { address: address } }); - this.scope.db.none(sql.query, sql.values).then(function () { return setImmediate(cb, null, address); }).catch(function (err) { diff --git a/modules/sql.js b/modules/sql.js index fcf9acea..e4a3a957 100644 --- a/modules/sql.js +++ b/modules/sql.js @@ -2,7 +2,7 @@ var async = require('async'); var extend = require('extend'); -var jsonSql = require('json-sql')({namedValues: false}); +var jsonSql = require('json-sql')(); jsonSql.setDialect('postgresql'); var sandboxHelper = require('../helpers/sandbox.js'); From 7692d55df96b637c78413264fd21eb47c7b71bd5 Mon Sep 17 00:00:00 2001 From: adamant-al Date: Fri, 24 Nov 2023 18:19:38 +0400 Subject: [PATCH 33/33] revert: back to old json-sql --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa97b268..bd85f3fc 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ip": "=1.1.8", "js-nacl": "^1.4.0", "json-schema": "=0.4.0", - "json-sql": "^0.5.0", + "json-sql": "./legacy/json-sql", "lodash": "=4.17.21", "method-override": "=3.0.0", "npm": "=8.17.0",