-
-
Notifications
You must be signed in to change notification settings - Fork 144
Expand file tree
/
Copy pathtemp-alias-transformer.ts
More file actions
69 lines (61 loc) · 2.56 KB
/
temp-alias-transformer.ts
File metadata and controls
69 lines (61 loc) · 2.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
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
62
63
64
65
66
67
68
69
import { IdentifierNode, OperationNodeTransformer, type OperationNode, type QueryId } from 'kysely';
import { TEMP_ALIAS_PREFIX } from '../query-utils';
type TempAliasTransformerMode = 'alwaysCompact' | 'compactLongNames';
type TempAliasTransformerOptions = {
mode?: TempAliasTransformerMode;
maxIdentifierLength?: number;
};
/**
* Kysely node transformer that replaces temporary aliases created during query construction with
* shorter names while ensuring the same temp alias gets replaced with the same name.
*/
export class TempAliasTransformer extends OperationNodeTransformer {
private aliasMap = new Map<string, string>();
private readonly textEncoder = new TextEncoder();
private readonly mode: TempAliasTransformerMode;
private readonly maxIdentifierLength: number;
constructor(options: TempAliasTransformerOptions = {}) {
super();
this.mode = options.mode ?? 'alwaysCompact';
// PostgreSQL limits identifier length to 63 bytes and silently truncates overlong aliases.
const maxIdentifierLength = options.maxIdentifierLength ?? 63;
if (
!Number.isFinite(maxIdentifierLength) ||
!Number.isInteger(maxIdentifierLength) ||
maxIdentifierLength <= 0
) {
throw new RangeError('maxIdentifierLength must be a positive integer');
}
this.maxIdentifierLength = maxIdentifierLength;
}
run<T extends OperationNode>(node: T): T {
this.aliasMap.clear();
return this.transformNode(node);
}
protected override transformIdentifier(node: IdentifierNode, queryId?: QueryId): IdentifierNode {
if (!node.name.startsWith(TEMP_ALIAS_PREFIX)) {
return super.transformIdentifier(node, queryId);
}
let shouldCompact = false;
if (this.mode === 'alwaysCompact') {
shouldCompact = true;
} else {
// check if the alias name exceeds the max identifier length, and
// if so, compact it
const aliasByteLength = this.textEncoder.encode(node.name).length;
if (aliasByteLength > this.maxIdentifierLength) {
shouldCompact = true;
}
}
if (shouldCompact) {
let mapped = this.aliasMap.get(node.name);
if (!mapped) {
mapped = `$$t${this.aliasMap.size + 1}`;
this.aliasMap.set(node.name, mapped);
}
return IdentifierNode.create(mapped);
} else {
return super.transformIdentifier(node, queryId);
}
}
}