@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/lib/db/sql/deep.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
const cds = require('@sap/cds')
|
|
2
|
-
const { compareJson } = require('@sap/cds/libx/_runtime/cds-services/services/utils/compareJson')
|
|
3
|
-
const { target_name4 } = require('./utils')
|
|
4
|
-
|
|
5
|
-
const handledDeep = Symbol('handledDeep')
|
|
6
|
-
|
|
7
|
-
async function onDeep (req, next) {
|
|
8
|
-
const { query } = req
|
|
9
|
-
// REVISIT: req.target does not match the query.INSERT target for path insert
|
|
10
|
-
// cds.inferred(query)
|
|
11
|
-
// const target = query.sources[Object.keys(query.sources)[0]]
|
|
12
|
-
if (!this.model?.definitions[target_name4(req.query)]) {
|
|
13
|
-
return next()
|
|
14
|
-
}
|
|
15
|
-
const {target} = this.infer(query)
|
|
16
|
-
if (!hasDeep(query, target)) return next()
|
|
17
|
-
const beforeData = query.INSERT ? [] : await this.run(getExpandForDeep(query, target, true))
|
|
18
|
-
const queries = getDeepQueries(query, beforeData, target)
|
|
19
|
-
const res = await Promise.all(queries.map (query => {
|
|
20
|
-
if (query.INSERT) return this.onINSERT({query})
|
|
21
|
-
if (query.UPDATE) return this.onUPDATE({query})
|
|
22
|
-
if (query.DELETE) return this.onSIMPLE({query})
|
|
23
|
-
}))
|
|
24
|
-
return res[0] ?? 0 // TODO what todo with multiple result responses?
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const hasDeep = (query, target) => {
|
|
29
|
-
if (handledDeep in query) return
|
|
30
|
-
if (query.DELETE) {
|
|
31
|
-
for (let c in target?.compositions) return true
|
|
32
|
-
return false
|
|
33
|
-
}
|
|
34
|
-
const data = query.INSERT?.entries || (query.UPDATE?.data && [query.UPDATE.data]) || (query.UPDATE?.with && [query.UPDATE.with])
|
|
35
|
-
if (data) for (const c in target.compositions) {
|
|
36
|
-
for (const row of data) if (row[c] !== undefined) return true
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// unofficial config!
|
|
41
|
-
const DEEP_DELETE_MAX_RECURSION_DEPTH =
|
|
42
|
-
(cds.env.features.recursion_depth && Number(cds.env.features.recursion_depth)) || 4 // we use 4 here as our test data has a max depth of 3
|
|
43
|
-
|
|
44
|
-
// IMPORTANT: Skip only if @cds.persistence.skip is `true` → e.g. this skips skipping targets marked with @cds.persistence.skip: 'if-unused'
|
|
45
|
-
const _hasPersistenceSkip = target => target?.["@cds.persistence.skip"] === true
|
|
46
|
-
|
|
47
|
-
const getColumnsFromDataOrKeys = (data, target) => {
|
|
48
|
-
if (Array.isArray(data)) {
|
|
49
|
-
// loop and get all columns from current level
|
|
50
|
-
const columns = new Set()
|
|
51
|
-
data.forEach(row =>
|
|
52
|
-
Object.keys(row || target.keys)
|
|
53
|
-
.filter(propName => !target.elements[propName]?.isAssociation)
|
|
54
|
-
.forEach(entry => {
|
|
55
|
-
columns.add(entry)
|
|
56
|
-
})
|
|
57
|
-
)
|
|
58
|
-
return Array.from(columns).map(c => ({ ref: [c] }))
|
|
59
|
-
} else {
|
|
60
|
-
// get all columns from current level
|
|
61
|
-
return Object.keys(data || target.keys)
|
|
62
|
-
.filter(propName => !target.elements[propName]?.isAssociation)
|
|
63
|
-
.map(c => ({ ref: [c] }))
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const _calculateExpandColumns = (target, data, expandColumns = [], elementMap = new Map()) => {
|
|
68
|
-
const compositions = target.compositions || {}
|
|
69
|
-
|
|
70
|
-
if(expandColumns.length === 0) {
|
|
71
|
-
// REVISIT: ensure that all keys are included in the expand columns
|
|
72
|
-
expandColumns.push(...getColumnsFromDataOrKeys(data, target))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
for (const compName in compositions) {
|
|
76
|
-
let compositionData
|
|
77
|
-
if (data === null || (Array.isArray(data) && !data.length)) {
|
|
78
|
-
compositionData = null
|
|
79
|
-
} else {
|
|
80
|
-
compositionData = data[compName]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ignore not provided compositions as nothing happens with them (expect deep delete)
|
|
84
|
-
if (compositionData === undefined) {
|
|
85
|
-
// fill columns in case
|
|
86
|
-
continue
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const composition = compositions[compName]
|
|
90
|
-
|
|
91
|
-
const fqn = composition.parent.name + ':' + composition.name
|
|
92
|
-
const seen = elementMap.get(fqn)
|
|
93
|
-
if (seen && seen >= DEEP_DELETE_MAX_RECURSION_DEPTH) {
|
|
94
|
-
// recursion -> abort
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
let expandColumn = expandColumns.find(expandColumn => expandColumn.ref[0] === composition.name)
|
|
99
|
-
if (!expandColumn) {
|
|
100
|
-
expandColumn = {
|
|
101
|
-
ref: [composition.name],
|
|
102
|
-
expand: getColumnsFromDataOrKeys(compositionData, composition._target)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
expandColumns.push(expandColumn)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// expand deep
|
|
109
|
-
// Make a copy and do not share the same map among brother compositions
|
|
110
|
-
// as we're only interested in deep recursions, not wide recursions.
|
|
111
|
-
const newElementMap = new Map(elementMap)
|
|
112
|
-
newElementMap.set(fqn, (seen && seen + 1) || 1)
|
|
113
|
-
|
|
114
|
-
if (composition.is2many) {
|
|
115
|
-
// expandColumn.expand = getColumnsFromDataOrKeys(compositionData, composition._target)
|
|
116
|
-
if (compositionData === null || compositionData.length === 0) {
|
|
117
|
-
// deep delete, get all subitems until recursion depth
|
|
118
|
-
_calculateExpandColumns(composition._target, null, expandColumn.expand, newElementMap)
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
for (const row of compositionData) {
|
|
123
|
-
_calculateExpandColumns(composition._target, row, expandColumn.expand, newElementMap)
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
126
|
-
// to one
|
|
127
|
-
_calculateExpandColumns(composition._target, compositionData, expandColumn.expand, newElementMap)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const getExpandForDeep = (query, target) => {
|
|
133
|
-
const from = query.DELETE?.from || query.UPDATE?.entity
|
|
134
|
-
const data = query.UPDATE?.data || null
|
|
135
|
-
const where = query.DELETE?.where || query.UPDATE?.where
|
|
136
|
-
|
|
137
|
-
const cqn = SELECT.from(from)
|
|
138
|
-
if (where) cqn.SELECT.where = where
|
|
139
|
-
|
|
140
|
-
const columns = []
|
|
141
|
-
_calculateExpandColumns(target, data, columns)
|
|
142
|
-
cqn.columns(columns)
|
|
143
|
-
return cqn
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const getDeepQueries = (query, dbData, target) => {
|
|
147
|
-
let queryData
|
|
148
|
-
if (query.INSERT) {
|
|
149
|
-
queryData = query.INSERT.entries
|
|
150
|
-
}
|
|
151
|
-
if (query.DELETE) {
|
|
152
|
-
queryData = []
|
|
153
|
-
}
|
|
154
|
-
if (query.UPDATE) {
|
|
155
|
-
queryData = [query.UPDATE.data]
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let diff = compareJson(queryData, dbData, target)
|
|
159
|
-
if (!Array.isArray(diff)) {
|
|
160
|
-
diff = [diff]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return _getDeepQueries(diff, target)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const _getDeepQueries = (diff, target) => {
|
|
167
|
-
const queries = []
|
|
168
|
-
|
|
169
|
-
for (const diffEntry of diff) {
|
|
170
|
-
if (diffEntry === undefined) continue
|
|
171
|
-
const subQueries = []
|
|
172
|
-
|
|
173
|
-
for (const prop in diffEntry) {
|
|
174
|
-
// handle deep operations
|
|
175
|
-
|
|
176
|
-
const propData = diffEntry[prop]
|
|
177
|
-
|
|
178
|
-
if (target.elements[prop] && _hasPersistenceSkip(target.elements[prop]._target)) {
|
|
179
|
-
delete diffEntry[prop]
|
|
180
|
-
} else if (target.compositions?.[prop]) {
|
|
181
|
-
const arrayed = Array.isArray(propData) ? propData : [propData]
|
|
182
|
-
arrayed.forEach(subEntry => {
|
|
183
|
-
subQueries.push(..._getDeepQueries([subEntry], target.elements[prop]._target))
|
|
184
|
-
})
|
|
185
|
-
delete diffEntry[prop]
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// handle current entity level
|
|
190
|
-
const op = diffEntry._op
|
|
191
|
-
delete diffEntry._op
|
|
192
|
-
|
|
193
|
-
if (diffEntry._old != null) {
|
|
194
|
-
delete diffEntry._old
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// first calculate subqueries and rm their properties, then build root query
|
|
198
|
-
if (op === 'create') {
|
|
199
|
-
queries.push(INSERT.into(target).entries(diffEntry))
|
|
200
|
-
} else if (op === 'delete') {
|
|
201
|
-
queries.push(DELETE.from(target).where(diffEntry))
|
|
202
|
-
} else if (op === 'update') {
|
|
203
|
-
// TODO do we need the where here?
|
|
204
|
-
const keys = target.keys
|
|
205
|
-
const cqn = UPDATE(target).with(diffEntry)
|
|
206
|
-
for (const key in keys) {
|
|
207
|
-
if (keys[key].virtual) continue
|
|
208
|
-
if (!keys[key].isAssociation) {
|
|
209
|
-
cqn.where(key + '=', diffEntry[key])
|
|
210
|
-
}
|
|
211
|
-
delete diffEntry[key]
|
|
212
|
-
}
|
|
213
|
-
cqn.with(diffEntry)
|
|
214
|
-
queries.push(cqn)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
queries.push(...subQueries)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
queries.forEach(q => {
|
|
221
|
-
Object.defineProperty(q, handledDeep, { value: true })
|
|
222
|
-
})
|
|
223
|
-
return queries
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
module.exports = {
|
|
227
|
-
onDeep,
|
|
228
|
-
getDeepQueries,
|
|
229
|
-
getExpandForDeep
|
|
230
|
-
}
|
package/lib/db/sql/func.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
const StandardFunctions = {
|
|
2
|
-
|
|
3
|
-
// OData: https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_CanonicalFunctions
|
|
4
|
-
|
|
5
|
-
// String and Collection Functions
|
|
6
|
-
// length : (x) => `length(${x})`,
|
|
7
|
-
search: function(ref, arg) {
|
|
8
|
-
if (!('val' in arg)) throw `SQLite only supports single value arguments for $search`
|
|
9
|
-
const refs = ref.list || [ref], {toString} = ref
|
|
10
|
-
return '(' + refs.map(ref2 => this.contains(this.tolower(toString(ref2)),this.tolower(arg))).join(' or ') + ')'
|
|
11
|
-
},
|
|
12
|
-
concat : (...args) => args.join('||'),
|
|
13
|
-
contains : (...args) => `ifnull(instr(${args}),0)`,
|
|
14
|
-
count : (x) => `count(${x||'*'})`,
|
|
15
|
-
countdistinct : (x) => `count(distinct ${x||'*'})`,
|
|
16
|
-
indexof : (x,y) => `instr(${x},${y}) - 1`, // sqlite instr is 1 indexed
|
|
17
|
-
startswith : (x,y) => `instr(${x},${y}) = 1`, // sqlite instr is 1 indexed
|
|
18
|
-
endswith : (x,y) => `instr(${x},${y}) = length(${x}) - length(${y}) +1`,
|
|
19
|
-
substring : (x,y,z) => ( z
|
|
20
|
-
? `substr( ${x}, case when ${y} < 0 then length(${x}) + ${y} + 1 else ${y} + 1 end, ${z} )`
|
|
21
|
-
: `substr( ${x}, case when ${y} < 0 then length(${x}) + ${y} + 1 else ${y} + 1 end )`
|
|
22
|
-
),
|
|
23
|
-
|
|
24
|
-
// String Functions
|
|
25
|
-
matchesPattern : (x,y) => `${x} regexp ${y})`,
|
|
26
|
-
tolower : (x) => `lower(${x})`,
|
|
27
|
-
toupper : (x) => `upper(${x})`,
|
|
28
|
-
// trim : (x) => `trim(${x})`,
|
|
29
|
-
|
|
30
|
-
// Arithmetic Functions
|
|
31
|
-
ceiling : (x) => `ceil(${x})`,
|
|
32
|
-
// floor : (x) => `floor(${x})`,
|
|
33
|
-
// round : (x) => `round(${x})`,
|
|
34
|
-
|
|
35
|
-
// Date and Time Functions
|
|
36
|
-
year : (x) => `cast( strftime('%Y',${x}) as Integer )`,
|
|
37
|
-
month : (x) => `cast( strftime('%m',${x}) as Integer )`,
|
|
38
|
-
day : (x) => `cast( strftime('%d',${x}) as Integer )`,
|
|
39
|
-
hour : (x) => `cast( strftime('%H',${x}) as Integer )`,
|
|
40
|
-
minute : (x) => `cast( strftime('%M',${x}) as Integer )`,
|
|
41
|
-
second : (x) => `cast( strftime('%S',${x}) as Integer )`,
|
|
42
|
-
|
|
43
|
-
fractionalseconds : (x) => `cast( strftime('%f',${x}) as Integer )`,
|
|
44
|
-
|
|
45
|
-
maxdatetime : ()=> '9999-12-31 23:59:59.999',
|
|
46
|
-
mindatetime : ()=> '0001-01-01 00:00:00.000',
|
|
47
|
-
|
|
48
|
-
// odata spec defines the date time offset type as a normal ISO time stamp
|
|
49
|
-
// Where the timezone can either be 'Z' (for UTC) or [+|-]xx:xx for the time offset
|
|
50
|
-
// sqlite understands this so by splitting the timezone from the actual date
|
|
51
|
-
// prefixing it with 1970 it allows sqlite to give back the number of seconds
|
|
52
|
-
// which can be divided by 60 back to minutes
|
|
53
|
-
totaloffsetminutes : (x) => `case
|
|
54
|
-
when substr(${x}, length(${x})) = 'z' then 0
|
|
55
|
-
else strftime('%s', '1970-01-01T00:00:00' || substr(${x}, length(${x}) - 5)) / 60
|
|
56
|
-
end`,
|
|
57
|
-
|
|
58
|
-
// odata spec defines the value format for totalseconds as a duration like: P12DT23H59M59.999999999999S
|
|
59
|
-
// P -> duration indicator
|
|
60
|
-
// D -> days, T -> Time seperator, H -> hours, M -> minutes, S -> fractional seconds
|
|
61
|
-
// By splitting the DT and calculating the seconds of the time separate from the day
|
|
62
|
-
// it possible to determine the full amount of seconds by adding them together as fractionals and multiplying
|
|
63
|
-
// the number of seconds in a day
|
|
64
|
-
// As sqlite is most accurate with juliandays it is better to do then then using actual second function
|
|
65
|
-
// while the odata specification states that the seconds has to be fractional which only julianday allows
|
|
66
|
-
totalseconds : (x) => `(
|
|
67
|
-
(
|
|
68
|
-
(
|
|
69
|
-
cast(substr(${x},2,instr(${x},'DT') - 2) as Integer)
|
|
70
|
-
) + (
|
|
71
|
-
julianday(
|
|
72
|
-
'-4713-11-25T' ||
|
|
73
|
-
replace(
|
|
74
|
-
replace(
|
|
75
|
-
replace(
|
|
76
|
-
substr(${x},instr(${x},'DT') + 2),
|
|
77
|
-
'H',':'
|
|
78
|
-
),'M',':'
|
|
79
|
-
),'S','Z'
|
|
80
|
-
)
|
|
81
|
-
) - 0.5
|
|
82
|
-
)
|
|
83
|
-
) * 86400
|
|
84
|
-
)`,
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const HANAFunctions = {
|
|
89
|
-
// https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/f12b86a6284c4aeeb449e57eb5dd3ebd.html
|
|
90
|
-
|
|
91
|
-
// Time functions
|
|
92
|
-
nano100_between : (x,y) => `(julianday(${y}) - julianday(${x})) * 864000000000`,
|
|
93
|
-
seconds_between : (x,y) => `(julianday(${y}) - julianday(${x})) * 86400`,
|
|
94
|
-
// Calculates the difference in full days using julian day
|
|
95
|
-
// Using the exact time of the day to determine whether 24 hours have passed or not to add the final day
|
|
96
|
-
// When just comparing the julianday values with each other there are leap seconds included
|
|
97
|
-
// Which on the day resolution are included as the individual days therefor ignoring them to match HANA
|
|
98
|
-
days_between : (x,y) => `(
|
|
99
|
-
cast ( julianday(${y}) as Integer ) - cast ( julianday(${x}) as Integer )
|
|
100
|
-
) + (
|
|
101
|
-
case
|
|
102
|
-
when ( julianday(${y}) < julianday(${x}) ) then
|
|
103
|
-
(cast( strftime('%H%M%S%f', ${y}) as Integer ) < cast( strftime('%H%M%S%f', ${x}) as Integer ))
|
|
104
|
-
else
|
|
105
|
-
(cast( strftime('%H%M%S%f', ${y}) as Integer ) > cast( strftime('%H%M%S%f', ${x}) as Integer )) * -1
|
|
106
|
-
end
|
|
107
|
-
)`,
|
|
108
|
-
|
|
109
|
-
// (y1 - y0) * 12 + (m1 - m0) + (t1 < t0) * -1
|
|
110
|
-
/* '%d%H%M%S%f' returns as a number like which results in an equal check to:
|
|
111
|
-
(
|
|
112
|
-
d1 < d0 ||
|
|
113
|
-
(d1 = d0 && h1 < h0) ||
|
|
114
|
-
(d1 = d0 && h1 = h0 && m1 < m0) ||
|
|
115
|
-
(d1 = d0 && h1 = h0 && m1 = m0 && s1 < s0) ||
|
|
116
|
-
(d1 = d0 && h1 = h0 && m1 = m0 && s1 = s0 && ms1 < ms0)
|
|
117
|
-
)
|
|
118
|
-
Which will remove the current month if the time of the month is below the time of the month of the start date
|
|
119
|
-
It should not matter that the number of days in the month is different as for a month to have passed
|
|
120
|
-
the time of the month would have to be higher then the time of the month of the start date
|
|
121
|
-
|
|
122
|
-
Also check whether the result will be positive or negative to make sure to not subtract an extra month
|
|
123
|
-
*/
|
|
124
|
-
months_between : (x,y) => `
|
|
125
|
-
(
|
|
126
|
-
(
|
|
127
|
-
( cast( strftime('%Y', ${y}) as Integer ) - cast( strftime('%Y', ${x}) as Integer ) ) * 12
|
|
128
|
-
) + (
|
|
129
|
-
cast( strftime('%m', ${y}) as Integer ) - cast( strftime('%m', ${x}) as Integer )
|
|
130
|
-
) + (
|
|
131
|
-
(
|
|
132
|
-
case
|
|
133
|
-
when ( cast( strftime('%Y%m', ${y}) as Integer ) < cast( strftime('%Y%m', ${x}) as Integer ) ) then
|
|
134
|
-
(cast( strftime('%d%H%M%S%f', ${y}) as Integer ) > cast( strftime('%d%H%M%S%f', ${x}) as Integer ))
|
|
135
|
-
else
|
|
136
|
-
(cast( strftime('%d%H%M%S%f', ${y}) as Integer ) < cast( strftime('%d%H%M%S%f', ${x}) as Integer )) * -1
|
|
137
|
-
end
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
)`,
|
|
141
|
-
years_between(x,y) { return `floor(${this.months_between(x,y)} / 12)` },
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
for (let each in HANAFunctions)
|
|
145
|
-
HANAFunctions[each.toUpperCase()] = HANAFunctions[each]
|
|
146
|
-
|
|
147
|
-
module.exports = { ...StandardFunctions, ...HANAFunctions }
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* For operators of <eqOps>, this is replaced by comparing all leaf elements with null, combined with and.
|
|
3
|
-
* If there are at least two leaf elements and if there are tokens before or after the recognized pattern, we enclose the resulting condition in parens (...)
|
|
4
|
-
*/
|
|
5
|
-
const eqOps = [['is'], ['='], /* ['=='] */ ]
|
|
6
|
-
/**
|
|
7
|
-
* For operators of <notEqOps>, do the same but use or instead of and.
|
|
8
|
-
* This ensures that not struc == <value> is the same as struc != <value>.
|
|
9
|
-
*/
|
|
10
|
-
const notEqOps = [['is', 'not'], ['<>'], ['!=']]
|
|
11
|
-
/**
|
|
12
|
-
* not supported in comparison w/ struct because of unclear semantics
|
|
13
|
-
*/
|
|
14
|
-
const notSupportedOps = [['>'], ['<'], ['>='], ['<=']]
|
|
15
|
-
|
|
16
|
-
module.exports = { eqOps, notEqOps, notSupportedOps }
|
package/lib/db/sql/utils.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const cds = require('@sap/cds/lib')
|
|
2
|
-
|
|
3
|
-
const target_name4 = q => {
|
|
4
|
-
const target = (
|
|
5
|
-
q.SELECT?.from ||
|
|
6
|
-
q.INSERT?.into ||
|
|
7
|
-
q.UPSERT?.into ||
|
|
8
|
-
q.UPDATE?.entity ||
|
|
9
|
-
q.DELETE?.from ||
|
|
10
|
-
q.CREATE?.entity ||
|
|
11
|
-
q.DROP?.entity ||
|
|
12
|
-
undefined
|
|
13
|
-
)
|
|
14
|
-
if (target?.SET?.op === "union") throw new cds.error('”UNION” based queries are not supported');
|
|
15
|
-
if (!target?.ref) return target
|
|
16
|
-
const [first] = target.ref
|
|
17
|
-
return first.id || first
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
module.exports = {
|
|
21
|
-
target_name4
|
|
22
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
const cds = require('@sap/cds')
|
|
2
|
-
const propagateForeignKeys = require('@sap/cds/libx/_runtime/common/utils/propagateForeignKeys')
|
|
3
|
-
const { enrichDataWithKeysFromWhere } = require('@sap/cds/libx/_runtime/common/utils/keys')
|
|
4
|
-
|
|
5
|
-
const generateUUIDandPropagateKeys = (target, data, event) => {
|
|
6
|
-
if (!data) return
|
|
7
|
-
const keys = target.keys
|
|
8
|
-
for (const key in keys) {
|
|
9
|
-
if (keys[key].type === 'cds.UUID' && !data[key] && event === 'CREATE') {
|
|
10
|
-
data[key] = cds.utils.uuid()
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
const elements = target.elements
|
|
14
|
-
for (const element in elements) {
|
|
15
|
-
// if assoc keys are structured, do not ignore them, as they need to be flattened in propagateForeignKeys
|
|
16
|
-
if (elements[element].key && !(elements[element]._isAssociationStrict && elements[element].is2one && element in data)) {
|
|
17
|
-
continue
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (elements[element].is2one || elements[element].is2many) {
|
|
21
|
-
let subData = data[element]
|
|
22
|
-
if (subData) {
|
|
23
|
-
if (!Array.isArray(subData)) {
|
|
24
|
-
subData = [subData]
|
|
25
|
-
}
|
|
26
|
-
for (const sub of subData) {
|
|
27
|
-
generateUUIDandPropagateKeys(elements[element]._target, sub, event)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
propagateForeignKeys(element, data, elements[element]._foreignKeys, elements[element].isComposition, {
|
|
32
|
-
deleteAssocs: true
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const input = async function (req, next) {
|
|
39
|
-
// REVISIT dummy handler until we have input processing
|
|
40
|
-
if (!req.target || !this.model || req.target._unresolved) return next()
|
|
41
|
-
|
|
42
|
-
if (req.event === "UPDATE") {
|
|
43
|
-
// REVISIT for deep update we need to inject the keys first
|
|
44
|
-
enrichDataWithKeysFromWhere(req.data, req, this)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// REVISIT no input processing for INPUT with rows/values
|
|
48
|
-
if (req.event !== 'DELETE' && !(req.query.INSERT?.rows || req.query.INSERT?.values)) {
|
|
49
|
-
if (Array.isArray(req.data)) {
|
|
50
|
-
for (const d of req.data) {
|
|
51
|
-
generateUUIDandPropagateKeys(req.target, d, req.event)
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
generateUUIDandPropagateKeys(req.target, req.data, req.event)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return next()
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
module.exports = {
|
|
62
|
-
input
|
|
63
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ABORT":1,
|
|
3
|
-
"ACTION":1,
|
|
4
|
-
"ADD":1,
|
|
5
|
-
"AFTER":1,
|
|
6
|
-
"ALL":1,
|
|
7
|
-
"ALTER":1,
|
|
8
|
-
"ALWAYS":1,
|
|
9
|
-
"ANALYZE":1,
|
|
10
|
-
"AND":1,
|
|
11
|
-
"AS":1,
|
|
12
|
-
"ASC":1,
|
|
13
|
-
"ATTACH":1,
|
|
14
|
-
"AUTOINCREMENT":1,
|
|
15
|
-
"BEFORE":1,
|
|
16
|
-
"BEGIN":1,
|
|
17
|
-
"BETWEEN":1,
|
|
18
|
-
"BY":1,
|
|
19
|
-
"CASCADE":1,
|
|
20
|
-
"CASE":1,
|
|
21
|
-
"CAST":1,
|
|
22
|
-
"CHECK":1,
|
|
23
|
-
"COLLATE":1,
|
|
24
|
-
"COLUMN":1,
|
|
25
|
-
"COMMIT":1,
|
|
26
|
-
"CONFLICT":1,
|
|
27
|
-
"CONSTRAINT":1,
|
|
28
|
-
"CREATE":1,
|
|
29
|
-
"CROSS":1,
|
|
30
|
-
"CURRENT":1,
|
|
31
|
-
"CURRENT_DATE":1,
|
|
32
|
-
"CURRENT_TIME":1,
|
|
33
|
-
"CURRENT_TIMESTAMP":1,
|
|
34
|
-
"DATABASE":1,
|
|
35
|
-
"DEFAULT":1,
|
|
36
|
-
"DEFERRABLE":1,
|
|
37
|
-
"DEFERRED":1,
|
|
38
|
-
"DELETE":1,
|
|
39
|
-
"DESC":1,
|
|
40
|
-
"DETACH":1,
|
|
41
|
-
"DISTINCT":1,
|
|
42
|
-
"DO":1,
|
|
43
|
-
"DROP":1,
|
|
44
|
-
"EACH":1,
|
|
45
|
-
"ELSE":1,
|
|
46
|
-
"END":1,
|
|
47
|
-
"ESCAPE":1,
|
|
48
|
-
"EXCEPT":1,
|
|
49
|
-
"EXCLUDE":1,
|
|
50
|
-
"EXCLUSIVE":1,
|
|
51
|
-
"EXISTS":1,
|
|
52
|
-
"EXPLAIN":1,
|
|
53
|
-
"FAIL":1,
|
|
54
|
-
"FILTER":1,
|
|
55
|
-
"FIRST":1,
|
|
56
|
-
"FOLLOWING":1,
|
|
57
|
-
"FOR":1,
|
|
58
|
-
"FOREIGN":1,
|
|
59
|
-
"FROM":1,
|
|
60
|
-
"FULL":1,
|
|
61
|
-
"GENERATED":1,
|
|
62
|
-
"GLOB":1,
|
|
63
|
-
"GROUP":1,
|
|
64
|
-
"GROUPS":1,
|
|
65
|
-
"HAVING":1,
|
|
66
|
-
"IF":1,
|
|
67
|
-
"IGNORE":1,
|
|
68
|
-
"IMMEDIATE":1,
|
|
69
|
-
"IN":1,
|
|
70
|
-
"INDEX":1,
|
|
71
|
-
"INDEXED":1,
|
|
72
|
-
"INITIALLY":1,
|
|
73
|
-
"INNER":1,
|
|
74
|
-
"INSERT":1,
|
|
75
|
-
"INSTEAD":1,
|
|
76
|
-
"INTERSECT":1,
|
|
77
|
-
"INTO":1,
|
|
78
|
-
"IS":1,
|
|
79
|
-
"ISNULL":1,
|
|
80
|
-
"JOIN":1,
|
|
81
|
-
"KEY":1,
|
|
82
|
-
"LAST":1,
|
|
83
|
-
"LEFT":1,
|
|
84
|
-
"LIKE":1,
|
|
85
|
-
"LIMIT":1,
|
|
86
|
-
"MATCH":1,
|
|
87
|
-
"MATERIALIZED":1,
|
|
88
|
-
"NATURAL":1,
|
|
89
|
-
"NO":1,
|
|
90
|
-
"NOT":1,
|
|
91
|
-
"NOTHING":1,
|
|
92
|
-
"NOTNULL":1,
|
|
93
|
-
"NULL":1,
|
|
94
|
-
"NULLS":1,
|
|
95
|
-
"OF":1,
|
|
96
|
-
"OFFSET":1,
|
|
97
|
-
"ON":1,
|
|
98
|
-
"OR":1,
|
|
99
|
-
"ORDER":1,
|
|
100
|
-
"OTHERS":1,
|
|
101
|
-
"OUTER":1,
|
|
102
|
-
"OVER":1,
|
|
103
|
-
"PARTITION":1,
|
|
104
|
-
"PLAN":1,
|
|
105
|
-
"PRAGMA":1,
|
|
106
|
-
"PRECEDING":1,
|
|
107
|
-
"PRIMARY":1,
|
|
108
|
-
"QUERY":1,
|
|
109
|
-
"RAISE":1,
|
|
110
|
-
"RANGE":1,
|
|
111
|
-
"RECURSIVE":1,
|
|
112
|
-
"REFERENCES":1,
|
|
113
|
-
"REGEXP":1,
|
|
114
|
-
"REINDEX":1,
|
|
115
|
-
"RELEASE":1,
|
|
116
|
-
"RENAME":1,
|
|
117
|
-
"REPLACE":1,
|
|
118
|
-
"RESTRICT":1,
|
|
119
|
-
"RETURNING":1,
|
|
120
|
-
"RIGHT":1,
|
|
121
|
-
"ROLLBACK":1,
|
|
122
|
-
"ROW":1,
|
|
123
|
-
"ROWS":1,
|
|
124
|
-
"SAVEPOINT":1,
|
|
125
|
-
"SELECT":1,
|
|
126
|
-
"SET":1,
|
|
127
|
-
"TABLE":1,
|
|
128
|
-
"TEMP":1,
|
|
129
|
-
"TEMPORARY":1,
|
|
130
|
-
"THEN":1,
|
|
131
|
-
"TIES":1,
|
|
132
|
-
"TO":1,
|
|
133
|
-
"TRANSACTION":1,
|
|
134
|
-
"TRIGGER":1,
|
|
135
|
-
"UNBOUNDED":1,
|
|
136
|
-
"UNION":1,
|
|
137
|
-
"UNIQUE":1,
|
|
138
|
-
"UPDATE":1,
|
|
139
|
-
"USING":1,
|
|
140
|
-
"VACUUM":1,
|
|
141
|
-
"VALUES":1,
|
|
142
|
-
"VIEW":1,
|
|
143
|
-
"VIRTUAL":1,
|
|
144
|
-
"WHEN":1,
|
|
145
|
-
"WHERE":1,
|
|
146
|
-
"WINDOW":1,
|
|
147
|
-
"WITH":1,
|
|
148
|
-
"WITHOUT":1
|
|
149
|
-
}
|