@zerodep-dev/easy-query-ts-hana 0.0.0-stage → 0.2.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,OAAO,EACP,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,OAAO,EAEZ,KAAK,WAAW,EAEhB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,IAAI,EAGV,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAKtC,qBAAa,WAAY,SAAQ,OAAO;IACtC,QAAQ,CAAC,IAAI,UAAU;IACvB,iDAAiD;IACjD,SAAkB,oBAAoB,SAAS;IAEtC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAc1D;IAED,gEAAgE;IACvD,gBAAgB,IAAI,MAAM,CAElC;IAED,kDAAkD;IAClD,SAAkB,gBAAgB,SAAS;IAC3C,SAAkB,YAAY,SAAS;IAE9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErD;IAEQ,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErE;IAED,mBAAmB;IACV,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAEtF;IAED,iCAAiC;IAClB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA6B9D;IAED,mCAAmC;IAC1B,SAAS,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAInD;IAED,sDAAsD;IAC7C,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAE9C;IAED,UAAmB,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAQ/D;IAED,2CAA2C;IAC3C,UAAmB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,CAE/E;IAED,qCAAqC;IACrC,UAAmB,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE;QAAE,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAKxF;IAED;;OAEG;IACM,MAAM,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,GAAE,SAA2B,GAAG,MAAM,CAoBrE;IAED;;;OAGG;IACM,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAG3D;IAEQ,WAAW,CAClB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,UAAQ,GACd,MAAM,CAIR;CACF;AAkED,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,WAAY,SAAQ,gBAAgB;IACnD,qBAAqB;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA0FD,uBAAuB;AACvB,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEhD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAExE"}
package/dist/index.js ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * SAP HANA 2.0,驱动用 SAP 维护的纯 JS 驱动 hdb(不需要原生编译)。
3
+ * ```ts
4
+ * const db = createDb({ schema, adapter: hana({ host, port: 39041, user, password, schema: 'APP', max: 5 }) })
5
+ * ```
6
+ * 适配器自带一个小连接池(hdb 一个 client 就是一条连接)。自增主键在同一条连接上用 CURRENT_IDENTITY_VALUE() 取回。
7
+ */
8
+ import hdb from 'hdb';
9
+ import { hanaFunctions } from './functions.js';
10
+ import { assembleTables, bareName, Dialect, SqlWriter, } from '@zerodep-dev/easy-query-ts';
11
+ export { Hana } from './functions.js';
12
+ /** HANA 的 LIMIT 不能省、OFFSET 前必须有 LIMIT */
13
+ const NO_LIMIT = 2147483647;
14
+ export class HanaDialect extends Dialect {
15
+ name = 'hana';
16
+ /** HANA 没有 WITH RECURSIVE(层级查询用 HIERARCHY 函数) */
17
+ supportsRecursiveCte = false;
18
+ columnType(kind, o) {
19
+ if (o.dbType)
20
+ return o.dbType;
21
+ switch (kind) {
22
+ case 'string':
23
+ return `NVARCHAR(${o.length ?? 255})`;
24
+ case 'double':
25
+ return 'DOUBLE';
26
+ case 'datetime':
27
+ return 'TIMESTAMP';
28
+ case 'json':
29
+ return 'NCLOB';
30
+ default:
31
+ return super.columnType(kind, o);
32
+ }
33
+ }
34
+ /** HANA 建列存表用 CREATE COLUMN TABLE,也没有 IF NOT EXISTS(表已存在会报错) */
35
+ createTableStart() {
36
+ return 'CREATE COLUMN TABLE';
37
+ }
38
+ /** CREATE INDEX、DROP TABLE 都没有 IF [NOT] EXISTS */
39
+ indexIfNotExists = false;
40
+ dropIfExists = false;
41
+ renameTable(from, to) {
42
+ return `RENAME TABLE ${this.table(from)} TO ${this.quote(bareName(to))}`;
43
+ }
44
+ renameColumn(table, from, to) {
45
+ return `RENAME COLUMN ${this.table(table)}.${this.quote(from)} TO ${this.quote(to)}`;
46
+ }
47
+ /** 加列的列定义要放在括号里 */
48
+ addColumn(table, name, kind, o) {
49
+ return `ALTER TABLE ${this.table(table)} ADD (${this.columnDefinition(name, kind, o)})`;
50
+ }
51
+ /** 读 SYS 系统视图(CURRENT_SCHEMA) */
52
+ async introspect(run) {
53
+ const schema = 'SCHEMA_NAME = CURRENT_SCHEMA';
54
+ return assembleTables({
55
+ tables: await run(`SELECT TABLE_NAME AS "table_name", COMMENTS AS "comment" FROM SYS.TABLES WHERE ${schema} ORDER BY TABLE_NAME`),
56
+ columns: await run(`SELECT c.TABLE_NAME AS "table_name", c.COLUMN_NAME AS "column_name",
57
+ CASE WHEN c.DATA_TYPE_NAME IN ('VARCHAR', 'NVARCHAR', 'VARBINARY') THEN c.DATA_TYPE_NAME || '(' || c.LENGTH || ')'
58
+ WHEN c.DATA_TYPE_NAME = 'DECIMAL' AND c.SCALE IS NOT NULL THEN 'DECIMAL(' || c.LENGTH || ', ' || c.SCALE || ')'
59
+ ELSE c.DATA_TYPE_NAME END AS "column_type",
60
+ c.IS_NULLABLE AS "is_nullable", c.DEFAULT_VALUE AS "column_default", c.COMMENTS AS "comment",
61
+ CASE WHEN c.GENERATION_TYPE IS NULL THEN 0 ELSE 1 END AS "is_generated",
62
+ CASE WHEN EXISTS (SELECT 1 FROM SYS.CONSTRAINTS k WHERE k.SCHEMA_NAME = c.SCHEMA_NAME AND k.TABLE_NAME = c.TABLE_NAME
63
+ AND k.COLUMN_NAME = c.COLUMN_NAME AND k.IS_PRIMARY_KEY = 'TRUE') THEN 1 ELSE 0 END AS "primary_key"
64
+ FROM SYS.TABLE_COLUMNS c WHERE c.${schema} ORDER BY c.TABLE_NAME, c.POSITION`),
65
+ indexes: await run(`SELECT i.TABLE_NAME AS "table_name", i.INDEX_NAME AS "index_name",
66
+ CASE WHEN i.CONSTRAINT IN ('UNIQUE', 'NOT NULL UNIQUE') THEN 1 ELSE 0 END AS "is_unique", i.COLUMN_NAME AS "column_name"
67
+ FROM SYS.INDEX_COLUMNS i WHERE i.${schema} AND (i.CONSTRAINT IS NULL OR i.CONSTRAINT <> 'PRIMARY KEY')
68
+ ORDER BY i.TABLE_NAME, i.INDEX_NAME, i.POSITION`),
69
+ foreignKeys: await run(`SELECT TABLE_NAME AS "table_name", CONSTRAINT_NAME AS "constraint_name", COLUMN_NAME AS "column_name",
70
+ REFERENCED_TABLE_NAME AS "referenced_table", REFERENCED_COLUMN_NAME AS "referenced_column"
71
+ FROM SYS.REFERENTIAL_CONSTRAINTS WHERE ${schema} ORDER BY TABLE_NAME, CONSTRAINT_NAME, POSITION`),
72
+ });
73
+ }
74
+ /** HANA 跳过被锁行的写法是 IGNORE LOCKED */
75
+ forUpdate(wait) {
76
+ return (' FOR UPDATE' + (wait === 'nowait' ? ' NOWAIT' : wait === 'skip' ? ' IGNORE LOCKED' : ''));
77
+ }
78
+ /** HANA 没有 RELEASE SAVEPOINT:回滚到保存点即可,其余在事务结束时一起清掉 */
79
+ releaseSavepoint() {
80
+ return undefined;
81
+ }
82
+ functions() {
83
+ return {
84
+ ...super.functions(),
85
+ length: (a) => `LENGTH(${a[0]})`,
86
+ // HANA 的 CONCAT 只收两个参数
87
+ concat: (a) => `(${a.join(' || ')})`,
88
+ ...hanaFunctions,
89
+ };
90
+ }
91
+ /** HANA 的格式化函数叫 TO_VARCHAR,格式符同 TO_CHAR */
92
+ formatDate(value, parts) {
93
+ return super.formatDate(value, parts).replace(/^TO_CHAR\(/, 'TO_VARCHAR(');
94
+ }
95
+ /** HANA 的 STRING_AGG 不支持 DISTINCT */
96
+ stringAgg(n, w) {
97
+ if (n.distinct)
98
+ throw new Error('joining({ distinct: true }) is not supported by the hana dialect');
99
+ // STRING_AGG 只收文字,数字等先转
100
+ return super.stringAgg({ ...n, e: { k: 'raw', parts: ['TO_VARCHAR(', ')'], args: [n.e] } }, w);
101
+ }
102
+ /**
103
+ * upsert 写成 MERGE:USING 里每行一个 SELECT … FROM DUMMY(多行 UNION ALL),按 target 列匹配;ignore 时没有 WHEN MATCHED。
104
+ */
105
+ insert(q, w = new SqlWriter()) {
106
+ if (!q.conflict && q.rows.length === 1)
107
+ return super.insert(q, w);
108
+ const cols = q.columns.map((c) => this.quote(c));
109
+ // 渲染时参数按出现顺序收集,所以 source 只渲染一次
110
+ const source = q.rows
111
+ .map((r) => `SELECT ${r.map((n, i) => `${this.node(n, w)} AS ${cols[i]}`).join(', ')} FROM DUMMY`)
112
+ .join(' UNION ALL ');
113
+ // HANA 的 VALUES 只能一行:多行写成 INSERT … SELECT … FROM DUMMY UNION ALL …
114
+ if (!q.conflict)
115
+ return `INSERT INTO ${this.table(q.table)} (${cols.join(', ')}) ${source}`;
116
+ const on = q.conflict.target
117
+ .map((t) => `eq_t.${this.quote(t)} = eq_s.${this.quote(t)}`)
118
+ .join(' AND ');
119
+ let sql = `MERGE INTO ${this.table(q.table)} eq_t USING (${source}) eq_s ON (${on})`;
120
+ if (q.conflict.update.length > 0) {
121
+ sql += ` WHEN MATCHED THEN UPDATE SET ${q.conflict.update.map((u) => `eq_t.${this.quote(u)} = eq_s.${this.quote(u)}`).join(', ')}`;
122
+ }
123
+ return `${sql} WHEN NOT MATCHED THEN INSERT (${cols.join(', ')}) VALUES (${cols.map((c) => `eq_s.${c}`).join(', ')})`;
124
+ }
125
+ /**
126
+ * hdb 按服务器推断的参数类型校验 JS 类型;{@code SELECT ? FROM DUMMY}(多行插入、MERGE 的来源)里的参数被推成 NVARCHAR,只收字符串。
127
+ * 数值类型的参数也接受字符串,所以数一律转成字符串传。
128
+ */
129
+ encodeParam(value, kind) {
130
+ const v = super.encodeParam(value, kind);
131
+ return typeof v === 'number' || typeof v === 'bigint' ? String(v) : v;
132
+ }
133
+ limitOffset(limit, offset, ordered = false) {
134
+ if (limit === undefined && offset !== undefined && offset > 0)
135
+ return ` LIMIT ${NO_LIMIT} OFFSET ${offset}`;
136
+ return super.limitOffset(limit, offset, ordered);
137
+ }
138
+ }
139
+ function call(fn) {
140
+ return new Promise((resolve, reject) => fn((error, result) => (error ? reject(error) : resolve(result))));
141
+ }
142
+ async function run(client, sql, params) {
143
+ if (params.length === 0)
144
+ return call((cb) => client.exec(sql, cb));
145
+ const statement = await call((cb) => client.prepare(sql, cb));
146
+ try {
147
+ return await call((cb) => statement.exec([...params], cb));
148
+ }
149
+ finally {
150
+ statement.drop();
151
+ }
152
+ }
153
+ class HanaSession {
154
+ connection;
155
+ constructor(connection) {
156
+ this.connection = connection;
157
+ }
158
+ async query(sql, params) {
159
+ const { client, release } = await this.connection();
160
+ try {
161
+ return (await run(client, sql, params));
162
+ }
163
+ finally {
164
+ release();
165
+ }
166
+ }
167
+ async execute(sql, params) {
168
+ const { client, release } = await this.connection();
169
+ try {
170
+ const affected = Number(await run(client, sql, params));
171
+ if (!/^\s*INSERT\b/i.test(sql))
172
+ return { affected, insertId: undefined };
173
+ // 同一条连接上取刚生成的 IDENTITY;表没有 IDENTITY 列时为 NULL
174
+ const rows = (await run(client, 'SELECT CURRENT_IDENTITY_VALUE() AS "id" FROM DUMMY', []));
175
+ const id = rows[0]?.id;
176
+ return { affected, insertId: id === null || id === undefined ? undefined : toKey(id) };
177
+ }
178
+ finally {
179
+ release();
180
+ }
181
+ }
182
+ }
183
+ function toKey(value) {
184
+ if (typeof value === 'number' || typeof value === 'bigint')
185
+ return value;
186
+ const n = Number(value);
187
+ return Number.isSafeInteger(n) ? n : BigInt(String(value));
188
+ }
189
+ const ISOLATION = {
190
+ 'read uncommitted': 'READ COMMITTED',
191
+ 'read committed': 'READ COMMITTED',
192
+ 'repeatable read': 'REPEATABLE READ',
193
+ serializable: 'SERIALIZABLE',
194
+ };
195
+ /** 最小连接池:空闲的复用,满了排队 */
196
+ class Pool {
197
+ options;
198
+ idle = [];
199
+ waiting = [];
200
+ size = 0;
201
+ constructor(options) {
202
+ this.options = options;
203
+ }
204
+ async acquire() {
205
+ const idle = this.idle.pop();
206
+ if (idle)
207
+ return idle;
208
+ if (this.size < (this.options.max ?? 5)) {
209
+ this.size++;
210
+ try {
211
+ return await this.open();
212
+ }
213
+ catch (error) {
214
+ this.size--;
215
+ throw error;
216
+ }
217
+ }
218
+ return new Promise((resolve) => this.waiting.push(resolve));
219
+ }
220
+ release(client) {
221
+ const next = this.waiting.shift();
222
+ if (next)
223
+ next(client);
224
+ else
225
+ this.idle.push(client);
226
+ }
227
+ end() {
228
+ for (const client of this.idle.splice(0))
229
+ client.end();
230
+ }
231
+ async open() {
232
+ const { schema, max: _max, ...connect } = this.options;
233
+ const client = hdb.createClient(connect);
234
+ client.on('error', () => undefined);
235
+ await call((cb) => client.connect((error) => cb(error, undefined)));
236
+ if (schema)
237
+ await run(client, `SET SCHEMA "${schema.replace(/"/g, '""')}"`, []);
238
+ return client;
239
+ }
240
+ }
241
+ class HanaDriver extends HanaSession {
242
+ pool;
243
+ constructor(pool) {
244
+ super(async () => {
245
+ const client = await pool.acquire();
246
+ return { client, release: () => pool.release(client) };
247
+ });
248
+ this.pool = pool;
249
+ }
250
+ async transaction(work, options) {
251
+ const client = await this.pool.acquire();
252
+ client.setAutoCommit(false);
253
+ try {
254
+ if (options?.isolation)
255
+ await run(client, `SET TRANSACTION ISOLATION LEVEL ${ISOLATION[options.isolation]}`, []);
256
+ if (options?.readOnly)
257
+ await run(client, 'SET TRANSACTION READ ONLY', []);
258
+ try {
259
+ const result = await work(new HanaSession(async () => ({ client, release: () => undefined })));
260
+ await call((cb) => client.commit((error) => cb(error, undefined)));
261
+ return result;
262
+ }
263
+ catch (error) {
264
+ await call((cb) => client.rollback((error) => cb(error, undefined)));
265
+ throw error;
266
+ }
267
+ }
268
+ finally {
269
+ client.setAutoCommit(true);
270
+ this.pool.release(client);
271
+ }
272
+ }
273
+ end() {
274
+ this.pool.end();
275
+ }
276
+ }
277
+ /** 关闭空闲连接(测试与进程退出时) */
278
+ export function closeHana(adapter) {
279
+ if (adapter.driver instanceof HanaDriver)
280
+ adapter.driver.end();
281
+ }
282
+ export function hana(options) {
283
+ return { dialect: new HanaDialect(), driver: new HanaDriver(new Pool(options)) };
284
+ }
285
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "@zerodep-dev/easy-query-ts-hana",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "SAP HANA adapter (hdb) for @zerodep-dev/easy-query-ts",
5
+ "license": "Apache-2.0",
6
+ "author": "lionheart (https://github.com/kenconnet666)",
7
+ "homepage": "https://github.com/kenconnet666/easy-query-ts#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/kenconnet666/easy-query-ts/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/kenconnet666/easy-query-ts.git",
14
+ "directory": "packages/hana"
15
+ },
16
+ "keywords": [
17
+ "sap-hana",
18
+ "hana",
19
+ "orm",
20
+ "easy-query"
21
+ ],
22
+ "exports": {
23
+ ".": {
24
+ "@easy-query-ts/source": "./src/index.ts",
25
+ "types": "./dist/index.d.ts",
26
+ "default": "./dist/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "src"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "peerDependencies": {
37
+ "@zerodep-dev/easy-query-ts": "^0.2.0",
38
+ "hdb": "^2.30.0"
39
+ },
40
+ "devDependencies": {
41
+ "@zerodep-dev/easy-query-ts": "0.2.0",
42
+ "@zerodep-dev/easy-query-ts-test": "0.2.0",
43
+ "hdb": "2.30.1"
44
+ },
45
+ "type": "module",
46
+ "sideEffects": false,
47
+ "scripts": {
48
+ "build": "tsc -b tsconfig.build.json"
49
+ }
6
50
  }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * HANA 专属函数,与 Java 版同名同形:
3
+ * ```ts
4
+ * db.queryable(Doc).where(d => Hana.text(d.title, d.body).fuzzy('easy qurey', 0.8)).orderBy(d => Hana.text(d.title).score().desc())
5
+ * ```
6
+ * 列上要有 FULLTEXT INDEX,否则 CONTAINS 按整列值匹配。生成带 {@code hana.} 前缀的函数节点,别的方言 toSQL 时报不支持。
7
+ */
8
+ import {
9
+ codecs,
10
+ dialectFn,
11
+ standardJsonPath,
12
+ unquoteSql,
13
+ type Expr,
14
+ type FunctionRenderer,
15
+ type Pred,
16
+ } from '@zerodep-dev/easy-query-ts';
17
+
18
+ export const Hana = {
19
+ text: (...columns: Expr<string | null>[]) => {
20
+ if (columns.length === 0) throw new Error('Hana.text needs at least one column');
21
+ return {
22
+ /** EXACT:按词,支持 "短语"、OR、-排除、* */
23
+ contains: (query: string): Pred =>
24
+ dialectFn<boolean>('hana.contains', [...columns, query], codecs.boolean),
25
+ /** 容错匹配,threshold 0–1(写进 SQL) */
26
+ fuzzy: (query: string, threshold: number): Pred => {
27
+ if (!(threshold >= 0 && threshold <= 1))
28
+ throw new RangeError(`fuzzy threshold must be between 0 and 1, got ${threshold}`);
29
+ return dialectFn<boolean>(
30
+ 'hana.contains_fuzzy',
31
+ [...columns, query, { k: 'lit', sql: String(threshold) }],
32
+ codecs.boolean,
33
+ );
34
+ },
35
+ /** 本查询里 CONTAINS 的相关度 */
36
+ score: () => dialectFn<number>('hana.score', [], codecs.double),
37
+ };
38
+ },
39
+ };
40
+
41
+ /** add / diff 的时、分、秒折合多少秒 */
42
+ const HANA_SECONDS: Record<string, number> = { second: 1, minute: 60, hour: 3600, day: 86400 };
43
+
44
+ const columnList = (cols: string[]) => (cols.length === 1 ? cols[0]! : `(${cols.join(', ')})`);
45
+
46
+ /** 加进 HANA 方言的函数表 */
47
+ export const hanaFunctions: Record<string, FunctionRenderer> = {
48
+ // 通用函数里与标准写法不同的部分(HANA 的 LOCATE 参数顺序是 字符串、子串)
49
+ index_of: (a) => `(LOCATE(${a[0]}, ${a[1]}) - 1)`,
50
+ add_days: (a) => `ADD_DAYS(${a[0]}, ${a[1]})`,
51
+ add_months: (a) => `ADD_MONTHS(${a[0]}, ${a[1]})`,
52
+ diff_days: (a) => `DAYS_BETWEEN(${a[1]}, ${a[0]})`,
53
+ add_time: (a) => {
54
+ const unit = unquoteSql(a[2]!);
55
+ if (unit === 'year') return `ADD_YEARS(${a[0]}, ${a[1]})`;
56
+ if (unit === 'month') return `ADD_MONTHS(${a[0]}, ${a[1]})`;
57
+ if (unit === 'day') return `ADD_DAYS(${a[0]}, ${a[1]})`;
58
+ return `ADD_SECONDS(${a[0]}, (${a[1]}) * ${HANA_SECONDS[unit]})`;
59
+ },
60
+ diff_time: (a) =>
61
+ `CAST(SECONDS_BETWEEN(${a[1]}, ${a[0]}) / ${HANA_SECONDS[unquoteSql(a[2]!)]} AS BIGINT)`,
62
+ hour: (a) => `HOUR(${a[0]})`,
63
+ minute: (a) => `MINUTE(${a[0]})`,
64
+ second: (a) => `CAST(FLOOR(SECOND(${a[0]})) AS INTEGER)`,
65
+ day_of_year: (a) => `DAYOFYEAR(${a[0]})`,
66
+ // WEEKDAY:星期一 0 … 星期日 6
67
+ day_of_week: (a) => `MOD(WEEKDAY(${a[0]}) + 1, 7)`,
68
+ iso_day_of_week: (a) => `(WEEKDAY(${a[0]}) + 1)`,
69
+ now: () => 'CURRENT_UTCTIMESTAMP',
70
+ in_csv: (a) => `((',' || ${a[1]} || ',') LIKE ('%,' || ${a[0]} || ',%'))`,
71
+ truncate: (a) => `ROUND(${a[0]}, ${a[1]}, ROUND_DOWN)`,
72
+ log10: (a) => `LOG(10, ${a[0]})`,
73
+ // JSON:JSON_QUERY 取对象、数组,JSON_VALUE 取标量
74
+ json_value: (a) => `JSON_QUERY(${a[0]}, ${standardJsonPath(a[1]!)})`,
75
+ json_text: (a) => `JSON_VALUE(${a[0]}, ${standardJsonPath(a[1]!)})`,
76
+ json_number: (a) => `CAST(JSON_VALUE(${a[0]}, ${standardJsonPath(a[1]!)}) AS DOUBLE)`,
77
+ json_boolean: (a) => `(JSON_VALUE(${a[0]}, ${standardJsonPath(a[1]!)}) = 'true')`,
78
+ json_has: (a) =>
79
+ `(JSON_VALUE(${a[0]}, ${standardJsonPath(a[1]!)}) IS NOT NULL OR JSON_QUERY(${a[0]}, ${standardJsonPath(a[1]!)}) IS NOT NULL)`,
80
+ cast_string: (a) => `CAST(${a[0]} AS NVARCHAR(5000))`,
81
+ cast_double: (a) => `CAST(${a[0]} AS DOUBLE)`,
82
+ 'hana.contains': (a) => `CONTAINS(${columnList(a.slice(0, -1))}, ${a[a.length - 1]})`,
83
+ 'hana.contains_fuzzy': (a) =>
84
+ `CONTAINS(${columnList(a.slice(0, -2))}, ${a[a.length - 2]}, FUZZY(${a[a.length - 1]}))`,
85
+ 'hana.score': () => 'SCORE()',
86
+ };
package/src/hdb.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /** hdb(SAP 的纯 JS HANA 驱动)没有自带类型:这里只声明适配器用到的部分 */
2
+ declare module 'hdb' {
3
+ type Callback<T> = (error: Error | null, result: T) => void;
4
+
5
+ export interface Statement {
6
+ exec(values: unknown[], cb: Callback<unknown>): void;
7
+ drop(cb?: (error: Error | null) => void): void;
8
+ }
9
+
10
+ export interface Client {
11
+ connect(cb: (error: Error | null) => void): void;
12
+ exec(sql: string, cb: Callback<unknown>): void;
13
+ prepare(sql: string, cb: Callback<Statement>): void;
14
+ setAutoCommit(autoCommit: boolean): void;
15
+ commit(cb: (error: Error | null) => void): void;
16
+ rollback(cb: (error: Error | null) => void): void;
17
+ on(event: 'error', listener: (error: Error) => void): void;
18
+ end(): void;
19
+ }
20
+
21
+ export interface ClientOptions {
22
+ host: string;
23
+ port: number;
24
+ user: string;
25
+ password: string;
26
+ [option: string]: unknown;
27
+ }
28
+
29
+ const hdb: { createClient(options: ClientOptions): Client };
30
+ export default hdb;
31
+ }