@cap-js/sqlite 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +1 -184
- package/cds-plugin.js +5 -0
- package/index.js +1 -1
- package/lib/ReservedWords.json +149 -0
- package/lib/SQLiteService.js +215 -0
- package/package.json +23 -30
- package/cds.js +0 -39
- package/lib/db/DatabaseService.js +0 -102
- package/lib/db/sql/InsertResults.js +0 -87
- package/lib/db/sql/SQLService.js +0 -243
- package/lib/db/sql/copy.js +0 -17
- package/lib/db/sql/cqn2sql.js +0 -531
- package/lib/db/sql/cqn4sql.js +0 -1477
- package/lib/db/sql/deep.js +0 -230
- package/lib/db/sql/func.js +0 -147
- package/lib/db/sql/structuralComparisonOps.js +0 -16
- package/lib/db/sql/utils.js +0 -22
- package/lib/db/sql/workarounds.js +0 -63
- package/lib/db/sqlite/ReservedWords.json +0 -149
- package/lib/db/sqlite/SQLiteService.js +0 -175
- package/lib/ql/cds.infer.js +0 -857
- package/lib/ql/join-tree.js +0 -166
- package/lib/ql/pseudos.js +0 -23
package/cds.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
const cds = module.exports = require('@sap/cds/lib')
|
|
2
|
-
const { extend, lazified } = cds
|
|
3
|
-
|
|
4
|
-
extend(cds).with(lazified({
|
|
5
|
-
inferred: lazy => require('./lib/ql/cds.infer'),
|
|
6
|
-
cqn2sql: lazy => require('./lib/db/sql/cqn2sql'),
|
|
7
|
-
cqn4sql: lazy => require('./lib/db/sql/cqn4sql'),
|
|
8
|
-
}))
|
|
9
|
-
|
|
10
|
-
extend (cds.ql.Query) .with (class {
|
|
11
|
-
forSQL (db = cds.db || cds) { return this.flat(db.cqn4sql(this)) }
|
|
12
|
-
toSQL (db = cds.db || cds) { return _2sql(db.cqn2sql(this)) }
|
|
13
|
-
toSql (db = cds.db || cds) { return this.toSQL(db).sql }
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
// Monkey-patch req.event to be undefined for plain sql query strings -> remove when @sap/cds 6.6 is released
|
|
17
|
-
const $super = Reflect.getOwnPropertyDescriptor(cds.Request.prototype,'event')
|
|
18
|
-
Reflect.defineProperty (cds.Request.prototype,'event', {...$super, get(){
|
|
19
|
-
if (typeof this.query === 'string') return this._set ('event', undefined)
|
|
20
|
-
else return $super.get.call(this)
|
|
21
|
-
}})
|
|
22
|
-
|
|
23
|
-
// skip .cqn property when in repl
|
|
24
|
-
const _2sql = cds.repl ? ({sql,values}) => ({sql,values}) : x => x
|
|
25
|
-
|
|
26
|
-
cds.ApplicationService // FIXME: somehow we need to invoke the getter, must be removed in the future
|
|
27
|
-
|
|
28
|
-
// cds.requires.kinds.postgres = {
|
|
29
|
-
// kind: 'postgres',
|
|
30
|
-
// dialect: 'plain',
|
|
31
|
-
// credentials: {
|
|
32
|
-
// "host": "localhost",
|
|
33
|
-
// "port": "5432",
|
|
34
|
-
// "database": "beershop", //> override per test
|
|
35
|
-
// "username": "postgres",
|
|
36
|
-
// "password": "postgres"
|
|
37
|
-
// },
|
|
38
|
-
// impl: __dirname + '/lib/db/pg'
|
|
39
|
-
// }
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
|
|
3
|
-
function Pool (factory, tenant) { return createPool (
|
|
4
|
-
{ __proto__:factory, create: factory.create.bind(undefined,tenant) },
|
|
5
|
-
factory.options
|
|
6
|
-
)}
|
|
7
|
-
const { createPool } = require('@sap/cds-foss').pool
|
|
8
|
-
|
|
9
|
-
class DatabaseService extends cds.Service {
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Return a pool factory + options property as expected by
|
|
13
|
-
* https://github.com/coopernurse/node-pool#createpool.
|
|
14
|
-
*/
|
|
15
|
-
get factory(){ throw '2b overriden in subclass' }
|
|
16
|
-
pools = { _factory: this.factory }
|
|
17
|
-
|
|
18
|
-
get isMultitenant() { return (
|
|
19
|
-
'multiTenant' in this.options ? this.options.multiTenant
|
|
20
|
-
: cds.env.requires.multitenancy
|
|
21
|
-
)}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Set one or more session context variables like so:
|
|
25
|
-
* ```js
|
|
26
|
-
* const tx = cds.db.tx()
|
|
27
|
-
* tx.set({
|
|
28
|
-
* '$user.name': 'Alice',
|
|
29
|
-
* '$user.role': 'admin'
|
|
30
|
-
* })
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
// eslint-disable-next-line no-unused-vars
|
|
34
|
-
set (variables) { throw '2b overridden by subclass' }
|
|
35
|
-
|
|
36
|
-
infer (q, m = this.model) {
|
|
37
|
-
return cds.inferred(q,m)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async begin() {
|
|
41
|
-
const ctx = this.context; if (!ctx) return this.tx().begin()
|
|
42
|
-
const tenant = this.isMultitenant && ctx.tenant
|
|
43
|
-
const pool = this.pools[tenant] ??= new Pool (this.pools._factory, tenant)
|
|
44
|
-
const dbc = this.dbc = await pool.acquire()
|
|
45
|
-
this._release = (dbc) => pool.release(dbc)
|
|
46
|
-
try {
|
|
47
|
-
// Setting session context variables
|
|
48
|
-
await this.set({
|
|
49
|
-
get '$user.id'(){ return _set (this, '$user.id', ctx.user?.id || 'anonymous') },
|
|
50
|
-
get '$user.locale'(){ return _set (this, '$user.locale', ctx.locale || cds.env.i18n.default_language) },
|
|
51
|
-
get '$valid.from'(){ return _set (this, '$valid.from', ctx._?.['VALID-FROM']?.toISOString() || ctx._?.['VALID-AT']?.toISOString() || '1970-01-01T00:00:00Z') },
|
|
52
|
-
get '$valid.to'(){ return _set (this, '$valid.to', ctx._?.['VALID-TO']?.toISOString() || _validTo4(ctx._?.['VALID-AT'])?.toISOString() || '9999-11-11T22:22:22Z') },
|
|
53
|
-
})
|
|
54
|
-
// Run BEGIN
|
|
55
|
-
await this.send('BEGIN')
|
|
56
|
-
} catch (e) { this._release(dbc); throw e }
|
|
57
|
-
return this
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async commit() {
|
|
61
|
-
const dbc = this.dbc; if (!dbc) return
|
|
62
|
-
await this.send('COMMIT')
|
|
63
|
-
this._release(dbc) // only release on successful commit as otherwise released on rollback
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async rollback() {
|
|
67
|
-
const dbc = this.dbc; if (!dbc) return
|
|
68
|
-
try { await this.send('ROLLBACK') }
|
|
69
|
-
finally { this._release(dbc) }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// REVISIT: should happen automatically after a configurable time
|
|
73
|
-
async disconnect (tenant) {
|
|
74
|
-
const pool = this.pools[tenant]
|
|
75
|
-
if (pool) delete this.pools[tenant]; else return
|
|
76
|
-
await pool.drain()
|
|
77
|
-
await pool.clear()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
run (query, data, ...etc) {
|
|
81
|
-
// Allow db.run('...',1,2,3,4)
|
|
82
|
-
if (data !== undefined && typeof query === 'string' && typeof data !== 'object') data = [ data, ...etc ]
|
|
83
|
-
return super.run (query, data)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
url4 (tenant) { // eslint-disable-line no-unused-vars
|
|
87
|
-
let { url } = this.options?.credentials || this.options || {}
|
|
88
|
-
return url
|
|
89
|
-
}
|
|
90
|
-
getDbUrl (tenant) { return this.url4 (tenant) } // REVISIT: Remove after cds v6.7
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const _set = (context, variable, value) => {
|
|
94
|
-
Object.defineProperty (context, variable, { value, configurable:true })
|
|
95
|
-
return value
|
|
96
|
-
}
|
|
97
|
-
const _validTo4 = validAt => {
|
|
98
|
-
return validAt?.replace(/(\dZ?)$/,d=>parseInt(d[0])+1+d[1]||'')
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
DatabaseService.prototype.isDatabaseService = true
|
|
102
|
-
module.exports = DatabaseService
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
const iterator = Symbol.iterator
|
|
2
|
-
|
|
3
|
-
// eslint-disable-next-line no-unused-vars
|
|
4
|
-
const USAGE_SAMPLE = async ()=>{ // from https://pages.github.tools.sap/cap/docs/node.js/services?q=Emily#srvrun--query--results
|
|
5
|
-
const { Authors, Books } = {}
|
|
6
|
-
const [ Emily, Charlotte ] = await INSERT.into (Authors, [
|
|
7
|
-
{ name: 'Emily Brontëe' },
|
|
8
|
-
{ name: 'Charlotte Brontëe' },
|
|
9
|
-
])
|
|
10
|
-
await INSERT.into (Books, [
|
|
11
|
-
{ title: 'Wuthering Heights', author: Emily },
|
|
12
|
-
{ title: 'Jane Eyre', author: Charlotte },
|
|
13
|
-
])
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
module.exports = class InsertResult {
|
|
18
|
-
|
|
19
|
-
constructor (query, results) {
|
|
20
|
-
this.query = query
|
|
21
|
-
this.results = results
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/*
|
|
25
|
-
* Lazy access to auto-generated keys.
|
|
26
|
-
*/
|
|
27
|
-
get [iterator]() {
|
|
28
|
-
|
|
29
|
-
// For INSERT.as(SELECT.from(...)) return a dummy iterator with correct length
|
|
30
|
-
const { INSERT } = this.query
|
|
31
|
-
if (INSERT.as) {
|
|
32
|
-
return super[iterator] = function*(){
|
|
33
|
-
for (let i = 0; i < this.affectedRows; i++) yield {}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const { target } = this.query; if (!target?.keys) return (super[iterator] = this.results[iterator])
|
|
38
|
-
const keys = Object.keys(target.keys), [k1] = keys
|
|
39
|
-
|
|
40
|
-
// For INSERT.entries() with generated keys in there return these keys
|
|
41
|
-
const { entries } = INSERT; if (entries && k1 in entries[0]) {
|
|
42
|
-
return super[iterator] = function*(){
|
|
43
|
-
for (const each of entries)
|
|
44
|
-
yield keys.reduce ((p,k) => { p[k] = each[k]; return p },{})
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// For INSERT.rows/values() with generated keys in there return these keys
|
|
49
|
-
const { columns } = INSERT; if (columns && columns.includes(k1)) {
|
|
50
|
-
return super[iterator] = function*(){
|
|
51
|
-
const indices = keys.reduce ((p,k) => {
|
|
52
|
-
let i = columns.indexOf(k); if (i >= 0) p[k] = i
|
|
53
|
-
return p
|
|
54
|
-
},{})
|
|
55
|
-
for (const each of INSERT.rows || [INSERT.values])
|
|
56
|
-
yield keys.reduce ((p,k) => { p[k] = each[indices[k]]; return p },{})
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// If no generated keys in entries/rows/values we might have database-generated keys
|
|
61
|
-
const rows = this.results.slice(0, this.affectedRows) // only up to # of root entries
|
|
62
|
-
return super[iterator] = function*(){
|
|
63
|
-
for (const each of rows)
|
|
64
|
-
yield { [k1]: this.insertedRowId4(each) } // REVISIT: sqlite only returns a single lastID per row -> how is that with others?
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/*
|
|
69
|
-
* the number of inserted (root) entries or the number of affectedRows in case of INSERT into SELECT
|
|
70
|
-
*/
|
|
71
|
-
get affectedRows() {
|
|
72
|
-
const { INSERT:_ } = this.query
|
|
73
|
-
if (_.as) return super.affectedRows = this.affectedRows4(this.results[0]|| this.results)
|
|
74
|
-
else return super.affectedRows = _.entries?.length || _.rows?.length || this.results.length || 1
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/*
|
|
78
|
-
* for checks such as res > 2
|
|
79
|
-
*/
|
|
80
|
-
valueOf() {
|
|
81
|
-
return this.affectedRows
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
insertedRowId4 (result) { return result.lastID }
|
|
85
|
-
affectedRows4 (result) { return result.changes }
|
|
86
|
-
|
|
87
|
-
}
|
package/lib/db/sql/SQLService.js
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
const cds = require('../../../cds'), DEBUG = cds.debug('sql|db')
|
|
2
|
-
const { resolveView } = require('@sap/cds/libx/_runtime/common/utils/resolveView')
|
|
3
|
-
const DatabaseService = require('../DatabaseService')
|
|
4
|
-
const cqn4sql = require('./cqn4sql')
|
|
5
|
-
const { target_name4 } = require('./utils')
|
|
6
|
-
const { PassThrough, pipeline } = require('stream')
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SQLService extends DatabaseService {
|
|
10
|
-
|
|
11
|
-
init() {
|
|
12
|
-
this.on([ 'INSERT', 'UPSERT', 'UPDATE', 'DELETE' ], require('./workarounds').input) // REVISIT should be replaced by correct input processing eventually
|
|
13
|
-
this.on([ 'INSERT', 'UPSERT', 'UPDATE', 'DELETE' ], require('./deep').onDeep)
|
|
14
|
-
this.on([ 'SELECT' ], this.onSELECT)
|
|
15
|
-
this.on([ 'INSERT' ], this.onINSERT)
|
|
16
|
-
this.on([ 'UPSERT' ], this.onUPSERT)
|
|
17
|
-
this.on([ 'UPDATE' ], this.onUPDATE)
|
|
18
|
-
this.on([ 'DELETE', 'CREATE ENTITY', 'DROP ENTITY' ], this.onSIMPLE)
|
|
19
|
-
this.on([ 'BEGIN', 'COMMIT', 'ROLLBACK' ], this.onEVENT)
|
|
20
|
-
this.on([ '*' ], this.onPlainSQL)
|
|
21
|
-
return super.init()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Handler for SELECT */
|
|
25
|
-
async onSELECT ({ query, data }) {
|
|
26
|
-
// REVISIT: disable this for queries like (SELECT 1)
|
|
27
|
-
// Will return multiple rows with objects inside
|
|
28
|
-
// REVISIT: streaming: if we need custom app and db handlers with app stream and cds.stream
|
|
29
|
-
if (query._streaming) return this.onStream(query) // TODO: implemented on HANA
|
|
30
|
-
query.SELECT.expand = 'root'
|
|
31
|
-
const { sql, values, cqn } = this.cqn2sql (query, data)
|
|
32
|
-
let ps = await this.prepare(sql)
|
|
33
|
-
let rows = await ps.all(values)
|
|
34
|
-
if (rows.length)
|
|
35
|
-
if (cqn.SELECT.expand) rows = rows.map(r => typeof r._json_ === 'string' ? JSON.parse(r._json_) : r._json_ || r)
|
|
36
|
-
if (cqn.SELECT.count) rows.$count = await this.count(query, rows)
|
|
37
|
-
return cqn.SELECT.one || query.SELECT.from.ref?.[0].cardinality?.max === 1 ? rows[0] || null : rows
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async onINSERT ({ query, data }) {
|
|
41
|
-
const { sql, entries, cqn } = this.cqn2sql (query, data)
|
|
42
|
-
if(!sql) return // Do nothing when there is nothing to be done
|
|
43
|
-
const ps = await this.prepare(sql)
|
|
44
|
-
const results = entries ? await Promise.all(entries.map(e => ps.run(e))) : await ps.run()
|
|
45
|
-
return new this.class.InsertResults (cqn, results)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async onUPSERT ({ query, data }) {
|
|
49
|
-
const { sql, entries } = this.cqn2sql (query, data)
|
|
50
|
-
if(!sql) return // Do nothing when there is nothing to be done
|
|
51
|
-
const ps = await this.prepare(sql)
|
|
52
|
-
const results = entries ? await Promise.all(entries.map(e => ps.run(e))) : await ps.run()
|
|
53
|
-
return results.reduce((lastValue, currentValue) => lastValue += currentValue.changes, 0)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Handler for UPDATE */
|
|
57
|
-
async onUPDATE (req) {
|
|
58
|
-
return this.onSIMPLE(req)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Handler for CREATE, DROP, UPDATE, DELETE, with simple CQN */
|
|
62
|
-
async onSIMPLE ({ query, data }) {
|
|
63
|
-
const { sql, values } = this.cqn2sql (query, data)
|
|
64
|
-
let ps = await this.prepare(sql)
|
|
65
|
-
return (await ps.run(values)).changes
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Handler for BEGIN, COMMIT, ROLLBACK, which don't have any CQN */
|
|
69
|
-
async onEVENT ({ event }) {
|
|
70
|
-
DEBUG?.(event) // in the other cases above DEBUG happens in cqn2sql
|
|
71
|
-
return await this.exec(event)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Handler for SQL statements which don't have any CQN */
|
|
75
|
-
async onPlainSQL ({ query, data }, next) {
|
|
76
|
-
if (typeof query === 'string') {
|
|
77
|
-
DEBUG?.(query)
|
|
78
|
-
const ps = await this.prepare(query)
|
|
79
|
-
const exec = this.hasResults(query) ? d => ps.all(d) : d => ps.run(d)
|
|
80
|
-
if (Array.isArray(data) && typeof data[0] === 'object')
|
|
81
|
-
return await Promise.all(data.map(exec))
|
|
82
|
-
else return exec(data)
|
|
83
|
-
}
|
|
84
|
-
else return next()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Override in subclasses to detect more statements to be called with ps.all() */
|
|
88
|
-
hasResults(sql) {
|
|
89
|
-
return /^(SELECT|WITH|CALL|PRAGMA table_info)/.test(sql)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
/** Derives and executes a query to fill in `$count` for given query */
|
|
94
|
-
async count (query, ret) {
|
|
95
|
-
if (ret) {
|
|
96
|
-
const { one, limit:_ } = query.SELECT, n = ret.length
|
|
97
|
-
const [ max, offset=0 ] = one ? [1] : _ ? [ _.rows?.val, _.offset?.val ] : []
|
|
98
|
-
if (max === undefined || n < max && (n || !offset)) return n + offset
|
|
99
|
-
}
|
|
100
|
-
const cq = cds.ql.clone (query, {
|
|
101
|
-
columns: [{func:'count'}],
|
|
102
|
-
localized: false,
|
|
103
|
-
expand: false,
|
|
104
|
-
limit: 0,
|
|
105
|
-
orderBy: 0
|
|
106
|
-
})
|
|
107
|
-
const { sql, values } = this.cqn2sql(cq)
|
|
108
|
-
const ps = await this.prepare(sql)
|
|
109
|
-
const { count } = await ps.get(values)
|
|
110
|
-
return count
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Streaming
|
|
115
|
-
* Returns either a readable stream for sync calls or a readable stream promise for async calls
|
|
116
|
-
*/
|
|
117
|
-
stream(q) {
|
|
118
|
-
return typeof q === 'object'
|
|
119
|
-
// aynchronous API: cds.stream(query)
|
|
120
|
-
? this.run(Object.assign(q, { _streaming: true }))
|
|
121
|
-
// synchronous API: cds.stream('column').from(entity).where(...)
|
|
122
|
-
: new StreamCQN(q, this)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
static InsertResults = require('./InsertResults')
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Helper class implementing {@link SQLService#cqn2sql}.
|
|
130
|
-
* Subclasses commonly override this.
|
|
131
|
-
*/
|
|
132
|
-
static CQN2SQL = require('./cqn2sql').class
|
|
133
|
-
constructor() {
|
|
134
|
-
super(...arguments)
|
|
135
|
-
this.class = new.target // for IntelliSense
|
|
136
|
-
}
|
|
137
|
-
cqn2sql(q,values) {
|
|
138
|
-
const cqn = this.cqn4sql(q)
|
|
139
|
-
|
|
140
|
-
const cmd = cqn.cmd || Object.keys(cqn)[0]
|
|
141
|
-
if (cmd in { INSERT:1, DELETE: 1, UPSERT: 1, UPDATE: 1 }) {
|
|
142
|
-
let resolvedCqn = resolveView(cqn, this.model, this)
|
|
143
|
-
if (resolvedCqn && resolvedCqn[cmd]._transitions?.[0].target) {
|
|
144
|
-
resolvedCqn = resolvedCqn || cqn
|
|
145
|
-
resolvedCqn.target = resolvedCqn?.[cmd]._transitions[0].target || cqn.target
|
|
146
|
-
}
|
|
147
|
-
return (new this.class.CQN2SQL(this.context)) .render (resolvedCqn, values)
|
|
148
|
-
}
|
|
149
|
-
return (new this.class.CQN2SQL(this.context)) .render (cqn, values)
|
|
150
|
-
}
|
|
151
|
-
cqn4sql(q) {
|
|
152
|
-
// REVISIT: move this check to cqn4sql?
|
|
153
|
-
if (!q.SELECT?.from?.join && !this.model?.definitions[target_name4(q)]) return _unquirked(q)
|
|
154
|
-
return cqn4sql (q, this.model)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Returns a Promise which resolves to a prepared statement object with
|
|
159
|
-
* `{run,get,all}` signature as specified in {@link PreparedStatement}.
|
|
160
|
-
* @returns {PreparedStatement}
|
|
161
|
-
*/
|
|
162
|
-
// eslint-disable-next-line no-unused-vars
|
|
163
|
-
async prepare (sql) { throw '2b overridden by subclass' }
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Used to execute simple SQL statement like BEGIN, COMMIT, ROLLBACK
|
|
167
|
-
*/
|
|
168
|
-
// eslint-disable-next-line no-unused-vars
|
|
169
|
-
async exec (sql) { throw '2b overridden by subclass' }
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
/** Interface of prepared statement objects as returned by {@link SQLService#prepare} */
|
|
175
|
-
class PreparedStatement { // eslint-disable-line no-unused-vars
|
|
176
|
-
/**
|
|
177
|
-
* Executes a prepared DML query, i.e., INSERT, UPDATE, DELETE, CREATE, DROP
|
|
178
|
-
* @param {[]|{}} binding_params
|
|
179
|
-
*/
|
|
180
|
-
async run (binding_params) {} // eslint-disable-line no-unused-vars
|
|
181
|
-
/**
|
|
182
|
-
* Executes a prepared SELECT query and returns a single/first row only
|
|
183
|
-
* @param {[]|{}} binding_params
|
|
184
|
-
*/
|
|
185
|
-
async get (binding_params) { return {} } // eslint-disable-line no-unused-vars
|
|
186
|
-
/**
|
|
187
|
-
* Executes a prepared SELECT query and returns an array of all rows
|
|
188
|
-
* @param {[]|{}} binding_params
|
|
189
|
-
*/
|
|
190
|
-
async all (binding_params) { return [{}] } // eslint-disable-line no-unused-vars
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Class that builds and runs stream CQN
|
|
195
|
-
*/
|
|
196
|
-
class StreamCQN {
|
|
197
|
-
constructor (column, srv) {
|
|
198
|
-
this.column = column
|
|
199
|
-
this.srv = srv
|
|
200
|
-
this.result = new PassThrough()
|
|
201
|
-
}
|
|
202
|
-
/** synchronous streaming API: returns readable stream or class instance for chaining */
|
|
203
|
-
from (...args) {
|
|
204
|
-
this.sq = SELECT.from(...args)
|
|
205
|
-
this.sq._streaming = true
|
|
206
|
-
if (this.column) this.sq.columns([this.column])
|
|
207
|
-
const ref = this.sq.SELECT.from.ref
|
|
208
|
-
if (!ref?.[ref.length - 1].where) return this
|
|
209
|
-
this._runStream()
|
|
210
|
-
return this.result
|
|
211
|
-
}
|
|
212
|
-
/** synchronous streaming API: returns readable stream */
|
|
213
|
-
where (...args) {
|
|
214
|
-
this.sq.where(...args)
|
|
215
|
-
this._runStream()
|
|
216
|
-
return this.result
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async _runStream() {
|
|
220
|
-
try {
|
|
221
|
-
const stream = await this.srv.run(this.sq)
|
|
222
|
-
// In case of streaming error while streaming from stream to this.result
|
|
223
|
-
// the error is emitted to both streams. After this the output stream this.result is destroyed.
|
|
224
|
-
// No explicit closing of this.result is needed.
|
|
225
|
-
// In (theoretical) case if for some error this.result is not destroyed the code like below can be used
|
|
226
|
-
// as callback: err => err && this.result.push(null)
|
|
227
|
-
stream ? pipeline(stream, this.result, () => {}) : this.result.push(null)
|
|
228
|
-
}
|
|
229
|
-
catch (err) { this.result.emit('error', err); this.result.push(null) }
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const _unquirked = q => {
|
|
234
|
-
if (typeof q.INSERT?.into === 'string') q.INSERT.into = {ref:[q.INSERT.into]}
|
|
235
|
-
if (typeof q.UPSERT?.into === 'string') q.UPSERT.into = {ref:[q.UPSERT.into]}
|
|
236
|
-
if (typeof q.UPDATE?.entity === 'string') q.UPDATE.entity = {ref:[q.UPDATE.entity]}
|
|
237
|
-
if (typeof q.DELETE?.from === 'string') q.DELETE.from = {ref:[q.DELETE.from]}
|
|
238
|
-
if (typeof q.CREATE?.entity === 'string') q.CREATE.entity = {ref:[q.CREATE.entity]}
|
|
239
|
-
if (typeof q.DROP?.entity === 'string') q.DROP.entity = {ref:[q.DROP.entity]}
|
|
240
|
-
return q
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
module.exports = SQLService
|
package/lib/db/sql/copy.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module.exports.copy = function (obj) {
|
|
2
|
-
const walk = function (par, prop) {
|
|
3
|
-
const val = prop ? par[prop] : par
|
|
4
|
-
|
|
5
|
-
// If value is native return
|
|
6
|
-
if (typeof val !== 'object' || val == null || val instanceof RegExp || val instanceof Date || val instanceof Buffer)
|
|
7
|
-
return val
|
|
8
|
-
|
|
9
|
-
const ret = Array.isArray(val) ? [] : {}
|
|
10
|
-
Object.keys(val).forEach(k => {
|
|
11
|
-
ret[k] = walk(val, k)
|
|
12
|
-
})
|
|
13
|
-
return ret
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return walk(obj)
|
|
17
|
-
}
|