typeorm-extensions 1.3.5 → 2.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/README.md +14 -10
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +2 -2
- package/dist/extensions/cursor-pagination.extension.d.ts +18 -0
- package/dist/extensions/cursor-pagination.extension.js +108 -0
- package/dist/extensions/index.d.ts +7 -0
- package/dist/extensions/index.js +7 -0
- package/dist/extensions/order-by-array-position.extension.d.ts +24 -0
- package/dist/extensions/order-by-array-position.extension.js +18 -0
- package/dist/extensions/order.extension.d.ts +3 -2
- package/dist/extensions/order.extension.js +16 -13
- package/dist/extensions/period-filter.extension.d.ts +29 -0
- package/dist/extensions/period-filter.extension.js +23 -0
- package/dist/extensions/typed/index.d.ts +10 -0
- package/dist/extensions/typed/index.js +10 -0
- package/dist/extensions/typed/typed-distinct/typed-distinct.extension.js +2 -2
- package/dist/extensions/typed/typed-joins/typed-joins.extension.js +2 -1
- package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.d.ts +20 -0
- package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.js +21 -0
- package/dist/extensions/typed/typed-order-by/typed-order-by.extension.d.ts +4 -3
- package/dist/extensions/typed/typed-order-by/typed-order-by.extension.js +7 -6
- package/dist/extensions/typed/typed-returning/typed-returning.extension.d.ts +27 -0
- package/dist/extensions/typed/typed-returning/typed-returning.extension.js +38 -0
- package/dist/extensions/typed/typed-select/typed-select.extension.d.ts +6 -6
- package/dist/extensions/typed/typed-select/typed-select.extension.js +11 -8
- package/dist/extensions/typed/typed-where/typed-period-filter.extension.d.ts +31 -0
- package/dist/extensions/typed/typed-where/typed-period-filter.extension.js +25 -0
- package/dist/extensions/typed/typed-where/typed-where-array.extension.d.ts +68 -0
- package/dist/extensions/typed/typed-where/typed-where-array.extension.js +41 -0
- package/dist/extensions/typed/typed-where/typed-where-ilike.extension.d.ts +62 -0
- package/dist/extensions/typed/typed-where/typed-where-ilike.extension.js +84 -0
- package/dist/extensions/typed/typed-where/typed-where-is-any.extension.d.ts +144 -0
- package/dist/extensions/typed/typed-where/typed-where-is-any.extension.js +37 -0
- package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.d.ts +53 -0
- package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.js +38 -0
- package/dist/extensions/typed/typed-where/typed-where-is-in.extension.d.ts +25 -26
- package/dist/extensions/typed/typed-where/typed-where-is-in.extension.js +16 -23
- package/dist/extensions/typed/typed-where/typed-where-json.extension.d.ts +53 -0
- package/dist/extensions/typed/typed-where/typed-where-json.extension.js +39 -0
- package/dist/extensions/typed/typed-where/typed-where-lower.extension.d.ts +59 -0
- package/dist/extensions/typed/typed-where/typed-where-lower.extension.js +86 -0
- package/dist/extensions/typed/typed-where/typed-where-similar.extension.d.ts +54 -0
- package/dist/extensions/typed/typed-where/typed-where-similar.extension.js +73 -0
- package/dist/extensions/typed/typed-where/typed-where.extension.d.ts +13 -13
- package/dist/extensions/typed/typed-where/typed-where.extension.js +4 -12
- package/dist/extensions/where-array.extension.d.ts +70 -0
- package/dist/extensions/where-array.extension.js +47 -0
- package/dist/extensions/where-is-any.extension.d.ts +142 -0
- package/dist/extensions/where-is-any.extension.js +44 -0
- package/dist/extensions/where-is-distinct-from.extension.d.ts +56 -0
- package/dist/extensions/where-is-distinct-from.extension.js +55 -0
- package/dist/extensions/where-json.extension.d.ts +55 -0
- package/dist/extensions/where-json.extension.js +64 -0
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +23 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/types/extensions/cursor-pagination.types.d.ts +16 -0
- package/dist/types/extensions/cursor-pagination.types.js +2 -0
- package/dist/types/extensions/get-many-with-totals.types.d.ts +3 -1
- package/dist/types/extensions/index.d.ts +4 -0
- package/dist/types/extensions/index.js +4 -0
- package/dist/types/extensions/order.types.d.ts +1 -1
- package/dist/types/extensions/period.types.d.ts +15 -0
- package/dist/types/extensions/period.types.js +2 -0
- package/dist/types/extensions/search.types.d.ts +68 -0
- package/dist/types/extensions/search.types.js +2 -0
- package/dist/types/extensions/typed-order-by.types.d.ts +3 -0
- package/dist/types/extensions/typed-order-by.types.js +2 -0
- package/dist/types/extensions/typed-where.types.d.ts +4 -4
- package/dist/types/extensions/where.types.d.ts +1 -0
- package/dist/types/interfaces/cursor-pagination.interface.d.ts +16 -0
- package/dist/types/interfaces/cursor-pagination.interface.js +2 -0
- package/dist/types/interfaces/index.d.ts +2 -0
- package/dist/types/interfaces/index.js +2 -0
- package/dist/types/interfaces/period.interface.d.ts +12 -0
- package/dist/types/interfaces/period.interface.js +2 -0
- package/dist/types/modules/proxy-callback.types.d.ts +26 -3
- package/dist/types/util-types/index.d.ts +3 -1
- package/dist/utils/cursor.utils.d.ts +31 -0
- package/dist/utils/cursor.utils.js +130 -0
- package/dist/utils/order.utils.d.ts +15 -0
- package/dist/utils/order.utils.js +45 -0
- package/dist/utils/period.utils.d.ts +9 -0
- package/dist/utils/period.utils.js +82 -0
- package/dist/utils/proxy-key.utils.d.ts +13 -2
- package/dist/utils/proxy-key.utils.js +32 -14
- package/dist/utils/query-builder.utils.d.ts +46 -0
- package/dist/utils/query-builder.utils.js +208 -0
- package/dist/utils/search.utils.d.ts +27 -0
- package/dist/utils/search.utils.js +83 -0
- package/dist/utils/sql.utils.d.ts +27 -1
- package/dist/utils/sql.utils.js +87 -9
- package/dist/utils/table.utils.d.ts +86 -0
- package/dist/utils/table.utils.js +135 -0
- package/dist/utils/where.utils.d.ts +18 -1
- package/dist/utils/where.utils.js +62 -9
- package/helpers/package.json +7 -0
- package/package.json +3 -3
- package/dist/utils/identifier.utils.d.ts +0 -15
- package/dist/utils/identifier.utils.js +0 -45
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.attachPeriodFilter = void 0;
|
|
4
|
+
const sql_utils_1 = require("./sql.utils");
|
|
5
|
+
const where_utils_1 = require("./where.utils");
|
|
6
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
7
|
+
/** `'2024-01-31'` as a UTC midnight Date, or a `TypeError` if it is not a calendar date (`'2024-02-30'`). */
|
|
8
|
+
function parseDateOnly(value, name) {
|
|
9
|
+
const day = new Date(`${value}T00:00:00.000Z`);
|
|
10
|
+
if (isNaN(day.getTime()) || day.toISOString().slice(0, 10) !== value) {
|
|
11
|
+
throw new TypeError(`Invalid ${name} "${value}": not a calendar date`);
|
|
12
|
+
}
|
|
13
|
+
return day;
|
|
14
|
+
}
|
|
15
|
+
/** A usable boundary, `undefined` for a missing one (`undefined`, `null`, `''`). */
|
|
16
|
+
function normalizeBoundary(value, name) {
|
|
17
|
+
if (value === undefined || value === null || value === '') {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
if (Object.prototype.toString.call(value) === '[object Date]') {
|
|
21
|
+
if (isNaN(value.getTime())) {
|
|
22
|
+
throw new TypeError(`Invalid ${name}: invalid Date`);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value !== 'string') {
|
|
27
|
+
throw new TypeError(`Invalid ${name}: expected a Date or a string, got ${typeof value}`);
|
|
28
|
+
}
|
|
29
|
+
if (DATE_ONLY.test(value)) {
|
|
30
|
+
parseDateOnly(value, name);
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
/** The day after a date-only string, or `undefined` past year 9999. */
|
|
35
|
+
function getNextDay(value, name) {
|
|
36
|
+
const day = parseDateOnly(value, name);
|
|
37
|
+
day.setUTCDate(day.getUTCDate() + 1);
|
|
38
|
+
const next = day.toISOString().slice(0, 10);
|
|
39
|
+
return DATE_ONLY.test(next) ? next : undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The comparison for one end of the period. A date-only string stands for the whole day,
|
|
43
|
+
* so excluding the start or including the end moves the bound to the next day:
|
|
44
|
+
* `> '2024-01-01'` would still match timestamps later on January 1st.
|
|
45
|
+
*/
|
|
46
|
+
function getBoundCondition(boundary, name, include) {
|
|
47
|
+
const isFrom = name === 'periodFrom';
|
|
48
|
+
const nextDay = typeof boundary === 'string' && DATE_ONLY.test(boundary) && include !== isFrom
|
|
49
|
+
? getNextDay(boundary, name)
|
|
50
|
+
: undefined;
|
|
51
|
+
if (nextDay !== undefined) {
|
|
52
|
+
// excluded start day: `>= next day`; included end day: `< next day`
|
|
53
|
+
return { operator: isFrom ? '>=' : '<', value: nextDay };
|
|
54
|
+
}
|
|
55
|
+
if (isFrom) {
|
|
56
|
+
return { operator: include ? '>=' : '>', value: boundary };
|
|
57
|
+
}
|
|
58
|
+
return { operator: include ? '<=' : '<', value: boundary };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Adds `column >= :periodFrom` and `column <= :periodTo` conditions (or their exclusive versions) with
|
|
62
|
+
* AND, so the filter narrows whatever WHERE conditions the query already has, OR ones included:
|
|
63
|
+
* `(a OR b) AND column >= …`. `column` is already quoted.
|
|
64
|
+
*/
|
|
65
|
+
function attachPeriodFilter(builder, column, periodFilter, options) {
|
|
66
|
+
const { includeFrom = true, includeTo = true } = options !== null && options !== void 0 ? options : {};
|
|
67
|
+
const bounds = [
|
|
68
|
+
{ name: 'periodFrom', include: includeFrom },
|
|
69
|
+
{ name: 'periodTo', include: includeTo },
|
|
70
|
+
];
|
|
71
|
+
for (const { name, include } of bounds) {
|
|
72
|
+
const boundary = normalizeBoundary(periodFilter === null || periodFilter === void 0 ? void 0 : periodFilter[name], name);
|
|
73
|
+
if (boundary === undefined) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const { operator, value } = getBoundCondition(boundary, name, include);
|
|
77
|
+
const paramName = (0, sql_utils_1.createUniqueParameterName)(name);
|
|
78
|
+
(0, where_utils_1.andWhereNarrowing)(builder, `${column} ${operator} :${paramName}`, { [paramName]: value });
|
|
79
|
+
}
|
|
80
|
+
return builder;
|
|
81
|
+
}
|
|
82
|
+
exports.attachPeriodFilter = attachPeriodFilter;
|
|
@@ -3,7 +3,7 @@ import { KeyProxyCallback } from '../types/modules/proxy-callback.types';
|
|
|
3
3
|
export declare class KeyHolder {
|
|
4
4
|
private _keys;
|
|
5
5
|
constructor(_keys?: string[]);
|
|
6
|
-
toSql(length?: number,
|
|
6
|
+
toSql(length?: number, useQuotes?: boolean): string;
|
|
7
7
|
toString(length?: number): string;
|
|
8
8
|
valueOf(): string;
|
|
9
9
|
[Symbol.toPrimitive](): string;
|
|
@@ -19,6 +19,17 @@ export declare class KeyProxy {
|
|
|
19
19
|
static defaultHandler: ProxyHandler<KeyHolder>;
|
|
20
20
|
static create<T extends ObjectLiteral>(): T;
|
|
21
21
|
}
|
|
22
|
+
/** Typed keys are `column` or `alias.column`: a deeper path can't be mapped to a query alias. */
|
|
23
|
+
export declare const MAX_KEY_DEPTH = 2;
|
|
22
24
|
export declare const getKeyHolder: <Type extends ObjectLiteral>(callback: KeyHolder | KeyProxyCallback<Type>) => KeyHolder | Array<KeyHolder>;
|
|
23
25
|
export declare const getKey: <Type extends ObjectLiteral>(callback: string | KeyHolder | KeyProxyCallback<Type>) => string;
|
|
24
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Alias to prefix a single-level key with: a string, or a function called only when it is needed,
|
|
28
|
+
* e.g. `() => query.alias`, which throws while the query has no main alias (before `from()`).
|
|
29
|
+
*/
|
|
30
|
+
export type KeyPrefix = string | (() => string | undefined);
|
|
31
|
+
/**
|
|
32
|
+
* SQL key of a typed callback: `(u) => u.id` → `prefix.id`, `(u) => u.images.url` → `images.url`
|
|
33
|
+
* (an `alias.column` path keeps its own alias). Strings are returned as is.
|
|
34
|
+
*/
|
|
35
|
+
export declare const getSqlKeyFromProxyCallback: <Type extends ObjectLiteral>(callback: string | KeyHolder | KeyProxyCallback<Type>, addPrefix?: KeyPrefix, useQuotes?: boolean) => string | string[];
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSqlKeyFromProxyCallback = exports.getKey = exports.getKeyHolder = exports.KeyProxy = exports.TypedProxy = exports.KeyHolder = void 0;
|
|
3
|
+
exports.getSqlKeyFromProxyCallback = exports.getKey = exports.getKeyHolder = exports.MAX_KEY_DEPTH = exports.KeyProxy = exports.TypedProxy = exports.KeyHolder = void 0;
|
|
4
4
|
const sql_utils_1 = require("./sql.utils");
|
|
5
5
|
class KeyHolder {
|
|
6
6
|
constructor(_keys = []) {
|
|
7
7
|
this._keys = _keys;
|
|
8
8
|
this._keys = [..._keys];
|
|
9
9
|
}
|
|
10
|
-
toSql(length = 2,
|
|
10
|
+
toSql(length = 2, useQuotes) {
|
|
11
11
|
const string = this.toString(length);
|
|
12
|
-
const sql =
|
|
12
|
+
const sql = useQuotes ? (0, sql_utils_1.stringToSQLIdentifier)(string) : string;
|
|
13
13
|
return sql;
|
|
14
14
|
}
|
|
15
15
|
toString(length = 2) {
|
|
@@ -69,14 +69,26 @@ KeyProxy.defaultHandler = {
|
|
|
69
69
|
return newLayer;
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
|
+
/** Typed keys are `column` or `alias.column`: a deeper path can't be mapped to a query alias. */
|
|
73
|
+
exports.MAX_KEY_DEPTH = 2;
|
|
74
|
+
const assertKeyDepth = (keyHolder) => {
|
|
75
|
+
const keys = keyHolder.getKeys();
|
|
76
|
+
if (keys.length > exports.MAX_KEY_DEPTH) {
|
|
77
|
+
throw new TypeError(`Typed key "${keys.join('.')}" is ${keys.length} levels deep, but typed methods support only ` +
|
|
78
|
+
`"column" or "alias.column". Join the nested relation and address it by its alias ` +
|
|
79
|
+
`with an alias-keyed type, e.g. type Joins = { someAlias: SomeEntity }; (j) => j.someAlias.column`);
|
|
80
|
+
}
|
|
81
|
+
return keyHolder;
|
|
82
|
+
};
|
|
72
83
|
const getKeyHolder = (callback) => {
|
|
73
84
|
if (typeof callback !== 'function') {
|
|
74
85
|
if (callback instanceof KeyHolder) {
|
|
75
|
-
return callback;
|
|
86
|
+
return assertKeyDepth(callback);
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
const keyHolder = callback(KeyProxy.create());
|
|
79
|
-
|
|
90
|
+
// fail loudly instead of silently dropping levels beyond `alias.column`
|
|
91
|
+
return Array.isArray(keyHolder) ? keyHolder.map(assertKeyDepth) : assertKeyDepth(keyHolder);
|
|
80
92
|
};
|
|
81
93
|
exports.getKeyHolder = getKeyHolder;
|
|
82
94
|
const getKey = (callback) => {
|
|
@@ -86,26 +98,32 @@ const getKey = (callback) => {
|
|
|
86
98
|
return keyHolder.toString();
|
|
87
99
|
};
|
|
88
100
|
exports.getKey = getKey;
|
|
89
|
-
|
|
101
|
+
/**
|
|
102
|
+
* SQL key of a typed callback: `(u) => u.id` → `prefix.id`, `(u) => u.images.url` → `images.url`
|
|
103
|
+
* (an `alias.column` path keeps its own alias). Strings are returned as is.
|
|
104
|
+
*/
|
|
105
|
+
const getSqlKeyFromProxyCallback = (callback, addPrefix, useQuotes) => {
|
|
90
106
|
if (typeof callback === 'string')
|
|
91
107
|
return callback;
|
|
92
108
|
const keyHolder = (0, exports.getKeyHolder)(callback);
|
|
93
109
|
if (Array.isArray(keyHolder)) {
|
|
94
|
-
return keyHolder.map((holder) => (0, exports.getSqlKeyFromProxyCallback)(holder, addPrefix));
|
|
110
|
+
return keyHolder.map((holder) => (0, exports.getSqlKeyFromProxyCallback)(holder, addPrefix, useQuotes));
|
|
95
111
|
}
|
|
96
112
|
// if select is like this: (proxy) => proxy.relation.column
|
|
97
113
|
if (keyHolder.getKeys().length > 1) {
|
|
98
114
|
// return unchanged
|
|
99
|
-
return keyHolder.toSql(undefined,
|
|
115
|
+
return keyHolder.toSql(undefined, useQuotes);
|
|
100
116
|
}
|
|
101
117
|
// else if select is one layer depth like this: (proxy) => proxy.column
|
|
102
|
-
// then add alias before the key
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
// then add alias before the key; resolve the alias only now, a path above doesn't need it
|
|
119
|
+
const prefix = typeof addPrefix === 'function' ? addPrefix() : addPrefix;
|
|
120
|
+
if (typeof prefix === 'string' && prefix.length >= 1) {
|
|
121
|
+
// a new holder: a KeyHolder passed in by the caller must stay as it was
|
|
122
|
+
return new KeyHolder([prefix, ...keyHolder.getKeys()]).toSql(undefined, useQuotes);
|
|
105
123
|
}
|
|
106
|
-
return keyHolder.toSql(undefined,
|
|
124
|
+
return keyHolder.toSql(undefined, useQuotes);
|
|
107
125
|
};
|
|
108
126
|
exports.getSqlKeyFromProxyCallback = getSqlKeyFromProxyCallback;
|
|
109
127
|
// example of typed select
|
|
110
|
-
// const key =
|
|
111
|
-
// console.log(key);
|
|
128
|
+
// const key = getKey<UserEntity>((proxy) => proxy.sessions);
|
|
129
|
+
// console.log(key); // "sessions"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ObjectLiteral, WhereExpressionBuilder } from 'typeorm';
|
|
2
|
+
import type { KeyProxyCallback } from '../types/modules/proxy-callback.types';
|
|
3
|
+
/**
|
|
4
|
+
* Database name of an entity property's column (`@Column({ name })`), looked up in the metadata of
|
|
5
|
+
* a query alias (the main alias when `aliasName` is undefined). `undefined` when the alias has no
|
|
6
|
+
* entity or the property isn't a column.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getColumnDatabaseName(builder: WhereExpressionBuilder, aliasName: string | undefined, propertyPath: string): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* SQL for a field passed as a string to the untyped methods (`whereIsIn('user.first-name', …)`).
|
|
11
|
+
*
|
|
12
|
+
* A string can't tell a column from an expression: `user.first-name` may be the column `first-name`
|
|
13
|
+
* or `user.first` minus `name`. The query builder knows its aliases and their entities, though:
|
|
14
|
+
* - `alias.column`, where `alias` is an alias of the query and `column` one of its entity's columns
|
|
15
|
+
* (by database name or property path): the column, quoted by the driver, `"user"."first-name"`.
|
|
16
|
+
* Property paths are mapped to their columns (`user.createdAt` → `"user"."created_at"`).
|
|
17
|
+
* UPDATE / DELETE give the bare column, as they have no alias.
|
|
18
|
+
* - `column`, a database column name of the main entity: the quoted column, `"first-name"`. Property
|
|
19
|
+
* names aren't mapped without an alias, as `status` may also be a column of a joined table.
|
|
20
|
+
* - anything else (unknown aliases, subqueries, expressions such as `LOWER(u.email)` or
|
|
21
|
+
* `u.price - u.discount`): quoted only when it is a path of plain identifiers, as
|
|
22
|
+
* `stringToSQLIdentifier` does, in the driver's quotes.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toFieldSql(builder: WhereExpressionBuilder, field: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* SQL key for a typed callback on `builder`: `"alias"."column"`, or `"column"` on UPDATE / DELETE.
|
|
27
|
+
*
|
|
28
|
+
* The property is mapped to its database column name (`@Column({ name })`, embedded columns)
|
|
29
|
+
* through the metadata of the alias it belongs to (see `resolveTypedKey`). Aliases without
|
|
30
|
+
* metadata (subqueries) and unknown properties keep the property name.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getTypedColumnKey<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, callback: KeyProxyCallback<Type>): string | string[];
|
|
33
|
+
/**
|
|
34
|
+
* Quoted column of a typed callback that must select exactly one column
|
|
35
|
+
* (`whereIsAnyTyped`, `whereArrayContainsTyped`, …). Throws a `TypeError` for several columns:
|
|
36
|
+
* one condition per column has no single obvious meaning (AND? OR?), so don't guess.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getTypedSingleColumnKey<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, callback: KeyProxyCallback<Type>, methodName: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Key for typed select / order / distinct on a SelectQueryBuilder.
|
|
41
|
+
*
|
|
42
|
+
* Without quotes it is `alias.propertyPath` (`user.name`, `images.url`, `p.address.city`),
|
|
43
|
+
* which TypeORM maps to the column itself. With `useQuotes` it is the quoted database column
|
|
44
|
+
* (`"t"."ticket_status"`), since TypeORM doesn't map quoted keys. Strings are kept as is.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getTypedSelectionKey<Type extends ObjectLiteral>(builder: WhereExpressionBuilder, selection: KeyProxyCallback<Type> | string, useQuotes?: boolean): string | string[];
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTypedSelectionKey = exports.getTypedSingleColumnKey = exports.getTypedColumnKey = exports.toFieldSql = exports.getColumnDatabaseName = void 0;
|
|
4
|
+
const proxy_key_utils_1 = require("./proxy-key.utils");
|
|
5
|
+
const sql_utils_1 = require("./sql.utils");
|
|
6
|
+
/**
|
|
7
|
+
* Whether single-level typed keys are qualified with the main alias (`user.firstName`).
|
|
8
|
+
*
|
|
9
|
+
* UPDATE / DELETE builders turn alias prefixing off (`aliasNamePrefixingEnabled === false`)
|
|
10
|
+
* and render `UPDATE "users" … WHERE …` without an alias, where `"UserEntity"."firstName"`
|
|
11
|
+
* is invalid: their keys are just the column, `"firstName"`.
|
|
12
|
+
*/
|
|
13
|
+
function isAliasPrefixingEnabled(queryBuilder) {
|
|
14
|
+
var _a;
|
|
15
|
+
return ((_a = queryBuilder.expressionMap) === null || _a === void 0 ? void 0 : _a.aliasNamePrefixingEnabled) !== false;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Main alias of the query for the single-level key `key`. A query created without an entity
|
|
19
|
+
* (`dataSource.createQueryBuilder()`, `qb.subQuery()`) has none until `from()`: throw a
|
|
20
|
+
* `TypeError` that says how to fix the call, instead of TypeORM's "Main alias is not set".
|
|
21
|
+
*/
|
|
22
|
+
function requireMainAliasName(queryBuilder, key) {
|
|
23
|
+
var _a;
|
|
24
|
+
const mainAlias = (_a = queryBuilder.expressionMap) === null || _a === void 0 ? void 0 : _a.mainAlias;
|
|
25
|
+
if (!mainAlias) {
|
|
26
|
+
throw new TypeError(`Typed key "${key}" has no alias and the query has no main alias yet. ` +
|
|
27
|
+
`Call from() before this method, or address the column by its alias with an alias-keyed type: ` +
|
|
28
|
+
`type Q = { someAlias: SomeEntity }; (q) => q.someAlias.${key}`);
|
|
29
|
+
}
|
|
30
|
+
return mainAlias.name;
|
|
31
|
+
}
|
|
32
|
+
/** Entity metadata of a query alias (the main alias when `aliasName` is undefined), if it has any. */
|
|
33
|
+
function getAliasMetadata(queryBuilder, aliasName) {
|
|
34
|
+
const expressionMap = queryBuilder.expressionMap;
|
|
35
|
+
const alias = aliasName === undefined
|
|
36
|
+
? expressionMap === null || expressionMap === void 0 ? void 0 : expressionMap.mainAlias
|
|
37
|
+
: expressionMap === null || expressionMap === void 0 ? void 0 : expressionMap.aliases.find((queryAlias) => queryAlias.name === aliasName);
|
|
38
|
+
return (alias === null || alias === void 0 ? void 0 : alias.hasMetadata) ? alias.metadata : undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Database name of an entity property's column (`@Column({ name })`), looked up in the metadata of
|
|
42
|
+
* a query alias (the main alias when `aliasName` is undefined). `undefined` when the alias has no
|
|
43
|
+
* entity or the property isn't a column.
|
|
44
|
+
*/
|
|
45
|
+
function getColumnDatabaseName(builder, aliasName, propertyPath) {
|
|
46
|
+
var _a;
|
|
47
|
+
const metadata = getAliasMetadata(builder, aliasName);
|
|
48
|
+
return (_a = metadata === null || metadata === void 0 ? void 0 : metadata.findColumnWithPropertyPath(propertyPath)) === null || _a === void 0 ? void 0 : _a.databaseName;
|
|
49
|
+
}
|
|
50
|
+
exports.getColumnDatabaseName = getColumnDatabaseName;
|
|
51
|
+
/** A column of the entity by its database name (`first-name`) or its property path (`firstName`, `address.city`). */
|
|
52
|
+
function findColumn(metadata, name) {
|
|
53
|
+
var _a;
|
|
54
|
+
return ((_a = metadata.columns.find((column) => column.databaseName === name)) !== null && _a !== void 0 ? _a : metadata.findColumnWithPropertyPath(name));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* SQL for a field passed as a string to the untyped methods (`whereIsIn('user.first-name', …)`).
|
|
58
|
+
*
|
|
59
|
+
* A string can't tell a column from an expression: `user.first-name` may be the column `first-name`
|
|
60
|
+
* or `user.first` minus `name`. The query builder knows its aliases and their entities, though:
|
|
61
|
+
* - `alias.column`, where `alias` is an alias of the query and `column` one of its entity's columns
|
|
62
|
+
* (by database name or property path): the column, quoted by the driver, `"user"."first-name"`.
|
|
63
|
+
* Property paths are mapped to their columns (`user.createdAt` → `"user"."created_at"`).
|
|
64
|
+
* UPDATE / DELETE give the bare column, as they have no alias.
|
|
65
|
+
* - `column`, a database column name of the main entity: the quoted column, `"first-name"`. Property
|
|
66
|
+
* names aren't mapped without an alias, as `status` may also be a column of a joined table.
|
|
67
|
+
* - anything else (unknown aliases, subqueries, expressions such as `LOWER(u.email)` or
|
|
68
|
+
* `u.price - u.discount`): quoted only when it is a path of plain identifiers, as
|
|
69
|
+
* `stringToSQLIdentifier` does, in the driver's quotes.
|
|
70
|
+
*/
|
|
71
|
+
function toFieldSql(builder, field) {
|
|
72
|
+
var _a;
|
|
73
|
+
const queryBuilder = builder;
|
|
74
|
+
const dot = field.indexOf('.');
|
|
75
|
+
if (dot > 0) {
|
|
76
|
+
const aliasName = field.slice(0, dot);
|
|
77
|
+
const metadata = ((_a = queryBuilder.expressionMap) === null || _a === void 0 ? void 0 : _a.aliases.some((alias) => alias.name === aliasName))
|
|
78
|
+
? getAliasMetadata(queryBuilder, aliasName)
|
|
79
|
+
: undefined;
|
|
80
|
+
const column = metadata && findColumn(metadata, field.slice(dot + 1));
|
|
81
|
+
if (column) {
|
|
82
|
+
const columnSql = queryBuilder.escape(column.databaseName);
|
|
83
|
+
return isAliasPrefixingEnabled(queryBuilder) ? `${queryBuilder.escape(aliasName)}.${columnSql}` : columnSql;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// only by database name: an unqualified property name may mean another table's column
|
|
88
|
+
const metadata = getAliasMetadata(queryBuilder, undefined);
|
|
89
|
+
const column = metadata === null || metadata === void 0 ? void 0 : metadata.columns.find((candidate) => candidate.databaseName === field);
|
|
90
|
+
if (column) {
|
|
91
|
+
return queryBuilder.escape(column.databaseName);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return (0, sql_utils_1.stringToSQLIdentifier)(field, (0, sql_utils_1.getDriverType)(builder));
|
|
95
|
+
}
|
|
96
|
+
exports.toFieldSql = toFieldSql;
|
|
97
|
+
/**
|
|
98
|
+
* Splits typed keys into the alias and the property path they refer to:
|
|
99
|
+
* - `t => t.status` → the query's main alias + `status` (no alias on UPDATE / DELETE);
|
|
100
|
+
* - `j => j.t.status`, `u => u.images.url` → the first key, when it is an alias of the query;
|
|
101
|
+
* - `p => p.address.city` → when the first key isn't an alias but the main entity has a column
|
|
102
|
+
* with that path (an embedded entity), the main alias + `address.city`;
|
|
103
|
+
* - anything else → the first key as the alias (an alias the query builder doesn't know yet,
|
|
104
|
+
* e.g. `q => q.base.chatRoomId` before `.from(…, 'base')`).
|
|
105
|
+
*
|
|
106
|
+
* Only single-level keys need the main alias, so `alias.column` keys work before `from()`.
|
|
107
|
+
* Their column names can be mapped only through aliases already known to the query, though:
|
|
108
|
+
* before `from()` the property name is used as the column name.
|
|
109
|
+
*/
|
|
110
|
+
function resolveTypedKey(queryBuilder, keys) {
|
|
111
|
+
const prefixing = isAliasPrefixingEnabled(queryBuilder);
|
|
112
|
+
if (keys.length === 1) {
|
|
113
|
+
const aliasName = prefixing ? requireMainAliasName(queryBuilder, keys[0]) : undefined;
|
|
114
|
+
return {
|
|
115
|
+
aliasName,
|
|
116
|
+
propertyPath: keys[0],
|
|
117
|
+
metadata: getAliasMetadata(queryBuilder, aliasName),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const [firstKey, ...restKeys] = keys;
|
|
121
|
+
const expressionMap = queryBuilder.expressionMap;
|
|
122
|
+
const mainAlias = expressionMap === null || expressionMap === void 0 ? void 0 : expressionMap.mainAlias;
|
|
123
|
+
// UPDATE / DELETE have no alias to qualify with: `u => u.user.firstName` is `"firstName"` there
|
|
124
|
+
if (!prefixing && (mainAlias === null || mainAlias === void 0 ? void 0 : mainAlias.name) === firstKey) {
|
|
125
|
+
return {
|
|
126
|
+
aliasName: undefined,
|
|
127
|
+
propertyPath: restKeys.join('.'),
|
|
128
|
+
metadata: getAliasMetadata(queryBuilder, undefined),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const queryAlias = expressionMap === null || expressionMap === void 0 ? void 0 : expressionMap.aliases.find((alias) => alias.name === firstKey);
|
|
132
|
+
if (!queryAlias && mainAlias) {
|
|
133
|
+
const mainMetadata = getAliasMetadata(queryBuilder, undefined);
|
|
134
|
+
const embeddedPath = keys.join('.');
|
|
135
|
+
if (mainMetadata === null || mainMetadata === void 0 ? void 0 : mainMetadata.findColumnWithPropertyPath(embeddedPath)) {
|
|
136
|
+
return {
|
|
137
|
+
aliasName: prefixing ? mainAlias.name : undefined,
|
|
138
|
+
propertyPath: embeddedPath,
|
|
139
|
+
metadata: mainMetadata,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
aliasName: firstKey,
|
|
145
|
+
propertyPath: restKeys.join('.'),
|
|
146
|
+
metadata: (queryAlias === null || queryAlias === void 0 ? void 0 : queryAlias.hasMetadata) ? queryAlias.metadata : undefined,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** `"alias"."column"` (or `"column"` without an alias) with the database column name. */
|
|
150
|
+
function toQuotedColumnKey(queryBuilder, { aliasName, propertyPath, metadata }) {
|
|
151
|
+
var _a, _b;
|
|
152
|
+
const column = (_b = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.findColumnWithPropertyPath(propertyPath)) === null || _a === void 0 ? void 0 : _a.databaseName) !== null && _b !== void 0 ? _b : propertyPath;
|
|
153
|
+
return aliasName === undefined
|
|
154
|
+
? queryBuilder.escape(column)
|
|
155
|
+
: `${queryBuilder.escape(aliasName)}.${queryBuilder.escape(column)}`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* SQL key for a typed callback on `builder`: `"alias"."column"`, or `"column"` on UPDATE / DELETE.
|
|
159
|
+
*
|
|
160
|
+
* The property is mapped to its database column name (`@Column({ name })`, embedded columns)
|
|
161
|
+
* through the metadata of the alias it belongs to (see `resolveTypedKey`). Aliases without
|
|
162
|
+
* metadata (subqueries) and unknown properties keep the property name.
|
|
163
|
+
*/
|
|
164
|
+
function getTypedColumnKey(builder, callback) {
|
|
165
|
+
const queryBuilder = builder;
|
|
166
|
+
const toKey = (keyHolder) => toQuotedColumnKey(queryBuilder, resolveTypedKey(queryBuilder, keyHolder.getKeys()));
|
|
167
|
+
const keyHolder = (0, proxy_key_utils_1.getKeyHolder)(callback);
|
|
168
|
+
return Array.isArray(keyHolder) ? keyHolder.map(toKey) : toKey(keyHolder);
|
|
169
|
+
}
|
|
170
|
+
exports.getTypedColumnKey = getTypedColumnKey;
|
|
171
|
+
/**
|
|
172
|
+
* Quoted column of a typed callback that must select exactly one column
|
|
173
|
+
* (`whereIsAnyTyped`, `whereArrayContainsTyped`, …). Throws a `TypeError` for several columns:
|
|
174
|
+
* one condition per column has no single obvious meaning (AND? OR?), so don't guess.
|
|
175
|
+
*/
|
|
176
|
+
function getTypedSingleColumnKey(builder, callback, methodName) {
|
|
177
|
+
const key = getTypedColumnKey(builder, callback);
|
|
178
|
+
if (Array.isArray(key)) {
|
|
179
|
+
throw new TypeError(`${methodName} expects the callback to select a single column, got ${key.length}: ${key.join(', ')}`);
|
|
180
|
+
}
|
|
181
|
+
return key;
|
|
182
|
+
}
|
|
183
|
+
exports.getTypedSingleColumnKey = getTypedSingleColumnKey;
|
|
184
|
+
/**
|
|
185
|
+
* Key for typed select / order / distinct on a SelectQueryBuilder.
|
|
186
|
+
*
|
|
187
|
+
* Without quotes it is `alias.propertyPath` (`user.name`, `images.url`, `p.address.city`),
|
|
188
|
+
* which TypeORM maps to the column itself. With `useQuotes` it is the quoted database column
|
|
189
|
+
* (`"t"."ticket_status"`), since TypeORM doesn't map quoted keys. Strings are kept as is.
|
|
190
|
+
*/
|
|
191
|
+
function getTypedSelectionKey(builder, selection, useQuotes) {
|
|
192
|
+
if (typeof selection === 'string') {
|
|
193
|
+
return selection;
|
|
194
|
+
}
|
|
195
|
+
const queryBuilder = builder;
|
|
196
|
+
const toKey = (keyHolder) => {
|
|
197
|
+
const resolved = resolveTypedKey(queryBuilder, keyHolder.getKeys());
|
|
198
|
+
if (useQuotes) {
|
|
199
|
+
return toQuotedColumnKey(queryBuilder, resolved);
|
|
200
|
+
}
|
|
201
|
+
return resolved.aliasName === undefined
|
|
202
|
+
? resolved.propertyPath
|
|
203
|
+
: `${resolved.aliasName}.${resolved.propertyPath}`;
|
|
204
|
+
};
|
|
205
|
+
const keyHolder = (0, proxy_key_utils_1.getKeyHolder)(selection);
|
|
206
|
+
return Array.isArray(keyHolder) ? keyHolder.map(toKey) : toKey(keyHolder);
|
|
207
|
+
}
|
|
208
|
+
exports.getTypedSelectionKey = getTypedSelectionKey;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DatabaseType } from 'typeorm';
|
|
2
|
+
import type { LikeMatch } from '../types/extensions/search.types';
|
|
3
|
+
/**
|
|
4
|
+
* Escapes the LIKE wildcards `%` and `_`, and the escape character `\` itself, with a backslash:
|
|
5
|
+
* `50%_off` → `50\%\_off`. The default escape character of PostgreSQL and MySQL;
|
|
6
|
+
* SQLite needs `ESCAPE '\'`, which the search methods add.
|
|
7
|
+
*/
|
|
8
|
+
export declare function escapeLikePattern(value: string): string;
|
|
9
|
+
/** LIKE pattern for `value`: `%value%`, `value%`, `%value` or `value`. */
|
|
10
|
+
export declare function toLikePattern(value: string, match?: LikeMatch, escape?: boolean): string;
|
|
11
|
+
/**
|
|
12
|
+
* Trimmed search term, or `undefined` when there is nothing to search for (`null`, `undefined`,
|
|
13
|
+
* an empty or blank string): search methods add no condition then.
|
|
14
|
+
*/
|
|
15
|
+
export declare function toSearchTerm(value: unknown, methodName: string): string | undefined;
|
|
16
|
+
/** Function to wrap both sides with for `unaccent`, or `undefined` for none. */
|
|
17
|
+
export declare function toUnaccentFunction(unaccent: boolean | string | undefined): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Case-insensitive LIKE of `column` against `parameter` in the syntax of the database:
|
|
20
|
+
* - PostgreSQL (and any other driver): `column ILIKE :p`, which `gin_trgm_ops` indexes support;
|
|
21
|
+
* - MySQL / MariaDB: `column LIKE :p`, case-insensitive with the default `_ci` collations;
|
|
22
|
+
* - SQLite: `column LIKE :p ESCAPE '\'`, case-insensitive for ASCII; SQLite has no default
|
|
23
|
+
* escape character.
|
|
24
|
+
*
|
|
25
|
+
* With `unaccentFunction`, both sides are wrapped with it: `unaccent(column) ILIKE unaccent(:p)`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildILikeCondition(driver: DatabaseType | undefined, column: string, parameter: string, unaccentFunction?: string): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildILikeCondition = exports.toUnaccentFunction = exports.toSearchTerm = exports.toLikePattern = exports.escapeLikePattern = void 0;
|
|
4
|
+
const sql_utils_1 = require("./sql.utils");
|
|
5
|
+
/**
|
|
6
|
+
* Escapes the LIKE wildcards `%` and `_`, and the escape character `\` itself, with a backslash:
|
|
7
|
+
* `50%_off` → `50\%\_off`. The default escape character of PostgreSQL and MySQL;
|
|
8
|
+
* SQLite needs `ESCAPE '\'`, which the search methods add.
|
|
9
|
+
*/
|
|
10
|
+
function escapeLikePattern(value) {
|
|
11
|
+
return value.replace(/[\\%_]/g, '\\$&');
|
|
12
|
+
}
|
|
13
|
+
exports.escapeLikePattern = escapeLikePattern;
|
|
14
|
+
/** LIKE pattern for `value`: `%value%`, `value%`, `%value` or `value`. */
|
|
15
|
+
function toLikePattern(value, match = 'contains', escape = true) {
|
|
16
|
+
const text = escape ? escapeLikePattern(value) : value;
|
|
17
|
+
switch (match) {
|
|
18
|
+
case 'contains':
|
|
19
|
+
return `%${text}%`;
|
|
20
|
+
case 'prefix':
|
|
21
|
+
return `${text}%`;
|
|
22
|
+
case 'suffix':
|
|
23
|
+
return `%${text}`;
|
|
24
|
+
case 'exact':
|
|
25
|
+
return text;
|
|
26
|
+
default:
|
|
27
|
+
throw new TypeError(`Invalid match "${match}": expected "contains", "prefix", "suffix" or "exact"`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.toLikePattern = toLikePattern;
|
|
31
|
+
/**
|
|
32
|
+
* Trimmed search term, or `undefined` when there is nothing to search for (`null`, `undefined`,
|
|
33
|
+
* an empty or blank string): search methods add no condition then.
|
|
34
|
+
*/
|
|
35
|
+
function toSearchTerm(value, methodName) {
|
|
36
|
+
if (value === null || value === undefined) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
if (typeof value !== 'string') {
|
|
40
|
+
throw new TypeError(`${methodName} expects a string to search for, got ${typeof value}`);
|
|
41
|
+
}
|
|
42
|
+
const term = value.trim();
|
|
43
|
+
return term.length > 0 ? term : undefined;
|
|
44
|
+
}
|
|
45
|
+
exports.toSearchTerm = toSearchTerm;
|
|
46
|
+
// `unaccent`, `public.f_unaccent`: a function name, optionally schema-qualified
|
|
47
|
+
const FUNCTION_NAME = /^[A-Za-z_][\w$]*(?:\.[A-Za-z_][\w$]*)?$/;
|
|
48
|
+
/** Function to wrap both sides with for `unaccent`, or `undefined` for none. */
|
|
49
|
+
function toUnaccentFunction(unaccent) {
|
|
50
|
+
if (unaccent === undefined || unaccent === false) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
if (unaccent === true) {
|
|
54
|
+
return 'unaccent';
|
|
55
|
+
}
|
|
56
|
+
if (typeof unaccent !== 'string' || !FUNCTION_NAME.test(unaccent)) {
|
|
57
|
+
// the name is inserted into the SQL as is
|
|
58
|
+
throw new TypeError(`Invalid unaccent function "${unaccent}": expected a function name like "unaccent"`);
|
|
59
|
+
}
|
|
60
|
+
return unaccent;
|
|
61
|
+
}
|
|
62
|
+
exports.toUnaccentFunction = toUnaccentFunction;
|
|
63
|
+
/**
|
|
64
|
+
* Case-insensitive LIKE of `column` against `parameter` in the syntax of the database:
|
|
65
|
+
* - PostgreSQL (and any other driver): `column ILIKE :p`, which `gin_trgm_ops` indexes support;
|
|
66
|
+
* - MySQL / MariaDB: `column LIKE :p`, case-insensitive with the default `_ci` collations;
|
|
67
|
+
* - SQLite: `column LIKE :p ESCAPE '\'`, case-insensitive for ASCII; SQLite has no default
|
|
68
|
+
* escape character.
|
|
69
|
+
*
|
|
70
|
+
* With `unaccentFunction`, both sides are wrapped with it: `unaccent(column) ILIKE unaccent(:p)`.
|
|
71
|
+
*/
|
|
72
|
+
function buildILikeCondition(driver, column, parameter, unaccentFunction) {
|
|
73
|
+
const left = unaccentFunction ? `${unaccentFunction}(${column})` : column;
|
|
74
|
+
const right = unaccentFunction ? `${unaccentFunction}(${parameter})` : parameter;
|
|
75
|
+
if ((0, sql_utils_1.isMysqlLikeDriver)(driver)) {
|
|
76
|
+
return `${left} LIKE ${right}`;
|
|
77
|
+
}
|
|
78
|
+
if ((0, sql_utils_1.isSqliteLikeDriver)(driver)) {
|
|
79
|
+
return `${left} LIKE ${right} ESCAPE '\\'`;
|
|
80
|
+
}
|
|
81
|
+
return `${left} ILIKE ${right}`;
|
|
82
|
+
}
|
|
83
|
+
exports.buildILikeCondition = buildILikeCondition;
|
|
@@ -1,6 +1,32 @@
|
|
|
1
|
-
import { DatabaseType } from 'typeorm';
|
|
1
|
+
import type { DatabaseType, WhereExpressionBuilder } from 'typeorm';
|
|
2
2
|
export declare const wrapWithDoubleQoutes: (str: string) => string;
|
|
3
3
|
export declare const wrapWithBackQoutes: (str: string) => string;
|
|
4
|
+
/** MySQL, MariaDB and Aurora MySQL: backtick identifiers, `<=>` null-safe comparison. */
|
|
5
|
+
export declare const isMysqlLikeDriver: (driver?: DatabaseType) => boolean;
|
|
6
|
+
/** SQLite drivers: `IS` / `IS NOT` are null-safe comparisons. */
|
|
7
|
+
export declare const isSqliteLikeDriver: (driver?: DatabaseType) => boolean;
|
|
8
|
+
/** Database type of the query builder's connection, e.g. `'postgres'`; undefined without one. */
|
|
9
|
+
export declare function getDriverType(builder: WhereExpressionBuilder): DatabaseType | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Quotes one identifier, whatever characters it has: `first-name` → `"first-name"`, a `"` inside is
|
|
12
|
+
* doubled. An already quoted identifier is kept. For names known to be identifiers (columns, aliases).
|
|
13
|
+
*/
|
|
14
|
+
export declare const quoteIdentifier: (name: string, driver?: DatabaseType) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Quotes an alias or a table: as a path when it is one (`schema.table` → `"schema"."table"`),
|
|
17
|
+
* otherwise as one identifier (`user-1` → `"user-1"`).
|
|
18
|
+
*/
|
|
19
|
+
export declare const quoteIdentifierPath: (path: string, driver?: DatabaseType) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Quotes an identifier path: `user.id` → `"user"."id"`, `user.*` → `"user".*`.
|
|
22
|
+
* Already quoted segments are kept. Anything that isn't a path of identifiers
|
|
23
|
+
* (`COALESCE(a.x, b.x)`, `LOWER(u.email)`, `u.id::text`, `*`) is returned as is.
|
|
24
|
+
*/
|
|
4
25
|
export declare const stringToSQLIdentifier: (str: string, driver?: DatabaseType) => string;
|
|
5
26
|
export declare const stringToSQLValue: (str: string) => string;
|
|
6
27
|
export declare const createUniqueParameterName: (prefix?: string) => string;
|
|
28
|
+
/**
|
|
29
|
+
* Returns `arrayType` if it is a type name (`uuid[]`, `public.order_status[]`, `varchar(36)[]`),
|
|
30
|
+
* throws a `TypeError` otherwise: the type is inserted into the SQL as is.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertArrayType(arrayType: string): string;
|