@sap/cds 1.15.0 → 1.17.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 +19 -0
- package/{developer-license-3.1.txt → LICENSE} +37 -35
- package/_hdbext/README.md +373 -0
- package/_hdbext/index.js +4 -0
- package/_hdbext/lib/client-factory.js +62 -0
- package/_hdbext/lib/client-session.js +96 -0
- package/_hdbext/lib/conn-options.js +84 -0
- package/_hdbext/lib/constants.js +79 -0
- package/_hdbext/lib/internal-constants.js +7 -0
- package/_hdbext/lib/middleware.js +46 -0
- package/_hdbext/lib/pool.js +236 -0
- package/_hdbext/lib/safe-sql.js +17 -0
- package/_hdbext/lib/sql-injection-utils.js +149 -0
- package/cds-queries-geo.js +347 -371
- package/cds-queries.js +2692 -2229
- package/cds.js +111 -104
- package/exprs.js +118 -107
- package/manager.js +696 -614
- package/metadata.js +604 -542
- package/npm-shrinkwrap.json +175 -0
- package/package.json +40 -1
- package/transaction.js +45 -51
- package/util/Queue.js +32 -30
- package/utils.js +182 -159
- package/xsjs-cds.js +231 -221
- package/.project +0 -11
- package/TUTORIAL.md +0 -1236
- package/dependencies +0 -56
- package/sapcds.manifest +0 -1747
package/cds.js
CHANGED
|
@@ -1,76 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var metadata = require('./metadata');
|
|
4
|
-
var manager = require('./manager');
|
|
5
|
-
var queries = require("./cds-queries");
|
|
6
|
-
var SqlQuery = queries.Query;
|
|
7
|
-
var transaction = require('./transaction');
|
|
1
|
+
const async = require('async');
|
|
8
2
|
|
|
3
|
+
const metadata = require('./metadata');
|
|
4
|
+
const manager = require('./manager');
|
|
5
|
+
const queries = require('./cds-queries');
|
|
6
|
+
const SqlQuery = queries.Query;
|
|
7
|
+
const transaction = require('./transaction');
|
|
8
|
+
const _hdbext = require('./_hdbext');
|
|
9
9
|
|
|
10
10
|
// transactions
|
|
11
11
|
|
|
12
12
|
// returns connected DB client
|
|
13
|
-
exports.$getTransaction = function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
exports.$getTransaction = function(dbconn, callback) {
|
|
14
|
+
if (typeof callback === 'undefined') {
|
|
15
|
+
callback = dbconn;
|
|
16
|
+
dbconn = null;
|
|
17
|
+
}
|
|
18
|
+
transaction.getClient(dbconn, function(err, client) {
|
|
19
|
+
if (err) {
|
|
20
|
+
return callback(err);
|
|
17
21
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
22
|
+
client.$get = function(entity, key, callback) {
|
|
23
|
+
manager._get(client, entity, key, callback);
|
|
24
|
+
};
|
|
25
|
+
client.$find = function(entity, condition, callback) {
|
|
26
|
+
manager._find(client, entity, condition, callback);
|
|
27
|
+
};
|
|
28
|
+
client.$save = function(instance, callback) {
|
|
29
|
+
manager._save(client, instance, callback);
|
|
30
|
+
};
|
|
31
|
+
client.$discard = function(instance, callback) {
|
|
32
|
+
manager._discard(client, instance, callback);
|
|
33
|
+
};
|
|
34
|
+
client.$getAll = function(refs, callback) {
|
|
35
|
+
const getter = function(ref, cb) {
|
|
36
|
+
manager._get(client, ref.$entity, ref, cb);
|
|
37
|
+
};
|
|
38
|
+
async.map(refs, getter, callback);
|
|
39
|
+
};
|
|
40
|
+
client.$findAll = function(refs, callback) {
|
|
41
|
+
const finder = function(ref, cb) {
|
|
42
|
+
manager._find(client, ref.$entity, ref, cb);
|
|
43
|
+
};
|
|
44
|
+
async.map(refs, finder, callback);
|
|
45
|
+
};
|
|
46
|
+
client.$saveAll = function(refs, callback) {
|
|
47
|
+
const saver = function(ref, cb) {
|
|
48
|
+
manager._save(client, ref, cb);
|
|
49
|
+
};
|
|
50
|
+
async.map(refs, saver, callback);
|
|
51
|
+
};
|
|
52
|
+
client.$discardAll = function(refs, callback) {
|
|
53
|
+
const discarder = function(ref, cb) {
|
|
54
|
+
manager._discard(client, ref, cb);
|
|
55
|
+
};
|
|
56
|
+
async.map(refs, discarder, function(err) {
|
|
57
|
+
callback(err);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
client.$delete = function(entity, condition, callback) {
|
|
61
|
+
manager._delete(client, entity, condition, callback);
|
|
62
|
+
};
|
|
63
|
+
client.$setAutoCommit = function(auto) {
|
|
64
|
+
transaction._autoCommit(client, auto);
|
|
65
|
+
};
|
|
66
|
+
client.$commit = function(callback) {
|
|
67
|
+
transaction._commit(client, callback);
|
|
68
|
+
};
|
|
69
|
+
client.$rollback = function(callback) {
|
|
70
|
+
transaction._rollback(client, callback);
|
|
71
|
+
};
|
|
72
|
+
client.$close = function() {
|
|
73
|
+
transaction.releaseClient(client, dbconn);
|
|
74
|
+
};
|
|
75
|
+
callback(err, client);
|
|
76
|
+
});
|
|
74
77
|
};
|
|
75
78
|
|
|
76
79
|
|
|
@@ -78,55 +81,57 @@ exports.$getTransaction = function (dbconn, callback) {
|
|
|
78
81
|
|
|
79
82
|
// batch-import (main API function)
|
|
80
83
|
exports.$importEntities = function(refs, opts, callback) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
if (typeof callback === 'undefined') {
|
|
85
|
+
callback = opts;
|
|
86
|
+
opts = {};
|
|
87
|
+
}
|
|
88
|
+
metadata._import(refs, opts, function(error, entities) {
|
|
89
|
+
for (const name in entities) {
|
|
90
|
+
if (entities[name]) {
|
|
91
|
+
addXSInterface(entities[name]);
|
|
92
|
+
}
|
|
84
93
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
addXSInterface(entities[name]);
|
|
89
|
-
callback(error, entities);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
94
|
+
callback(error, entities);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
92
97
|
|
|
93
98
|
exports.$importEntity = function(ref, callback) {
|
|
94
|
-
|
|
99
|
+
exports.$importEntities([ref], callback);
|
|
95
100
|
};
|
|
96
101
|
|
|
97
102
|
// (async) retrieve entity object by name
|
|
98
103
|
// NOTE: callback will never be invoked if entity is not imported!
|
|
99
104
|
exports.$getEntity = function(entityName, callback) {
|
|
100
|
-
|
|
105
|
+
metadata.getEntity(entityName, callback);
|
|
101
106
|
};
|
|
102
107
|
exports.$getEntities = function(entityNames, callback) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
const getter = function(name, cb) {
|
|
109
|
+
exports.$getEntity(name, cb);
|
|
110
|
+
};
|
|
111
|
+
async.map(entityNames, getter, callback);
|
|
107
112
|
};
|
|
108
113
|
|
|
109
114
|
// retrieve entity object by name
|
|
110
115
|
// NOTE: returns null if entity import has not completed yet
|
|
111
116
|
exports.$getEntitySync = function(entityName) {
|
|
112
|
-
|
|
117
|
+
return metadata.getEntitySync(entityName);
|
|
113
118
|
};
|
|
114
119
|
|
|
115
120
|
|
|
116
121
|
// instance management
|
|
117
122
|
|
|
118
123
|
function addXSInterface(entity) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
// batch operations
|
|
125
|
+
entity.$prepare = function(value) {
|
|
126
|
+
value.$entity = entity;
|
|
127
|
+
return value;
|
|
128
|
+
};
|
|
129
|
+
// general queries
|
|
130
|
+
entity.$query = function(client) {
|
|
131
|
+
const param = {};
|
|
132
|
+
param['t0'] = {entity: entity};
|
|
133
|
+
return new SqlQuery(client, param);
|
|
134
|
+
};
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
|
|
@@ -141,7 +146,7 @@ exports._clearImports = metadata._clearImports;
|
|
|
141
146
|
exports.Query = SqlQuery;
|
|
142
147
|
exports.createQuery = queries.createQuery;
|
|
143
148
|
exports.$par = function(id) {
|
|
144
|
-
|
|
149
|
+
return new queries.Par(id);
|
|
145
150
|
};
|
|
146
151
|
|
|
147
152
|
|
|
@@ -154,12 +159,14 @@ exports.save = exports.$save;
|
|
|
154
159
|
exports.queries = queries;
|
|
155
160
|
exports.extensionPoints = manager.extensionPoints; // hook for extenders
|
|
156
161
|
exports.xsjs = function(conn, callback) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
exports.$getTransaction(conn._client, function(err, tx) {
|
|
163
|
+
if (err) {
|
|
164
|
+
callback(err);
|
|
165
|
+
} else {
|
|
166
|
+
const xsjscds = require('./xsjs-cds').init(tx);
|
|
167
|
+
callback(null, xsjscds);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
165
170
|
}.sync;
|
|
171
|
+
// Use at your own risk, middleware comes from @sap/hdbext@^4
|
|
172
|
+
exports.middleware = _hdbext.middleware;
|
package/exprs.js
CHANGED
|
@@ -1,142 +1,153 @@
|
|
|
1
|
-
|
|
1
|
+
const utils = require('./utils');
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
// NOTE: function raises exception in case of invalid expression
|
|
5
5
|
exports.buildInstanceFilter = function(entity, condition) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
return function(instance) {
|
|
7
|
+
return evalExpression(entity, condition, instance);
|
|
8
|
+
};
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
// check if given instance matches property criteria
|
|
13
13
|
function evalExpression(entity, criteria, instance) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
14
|
+
const realcrit = criteria.$eq || criteria.$ne || criteria;
|
|
15
|
+
let match = true;
|
|
16
|
+
utils.forInstance(realcrit, entity.$_mapping, {
|
|
17
|
+
$column: function(p, f, v, m) {
|
|
18
|
+
if (v === null || typeof v[f] === 'undefined') {
|
|
19
|
+
return;
|
|
20
|
+
} // property not part of criteria
|
|
21
|
+
const value = utils.getPropPath(instance, p + f);
|
|
22
|
+
if (!evalCondition(v[f], value)) {
|
|
23
|
+
match = false;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
$association: function(p, f, v, m) {
|
|
27
|
+
if (v === null || typeof v[f] === 'undefined') {
|
|
28
|
+
return;
|
|
29
|
+
} // property not part of criteria
|
|
30
|
+
const value = utils.getPropPath(instance, p + f);
|
|
31
|
+
// empty association
|
|
32
|
+
if (v[f] === null) { // short for { $none: true }
|
|
33
|
+
match = match && !value;
|
|
34
|
+
return;
|
|
35
|
+
} else if ('$none' in v[f]) {
|
|
36
|
+
match = match && (v[f].$none && isnone(value) || !v[f].$none && !isnone(value));
|
|
37
|
+
return;
|
|
38
|
+
} else if ('$empty' in v[f]) { // deprecated for "$none"
|
|
39
|
+
match = match && (v[f].$empty && isnone(value) || !v[f].$empty && !isnone(value));
|
|
40
|
+
return;
|
|
41
|
+
} else if ('$null' in v[f]) {
|
|
42
|
+
throw new Error('invalid $null operator on association');
|
|
43
|
+
} else if (!value) {
|
|
44
|
+
match = match && (v[f].$none || v[f].$empty);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// non-empty association
|
|
48
|
+
if (utils.isArray(value)) {
|
|
49
|
+
throw new Error('invalid navigation in expression');
|
|
50
|
+
}
|
|
51
|
+
if (!('$_entity' in value)) {
|
|
52
|
+
throw new Error('*** ASSERT FAIL *** comparing against non-instance');
|
|
53
|
+
}
|
|
54
|
+
if ('$_entity' in v[f]) {
|
|
55
|
+
// shortcut for comparing two instances
|
|
56
|
+
if (v[f] === value) {
|
|
57
|
+
return;
|
|
58
|
+
} // found match --> keep match unchanged
|
|
59
|
+
} else {
|
|
60
|
+
// follow association recursively
|
|
61
|
+
if (evalExpression(m[f].$association.$class, v[f], value)) {
|
|
62
|
+
return;
|
|
63
|
+
} // found match --> keep match unchanged
|
|
64
|
+
}
|
|
65
|
+
match = false;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
return '$ne' in criteria ? !match : match;
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
|
|
65
72
|
// evaluate condition for property value
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
const evalCondition = function(condition, value) {
|
|
74
|
+
// condition === expr
|
|
75
|
+
// NOTE: direct comparison between objects is always a bad idea
|
|
76
|
+
if (typeof condition !== 'object') {
|
|
77
|
+
return defaultCompare(value, condition) === 0;
|
|
78
|
+
}
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
// use user-supplied comparison function?
|
|
81
|
+
const compare = '$using' in condition ? condition.$using : defaultCompare;
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
83
|
+
// condition === { op: expr, op: expr, ... }
|
|
84
|
+
for (const op in condition) {
|
|
85
|
+
const expr = condition[op];
|
|
86
|
+
switch (op) {
|
|
87
|
+
// NOTE: using !(...) to handle "undefined" values
|
|
88
|
+
case '$using':
|
|
89
|
+
break; // skip comparison function
|
|
90
|
+
case '$eq':
|
|
91
|
+
if (!(compare(value, expr) === 0)) return false;
|
|
92
|
+
break;
|
|
93
|
+
case '$ne':
|
|
94
|
+
const res = compare(value, expr);
|
|
95
|
+
if (!(res < 0 || res > 0)) return false;
|
|
96
|
+
break;
|
|
97
|
+
case '$lt':
|
|
98
|
+
if (!(compare(value, expr) < 0)) return false;
|
|
99
|
+
break;
|
|
100
|
+
case '$le':
|
|
101
|
+
if (!(compare(value, expr) <= 0)) return false;
|
|
102
|
+
break;
|
|
103
|
+
case '$gt':
|
|
104
|
+
if (!(compare(value, expr) > 0)) return false;
|
|
105
|
+
break;
|
|
106
|
+
case '$ge':
|
|
107
|
+
if (!(compare(value, expr) >= 0)) return false;
|
|
108
|
+
break;
|
|
109
|
+
case '$like':
|
|
110
|
+
if (!islike(expr, value)) return false;
|
|
111
|
+
break;
|
|
112
|
+
case '$unlike':
|
|
113
|
+
if (islike(expr, value) || typeof value === 'undefined') return false;
|
|
114
|
+
break;
|
|
115
|
+
case '$null':
|
|
116
|
+
if (!(isnull(value) && expr || !isnull(value) && !expr)) return false;
|
|
117
|
+
break;
|
|
118
|
+
case '$empty':
|
|
119
|
+
throw new Error('invalid $empty operator on non-association');
|
|
120
|
+
case '$none':
|
|
121
|
+
throw new Error('invalid $none operator on non-association');
|
|
122
|
+
default:
|
|
123
|
+
throw new Error(`invalid expression operator: ${ op}`);
|
|
117
124
|
}
|
|
118
|
-
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
119
127
|
};
|
|
120
128
|
|
|
121
129
|
|
|
122
130
|
// compare two primitive values semantically
|
|
123
131
|
function defaultCompare(lhs, rhs) {
|
|
124
|
-
|
|
132
|
+
return lhs < rhs ? -1 : lhs > rhs ? +1 : lhs == rhs ? 0 : undefined;
|
|
125
133
|
};
|
|
126
134
|
|
|
127
135
|
|
|
128
136
|
// convert SQL LIKE pattern to regular expression
|
|
129
137
|
function islike(pattern, value) {
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
const escaped = pattern
|
|
139
|
+
.replace(/[-\\.^$*+?()|[\]{}]/g, '\\$&')
|
|
140
|
+
.replace(/%/g, '.*')
|
|
141
|
+
.replace(/_/g, '.');
|
|
142
|
+
return (new RegExp(escaped)).test(value);
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
// check for NULL value; includes JavaScript null for associations
|
|
135
146
|
function isnull(value) {
|
|
136
|
-
|
|
147
|
+
return typeof value === 'undefined' || value === null;
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
// check if target instance(s) exist
|
|
140
151
|
function isnone(value) {
|
|
141
|
-
|
|
152
|
+
return isnull(value) || utils.isArray(value) && value.length === 0;
|
|
142
153
|
}
|