@sap/cds-sqlite
Advanced tools
Comparing version 0.9.0 to 1.0.3
@@ -9,2 +9,22 @@ # Changelog | ||
## Version 1.0.3 - 2018-11-27 | ||
### Added | ||
- credentials.database can be used instead of parameters host and url | ||
### Changed | ||
- Throw db error instead of wrapping it in Sql Error | ||
- Throw an error if database is not defined instead of fallback to memory | ||
### Fixed | ||
- Bulk Insert with $user / $now | ||
- Post processing of Binary, Boolean, DateTime and Integer64 | ||
## Version 0.10.0 - 2018-10-17 | ||
- Refactoring and changes due to updated dependencies | ||
## Version 0.9.0 - 2018-10-04 | ||
@@ -11,0 +31,0 @@ |
@@ -5,3 +5,3 @@ const _notInjected = () => { | ||
const _validateInjection = (cds) => { | ||
const _validateInjection = cds => { | ||
if (!cds || typeof cds !== 'object') { | ||
@@ -8,0 +8,0 @@ throw new Error('Injected value is not of type `cds`') |
@@ -1,26 +0,14 @@ | ||
const {Database} = require('sqlite3') | ||
const { Database } = require('sqlite3') | ||
const SqliteStatement = require('../statement/SqliteStatement') | ||
const { | ||
BaseClient, | ||
errors: { | ||
IllegalFunctionArgumentError, | ||
InconsistentClientError, | ||
SqlError | ||
}, | ||
builder: {sqlFactory}, | ||
expand: { | ||
createJoinCQNFromExpanded, | ||
hasExpand, | ||
rawToExpanded | ||
}, | ||
contains: { | ||
replaceContainsWithLike, | ||
hasContains | ||
}, | ||
postProcessing: { | ||
getPostProcessMapper, | ||
postProcess | ||
} | ||
errors: { IllegalFunctionArgumentError, InconsistentClientError }, | ||
builder: { sqlFactory }, | ||
expand: { createJoinCQNFromExpanded, hasExpand, rawToExpanded }, | ||
contains: { replaceContainsWithLike, hasContains }, | ||
postProcessing: { getPostProcessMapper, postProcess } | ||
} = require('@sap/cds-sql') | ||
const { convertToBoolean, convertInt64ToString, convertToISOTime, convertToISONoMillis } = require('./util') | ||
class Client extends BaseClient { | ||
@@ -30,36 +18,14 @@ /** | ||
* | ||
* Username/password are currently not supported, | ||
* but at least the host must be provided. | ||
* | ||
* @param {Object} connect - Connection details. | ||
* @param {string} connect.host - Filename to the database or :memory; for in memory | ||
* @param {string} [connect.user] - Username for authentication | ||
* @param {string} [connect.password] - Password for authentication | ||
* @param {Object} credentials - Connection details. | ||
* @param {string} credentials.database - Filename to the database or :memory; for in memory | ||
*/ | ||
constructor (connect) { | ||
const convertToBase64Buffer = (element) => { | ||
return Buffer.from(element, 'base64') | ||
} | ||
const convertToISOTime = (value) => { | ||
if (value === null) { | ||
return value | ||
} | ||
if (!value) { | ||
value = 0 | ||
} | ||
return new Date(value).toISOString() | ||
} | ||
constructor (credentials) { | ||
super([ | ||
['cds.Boolean', Boolean], | ||
['cds.Integer64', String], | ||
['cds.Binary', convertToBase64Buffer], | ||
['cds.LargeBinary', convertToBase64Buffer], | ||
['cds.DateTime', convertToISOTime], | ||
['cds.Boolean', convertToBoolean], | ||
['cds.Integer64', convertInt64ToString], | ||
['cds.DateTime', convertToISONoMillis], | ||
['cds.Timestamp', convertToISOTime] | ||
]) | ||
this._connect = connect | ||
this._credentials = Object.assign({}, credentials) | ||
this._user = 'anonymous' // default user | ||
@@ -74,11 +40,12 @@ this._transCount = 0 | ||
* | ||
* @returns {Promise} Promise, that resolves with SqliteClient if _connect is successful or rejects with error if not. | ||
* @returns {Promise} Promise, that resolves with SqliteClient if _credentials is successful or rejects with error if not. | ||
*/ | ||
connect () { | ||
return new Promise((resolve, reject) => { | ||
if (!this._connect) { | ||
return reject(new IllegalFunctionArgumentError('connect')) | ||
// .options() will ensure that credentials.database is set | ||
if (!this._credentials) { | ||
return reject(new IllegalFunctionArgumentError('options.credentials')) | ||
} | ||
this._dbc = new Database(this._connect.host, (err) => { | ||
this._dbc = new Database(this._credentials.database, err => { | ||
if (err) { | ||
@@ -102,3 +69,3 @@ return reject(err) | ||
return new Promise((resolve, reject) => { | ||
this._dbc.close((err) => { | ||
this._dbc.close(err => { | ||
if (err) { | ||
@@ -171,7 +138,11 @@ return reject(err) | ||
const {sql, values = []} = sqlFactory(newQuery, { | ||
typeConversion: this._typeConversionMap, | ||
user: this._user, | ||
now: {sql: 'strftime(\'%Y-%m-%dT%H:%M:%SZ\',\'now\')'} // '2012-12-03T07:16:23Z' | ||
}, this._csn) | ||
const { sql, values = [] } = sqlFactory( | ||
newQuery, | ||
{ | ||
typeConversion: this._typeConversionMap, | ||
user: this._user, | ||
now: { sql: "strftime('%Y-%m-%dT%H:%M:%SZ','now')" } // '2012-12-03T07:16:23Z' | ||
}, | ||
this._csn | ||
) | ||
@@ -209,3 +180,3 @@ return this._executeSQL(sql, values, getPostProcessMapper(this._toService, this._csn, newQuery)) | ||
// Regex is faster than toLower + trim + startsWith | ||
return (new RegExp(`^\\s*${type}`, 'i')).test(sql) | ||
return new RegExp(`^\\s*${type}`, 'i').test(sql) | ||
} | ||
@@ -218,3 +189,4 @@ | ||
if (err) { | ||
return reject(new SqlError(err, sql, values)) | ||
err.failedQuery = sql | ||
return reject(err) | ||
} | ||
@@ -231,3 +203,4 @@ | ||
if (err) { | ||
return reject(new SqlError(err, sql, values)) | ||
err.failedQuery = sql | ||
return reject(err) | ||
} | ||
@@ -240,54 +213,8 @@ | ||
/** | ||
* One can not use more than 999 values per insert. | ||
* There is no build in bulk support of sqlite3. | ||
* This requires to split into multiple SQLs in case the total sum of values is greater than 999. | ||
*/ | ||
_bulkInsert (sql, values) { | ||
const valuesPerRow = values[0].length | ||
const placeholders = this._getBulkPlaceholders(valuesPerRow) | ||
const promises = [] | ||
// In case 999 will be reached this vars will be reset | ||
let bulkSql = sql | ||
let flattenedValues = [] | ||
for (const insert of values) { | ||
// Execute as adding this row would add more values than supported | ||
if ((flattenedValues.length + valuesPerRow) > 999) { | ||
promises.push(this._runSingle(bulkSql, flattenedValues)) | ||
bulkSql = sql | ||
flattenedValues = [] | ||
} | ||
// Array is empty on first run and after reset | ||
if (flattenedValues.length !== 0) { | ||
bulkSql += placeholders | ||
} | ||
flattenedValues.push(...insert) | ||
} | ||
// Ensure last bulk is executed | ||
promises.push(this._runSingle(bulkSql, flattenedValues)) | ||
// Wait for all inserts to be done | ||
return Promise.all(promises) | ||
.then(() => { | ||
// return affected rows | ||
return values.length | ||
}) | ||
return this.prepareStatement(sql) | ||
.then(stmt => stmt.execute(values)) | ||
.then(() => values.length) | ||
} | ||
_getBulkPlaceholders (valuesPerRow) { | ||
const placeholders = [] | ||
for (let i = 0; i < valuesPerRow; i++) { | ||
placeholders.push('?') | ||
} | ||
return `,(${placeholders.join(',')})` | ||
} | ||
_processExpand (cqn) { | ||
@@ -300,10 +227,9 @@ const sqls = [] | ||
const {sql, values} = sqlFactory(cqn) | ||
const { sql, values } = sqlFactory(cqn) | ||
sqls.push(this._executeSelect(sql, values, false)) | ||
} | ||
return Promise.all(sqls) | ||
.then((results) => { | ||
return rawToExpanded(expandQueries, results) | ||
}) | ||
return Promise.all(sqls).then(results => { | ||
return rawToExpanded(expandQueries, results) | ||
}) | ||
} | ||
@@ -320,5 +246,6 @@ | ||
this._dbc.serialize(() => { | ||
const stmt = this._dbc.prepare(sql, (err) => { | ||
const stmt = this._dbc.prepare(sql, err => { | ||
if (err) { | ||
return reject(new SqlError(err, sql)) | ||
err.failedQuery = sql | ||
return reject(err) | ||
} | ||
@@ -348,4 +275,3 @@ | ||
*/ | ||
setLocale () { | ||
} | ||
setLocale () {} | ||
@@ -352,0 +278,0 @@ /** |
@@ -1,28 +0,56 @@ | ||
const {IllegalFunctionArgumentError} = require('@sap/cds-sql').errors | ||
const { errors: { IllegalFunctionArgumentError } } = require('@sap/cds-sql') | ||
const _validateDatabase = database => { | ||
if (!database) { | ||
throw new IllegalFunctionArgumentError('options.credentials.database') | ||
} | ||
} | ||
const _validatePool = options => { | ||
if (options.pool.min > options.pool.max) { | ||
throw new IllegalFunctionArgumentError('options.pool.min') | ||
} | ||
if (options.credentials.database === ':memory:') { | ||
if (options.pool.max !== 1) { | ||
throw new IllegalFunctionArgumentError('options.pool.max') | ||
} | ||
if (options.pool.evictionRunIntervalMillis !== 0) { | ||
throw new IllegalFunctionArgumentError('options.pool.evictionRunIntervalMillis') | ||
} | ||
if (options.pool.idleTimeoutMillisForPools !== 0) { | ||
throw new IllegalFunctionArgumentError('options.pool.idleTimeoutMillisForPools') | ||
} | ||
} | ||
} | ||
/** | ||
* Validates the connect and pool options and adds defaults if not given. | ||
* @param {Object} [options] - The db connection options. | ||
* @param {Object} [options.url] - Connect url with driver. | ||
* @param {Object} [options.database] - Alias for url. | ||
* @param {Object} [options.driver] - Package ID of driver facade. | ||
* @param {string} [options.host] - Filename to the database or :memory: for in memory | ||
* @param {string} [options.user] - Username for authentication | ||
* @param {string} [options.password] - Password for authentication | ||
* @param {Object} [options.credentials] - The db connection options. | ||
* @param {Object} [options.credentials.database] - Alias for url. | ||
* @param {Object} [options.pool] - The min and max pool options. | ||
* @param {number} [options.pool.min] - The minimum number of db connection clients. | ||
* @param {number} [options.pool.max] - The maximum number of db connection clients. | ||
* @throws {Error} If pool is set up with more than one client in ':memory' case | ||
* @param {number} [options.pool.evictionRunIntervalMillis] - How often to run eviction checks. | ||
* @param {number} [options.pool.idleTimeoutMillisForPools] - The time interval in ms until an idle pool is evicted. | ||
* @throws {IllegalFunctionArgumentError} | ||
*/ | ||
const options = (options) => { | ||
options.host = options.host || options.database || options.url || ':memory:' | ||
const options = options => { | ||
options.credentials = options.credentials || {} | ||
options.credentials.database = options.credentials.database || options.database || options.host || options.url | ||
options.pool.min = options.pool.min || 1 | ||
options.pool.max = options.pool.max || ((options.host === ':memory:') ? 1 : 10) | ||
options.pool.max = options.pool.max || (options.credentials.database === ':memory:' ? 1 : 10) | ||
options.pool.evictionRunIntervalMillis = | ||
options.pool.evictionRunIntervalMillis || (options.credentials.database === ':memory:' ? 0 : 10000) | ||
options.pool.idleTimeoutMillisForPools = | ||
options.pool.idleTimeoutMillisForPools || (options.credentials.database === ':memory:' ? 0 : 60000) | ||
if (options.host === ':memory:' && options.pool.max !== 1) { | ||
throw new IllegalFunctionArgumentError('pool') | ||
} | ||
_validateDatabase(options.credentials.database) | ||
_validatePool(options) | ||
} | ||
module.exports = options |
const dependencies = { | ||
get Client () { | ||
const Client = require('./client').Client | ||
Object.defineProperty(dependencies, 'Client', {value: Client}) | ||
Object.defineProperty(dependencies, 'Client', { value: Client }) | ||
return Client | ||
@@ -9,3 +9,3 @@ }, | ||
const options = require('./client').options | ||
Object.defineProperty(dependencies, 'options', {value: options}) | ||
Object.defineProperty(dependencies, 'options', { value: options }) | ||
return options | ||
@@ -15,3 +15,3 @@ }, | ||
const SqliteStatement = require('./statement/SqliteStatement') | ||
Object.defineProperty(dependencies, 'SqliteStatement', {value: SqliteStatement}) | ||
Object.defineProperty(dependencies, 'SqliteStatement', { value: SqliteStatement }) | ||
return SqliteStatement | ||
@@ -18,0 +18,0 @@ }, |
@@ -1,2 +0,2 @@ | ||
const {IllegalFunctionArgumentError} = require('@sap/cds-sql').errors | ||
const { IllegalFunctionArgumentError } = require('@sap/cds-sql').errors | ||
@@ -68,12 +68,14 @@ class SqliteStatement { | ||
values.forEach((val) => { | ||
listPromise.push(new Promise((resolve, reject) => { | ||
this._stmt.all(val, (err) => { | ||
if (err) { | ||
return reject(err) | ||
} | ||
values.forEach(val => { | ||
listPromise.push( | ||
new Promise((resolve, reject) => { | ||
this._stmt.all(val, err => { | ||
if (err) { | ||
return reject(err) | ||
} | ||
resolve() | ||
resolve() | ||
}) | ||
}) | ||
})) | ||
) | ||
}) | ||
@@ -88,3 +90,3 @@ | ||
}) | ||
.catch((err) => { | ||
.catch(err => { | ||
this._stmt.finalize() // statement must be finalized also in case of error | ||
@@ -91,0 +93,0 @@ reject(err) |
{ | ||
"name": "@sap/cds-sqlite", | ||
"version": "0.9.0", | ||
"version": "1.0.3", | ||
"dependencies": { | ||
"@sap/cds-sql": { | ||
"version": "0.11.0" | ||
"version": "1.0.3" | ||
} | ||
} | ||
} |
@@ -1,1 +0,1 @@ | ||
{"bundleDependencies":false,"dependencies":{"@sap/cds-sql":"0.11.0"},"deprecated":false,"description":"Driver package for access to sqlite database, including setting up the client, configuring all the necessary options to initiate the connection and handling database specifics so that they can be processed on our end.","devDependencies":{"@sap/cds":"git://github.wdf.sap.corp/cdx/cds","jest":"^23.4.0","jest-junit":"^3.6.0","jsdoc-to-markdown":"^4.0.1","request":"2.83.0","sqlite3":"4.0.1","standard":"^11.0.0","standard-reporter":"^1.0.5"},"engines":{"node":">= 6.11.0"},"jest":{"testEnvironment":"node"},"jest-junit":{"suiteName":"jest tests","output":"reports/sonar/test-reporter.xml","classNameTemplate":"{classname}-{title}","titleTemplate":"{classname}-{title}","ancestorSeparator":" › ","usePathForSuiteName":"true"},"main":"lib/index.js","name":"@sap/cds-sqlite","scripts":{"build":"npm run test && npm run jsdoc2md","clean":"rm -rf reports && rm -f npm-debug.log","jsdoc2md":"jsdoc2md --param-list-format list lib/**/*.js > docs/api.md","lint":"([ -d reports ] || mkdir reports) && standard --env jest && standard | standard-reporter --checkstyle > reports/eslint.jslint.xml","test":"npm run clean && npm run lint && npm run test-unit && npm run test-integration","test-integration":"jest --config=jest-integration.json && [ -e reports/sonar/test-reporter.xml ] && mv reports/sonar/test-reporter.xml reports/sonar/test-integration.xml","test-unit":"jest --config=jest-unit.json && [ -e reports/sonar/test-reporter.xml ] && mv reports/sonar/test-reporter.xml reports/sonar/test-unit.xml"},"standard":{"env":["jest"],"globals":["jest"]},"version":"0.9.0","license":"SEE LICENSE IN developer-license-3.1.txt"} | ||
{"bundleDependencies":false,"dependencies":{"@sap/cds-sql":"1.0.3"},"deprecated":false,"description":"Driver package for access to sqlite database, including setting up the client, configuring all the necessary options to initiate the connection and handling database specifics so that they can be processed on our end.","devDependencies":{"husky":"^1.2.0","jest":"^23.6.0","jest-junit":"^5.2.0","jsdoc-to-markdown":"^4.0.1","lint-staged":"^8.1.0","prettier-standard":"^8.0.1","request":"^2.88.0","sqlite3":"4.0.4","standard":"^12.0.1","standard-reporter":"^1.0.5"},"engines":{"node":">= 8.0.0"},"husky":{"hooks":{"pre-commit":"lint-staged"}},"jest":{"testEnvironment":"node"},"jest-junit":{"suiteName":"jest tests","output":"reports/sonar/test-reporter.xml","classNameTemplate":"{classname}-{title}","titleTemplate":"{classname}-{title}","ancestorSeparator":" › ","usePathForSuiteName":"true"},"lint-staged":{"{lib,test}/**/*.js":["prettier-standard","standard --fix","git add"]},"main":"lib/index.js","name":"@sap/cds-sqlite","scripts":{"build":"npm run test && npm run jsdoc2md","clean":"rm -rf reports && rm -f npm-debug.log","jsdoc2md":"jsdoc2md --param-list-format list lib/**/*.js > docs/api.md","lint":"([ -d reports ] || mkdir reports) && standard --env jest && standard | standard-reporter --checkstyle > reports/eslint.jslint.xml","prepareRelease":"node node_modules/filter-node-package package.json","snapshots":"[ -e .pipeline/snapshots.sh ] && sh .pipeline/snapshots.sh","test":"npm run clean && npm run lint && npm run test-unit && npm run test-integration","test-integration":"jest --config=jest-integration.json && [ -e reports/sonar/test-reporter.xml ] && mv reports/sonar/test-reporter.xml reports/sonar/test-integration.xml","test-unit":"jest --config=jest-unit.json && [ -e reports/sonar/test-reporter.xml ] && mv reports/sonar/test-reporter.xml reports/sonar/test-unit.xml"},"standard":{"env":["jest"],"globals":["jest"]},"version":"1.0.3","license":"SEE LICENSE IN developer-license-3.1.txt"} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
43877
13
1
10
523
+ Added@sap/cds-sql@1.0.3(transitive)
- Removed@sap/cds-sql@0.11.0(transitive)
Updated@sap/cds-sql@1.0.3