oak-db 3.3.14 → 4.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 +5 -1
- package/lib/MySQL/connector.d.ts +9 -1
- package/lib/MySQL/connector.js +24 -0
- package/lib/MySQL/migration.d.ts +10 -0
- package/lib/MySQL/migration.js +649 -0
- package/lib/MySQL/store.d.ts +16 -2
- package/lib/MySQL/store.js +149 -109
- package/lib/MySQL/translator.d.ts +5 -1
- package/lib/MySQL/translator.js +29 -5
- package/lib/PostgreSQL/connector.d.ts +10 -0
- package/lib/PostgreSQL/connector.js +37 -11
- package/lib/PostgreSQL/migration.d.ts +10 -0
- package/lib/PostgreSQL/migration.js +984 -0
- package/lib/PostgreSQL/prepare.d.ts +2 -0
- package/lib/PostgreSQL/prepare.js +69 -0
- package/lib/PostgreSQL/store.d.ts +13 -2
- package/lib/PostgreSQL/store.js +142 -151
- package/lib/PostgreSQL/translator.d.ts +10 -2
- package/lib/PostgreSQL/translator.js +125 -9
- package/lib/index.d.ts +1 -0
- package/lib/migration.d.ts +27 -0
- package/lib/migration.js +1029 -0
- package/lib/types/dbStore.d.ts +7 -15
- package/lib/types/migration.d.ts +251 -0
- package/lib/types/migration.js +2 -0
- package/lib/utils/indexInspection.d.ts +4 -0
- package/lib/utils/indexInspection.js +32 -0
- package/lib/utils/indexName.d.ts +15 -0
- package/lib/utils/indexName.js +76 -0
- package/lib/utils/inspection.d.ts +13 -0
- package/lib/utils/inspection.js +56 -0
- package/package.json +5 -2
package/lib/types/dbStore.d.ts
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EntityDict, OperateOption, OperationResult, StorageSchema, TxnOption } from 'oak-domain/lib/types';
|
|
2
2
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
3
3
|
import { AsyncContext, AsyncRowStore } from "oak-domain/lib/store/AsyncRowStore";
|
|
4
4
|
import { CreateEntityOption } from "./Translator";
|
|
5
5
|
import { AggregationResult, SelectOption } from "oak-domain/lib/types";
|
|
6
|
-
export type Plan
|
|
7
|
-
|
|
8
|
-
attributes: Record<string, Attribute>;
|
|
9
|
-
}>;
|
|
10
|
-
newIndexes: Record<string, Index<any>[]>;
|
|
11
|
-
updatedTables: Record<string, {
|
|
12
|
-
attributes: Record<string, Attribute & {
|
|
13
|
-
isNew: boolean;
|
|
14
|
-
}>;
|
|
15
|
-
}>;
|
|
16
|
-
updatedIndexes: Record<string, Index<any>[]>;
|
|
17
|
-
};
|
|
6
|
+
export type { Plan } from './migration';
|
|
7
|
+
import type { MigrationPlanningOptions, Plan } from './migration';
|
|
18
8
|
export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends AsyncRowStore<ED, Cxt> {
|
|
19
9
|
checkRelationAsync<T extends keyof ED, Cxt extends AsyncContext<ED>>(entity: T, operation: Omit<ED[T]['Operation'] | ED[T]['Selection'], 'id'>, context: Cxt): Promise<void>;
|
|
20
10
|
connect: () => Promise<void>;
|
|
21
11
|
disconnect: () => Promise<void>;
|
|
12
|
+
exec(script: string, txnId?: string): Promise<void>;
|
|
13
|
+
supportsTransactionalDdl(): boolean;
|
|
22
14
|
initialize(options: CreateEntityOption): Promise<void>;
|
|
23
15
|
aggregate<T extends keyof ED, OP extends SelectOption>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
|
|
24
16
|
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OperateOption): Promise<OperationResult<ED>>;
|
|
@@ -28,6 +20,6 @@ export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asy
|
|
|
28
20
|
commit(txnId: string): Promise<void>;
|
|
29
21
|
rollback(txnId: string): Promise<void>;
|
|
30
22
|
readSchema(): Promise<StorageSchema<ED>>;
|
|
31
|
-
makeUpgradePlan(): Promise<Plan>;
|
|
32
|
-
diffSchema(schemaOld: StorageSchema<
|
|
23
|
+
makeUpgradePlan(options?: MigrationPlanningOptions): Promise<Plan>;
|
|
24
|
+
diffSchema(schemaOld: StorageSchema<ED>, schemaNew: StorageSchema<ED>, options?: MigrationPlanningOptions): Plan;
|
|
33
25
|
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { Attribute, EntityDict, GeneralEntityShape, Index, StorageDesc, StorageSchema } from 'oak-domain/lib/types';
|
|
2
|
+
export type MigrationEntityDict = EntityDict;
|
|
3
|
+
export type MigrationEntityKey = keyof MigrationEntityDict & string;
|
|
4
|
+
export type MigrationSchema<ED extends MigrationEntityDict = MigrationEntityDict> = StorageSchema<ED>;
|
|
5
|
+
export type MigrationTableDef<SH extends GeneralEntityShape = GeneralEntityShape> = StorageDesc<SH>;
|
|
6
|
+
export type MigrationIndex<SH extends GeneralEntityShape = GeneralEntityShape> = Index<SH>;
|
|
7
|
+
export interface MigrationIndexMetadata {
|
|
8
|
+
__originName?: string;
|
|
9
|
+
__originNames?: string[];
|
|
10
|
+
}
|
|
11
|
+
export type MigrationIndexWithOrigin = MigrationIndex & MigrationIndexMetadata;
|
|
12
|
+
export type MigrationWarningLevel = 'info' | 'warning';
|
|
13
|
+
export type MigrationWarningCode = 'rename-candidate' | 'manual-column-change' | 'manual-index-change' | 'manual-foreign-key-change' | 'manual-enum-change' | 'manual-constraint-change' | 'large-table-index-change';
|
|
14
|
+
export interface MigrationWarning {
|
|
15
|
+
code: MigrationWarningCode;
|
|
16
|
+
level: MigrationWarningLevel;
|
|
17
|
+
message: string;
|
|
18
|
+
table?: string;
|
|
19
|
+
column?: string;
|
|
20
|
+
sql?: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface RenameCandidate {
|
|
23
|
+
table: string;
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
sql: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface ForeignKeyDef {
|
|
30
|
+
name: string;
|
|
31
|
+
column: string;
|
|
32
|
+
refTable: string;
|
|
33
|
+
refColumn: string;
|
|
34
|
+
onDelete: 'cascade' | 'set null' | 'no action';
|
|
35
|
+
}
|
|
36
|
+
export interface TableStats {
|
|
37
|
+
rowCount?: number;
|
|
38
|
+
approximate?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface SchemaInspectionResult<ED extends MigrationEntityDict = MigrationEntityDict> {
|
|
41
|
+
schema: MigrationSchema<ED>;
|
|
42
|
+
tableStats: Record<string, TableStats>;
|
|
43
|
+
}
|
|
44
|
+
export interface MigrationPlanningOptions {
|
|
45
|
+
currentTableStats?: Record<string, TableStats>;
|
|
46
|
+
largeTableRowThreshold?: number;
|
|
47
|
+
compareForeignKeys?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface IndexPlanContext {
|
|
50
|
+
isNewTable: boolean;
|
|
51
|
+
tableStats?: TableStats;
|
|
52
|
+
largeTableRowThreshold: number;
|
|
53
|
+
}
|
|
54
|
+
export interface MigrationPlanSummary {
|
|
55
|
+
newTables: number;
|
|
56
|
+
removedTables: number;
|
|
57
|
+
addedColumns: number;
|
|
58
|
+
removedColumns: number;
|
|
59
|
+
changedColumns: number;
|
|
60
|
+
addedIndexes: number;
|
|
61
|
+
removedIndexes: number;
|
|
62
|
+
changedIndexes: number;
|
|
63
|
+
addedForeignKeys: number;
|
|
64
|
+
removedForeignKeys: number;
|
|
65
|
+
changedForeignKeys: number;
|
|
66
|
+
manualActions: number;
|
|
67
|
+
onlineActions: number;
|
|
68
|
+
largeTableIndexes: number;
|
|
69
|
+
}
|
|
70
|
+
export type MigrationSqlCategory = 'prepareSql' | 'forwardSql' | 'onlineSql' | 'manualSql' | 'backwardSql';
|
|
71
|
+
export interface MigrationSqlByCategory {
|
|
72
|
+
prepareSql: string[];
|
|
73
|
+
forwardSql: string[];
|
|
74
|
+
onlineSql: string[];
|
|
75
|
+
manualSql: string[];
|
|
76
|
+
backwardSql: string[];
|
|
77
|
+
}
|
|
78
|
+
export interface MigrationTableColumnAddition {
|
|
79
|
+
name: string;
|
|
80
|
+
categories: MigrationSqlCategory[];
|
|
81
|
+
reason?: string;
|
|
82
|
+
definition: AttributeSnapshot;
|
|
83
|
+
sql: MigrationSqlByCategory;
|
|
84
|
+
}
|
|
85
|
+
export interface MigrationTableColumnRemoval {
|
|
86
|
+
name: string;
|
|
87
|
+
categories: MigrationSqlCategory[];
|
|
88
|
+
definition: AttributeSnapshot;
|
|
89
|
+
sql: MigrationSqlByCategory;
|
|
90
|
+
}
|
|
91
|
+
export interface MigrationTableColumnChange {
|
|
92
|
+
name: string;
|
|
93
|
+
categories: MigrationSqlCategory[];
|
|
94
|
+
reason?: string;
|
|
95
|
+
before: AttributeSnapshot;
|
|
96
|
+
after: AttributeSnapshot;
|
|
97
|
+
sql: MigrationSqlByCategory;
|
|
98
|
+
}
|
|
99
|
+
export interface MigrationTableColumnRename {
|
|
100
|
+
from: string;
|
|
101
|
+
to: string;
|
|
102
|
+
categories: MigrationSqlCategory[];
|
|
103
|
+
reason: string;
|
|
104
|
+
before: AttributeSnapshot;
|
|
105
|
+
after: AttributeSnapshot;
|
|
106
|
+
sql: MigrationSqlByCategory;
|
|
107
|
+
}
|
|
108
|
+
export interface MigrationTableIndexAddition {
|
|
109
|
+
name: string;
|
|
110
|
+
categories: MigrationSqlCategory[];
|
|
111
|
+
definition: IndexSnapshot;
|
|
112
|
+
sql: MigrationSqlByCategory;
|
|
113
|
+
}
|
|
114
|
+
export interface MigrationTableIndexRemoval {
|
|
115
|
+
name: string;
|
|
116
|
+
categories: MigrationSqlCategory[];
|
|
117
|
+
definition: IndexSnapshot;
|
|
118
|
+
sql: MigrationSqlByCategory;
|
|
119
|
+
}
|
|
120
|
+
export interface MigrationTableIndexChange {
|
|
121
|
+
name: string;
|
|
122
|
+
categories: MigrationSqlCategory[];
|
|
123
|
+
before: IndexSnapshot;
|
|
124
|
+
after: IndexSnapshot;
|
|
125
|
+
sql: MigrationSqlByCategory;
|
|
126
|
+
}
|
|
127
|
+
export interface MigrationTableIndexRename {
|
|
128
|
+
from: string;
|
|
129
|
+
to: string;
|
|
130
|
+
categories: MigrationSqlCategory[];
|
|
131
|
+
before: IndexSnapshot;
|
|
132
|
+
after: IndexSnapshot;
|
|
133
|
+
sql: MigrationSqlByCategory;
|
|
134
|
+
}
|
|
135
|
+
export interface MigrationTableForeignKeyAddition {
|
|
136
|
+
name: string;
|
|
137
|
+
categories: MigrationSqlCategory[];
|
|
138
|
+
definition: ForeignKeyDef;
|
|
139
|
+
sql: MigrationSqlByCategory;
|
|
140
|
+
}
|
|
141
|
+
export interface MigrationTableForeignKeyRemoval {
|
|
142
|
+
name: string;
|
|
143
|
+
categories: MigrationSqlCategory[];
|
|
144
|
+
definition: ForeignKeyDef;
|
|
145
|
+
sql: MigrationSqlByCategory;
|
|
146
|
+
}
|
|
147
|
+
export interface MigrationTableForeignKeyChange {
|
|
148
|
+
name: string;
|
|
149
|
+
categories: MigrationSqlCategory[];
|
|
150
|
+
before?: ForeignKeyDef;
|
|
151
|
+
after?: ForeignKeyDef;
|
|
152
|
+
sql: MigrationSqlByCategory;
|
|
153
|
+
}
|
|
154
|
+
export interface MigrationTableChange {
|
|
155
|
+
entity: string;
|
|
156
|
+
table: string;
|
|
157
|
+
lifecycle: 'create' | 'alter' | 'drop';
|
|
158
|
+
summary: MigrationPlanSummary;
|
|
159
|
+
sql: MigrationSqlByCategory;
|
|
160
|
+
columns: {
|
|
161
|
+
added: MigrationTableColumnAddition[];
|
|
162
|
+
removed: MigrationTableColumnRemoval[];
|
|
163
|
+
changed: MigrationTableColumnChange[];
|
|
164
|
+
renamed: MigrationTableColumnRename[];
|
|
165
|
+
};
|
|
166
|
+
indexes: {
|
|
167
|
+
added: MigrationTableIndexAddition[];
|
|
168
|
+
removed: MigrationTableIndexRemoval[];
|
|
169
|
+
changed: MigrationTableIndexChange[];
|
|
170
|
+
renamed: MigrationTableIndexRename[];
|
|
171
|
+
};
|
|
172
|
+
foreignKeys: {
|
|
173
|
+
added: MigrationTableForeignKeyAddition[];
|
|
174
|
+
removed: MigrationTableForeignKeyRemoval[];
|
|
175
|
+
changed: MigrationTableForeignKeyChange[];
|
|
176
|
+
};
|
|
177
|
+
warnings: MigrationWarning[];
|
|
178
|
+
renameCandidates: RenameCandidate[];
|
|
179
|
+
}
|
|
180
|
+
export interface MigrationPlan<ED extends MigrationEntityDict = MigrationEntityDict> {
|
|
181
|
+
currentSchema: MigrationSchema<ED>;
|
|
182
|
+
targetSchema: MigrationSchema<ED>;
|
|
183
|
+
prepareSql: string[];
|
|
184
|
+
forwardSql: string[];
|
|
185
|
+
onlineSql: string[];
|
|
186
|
+
manualSql: string[];
|
|
187
|
+
backwardSql: string[];
|
|
188
|
+
warnings: MigrationWarning[];
|
|
189
|
+
renameCandidates: RenameCandidate[];
|
|
190
|
+
summary: MigrationPlanSummary;
|
|
191
|
+
tableChanges: MigrationTableChange[];
|
|
192
|
+
}
|
|
193
|
+
export type Plan<ED extends MigrationEntityDict = MigrationEntityDict> = MigrationPlan<ED>;
|
|
194
|
+
export interface MigrationChangePlan {
|
|
195
|
+
prepareSql?: string[];
|
|
196
|
+
forwardSql?: string[];
|
|
197
|
+
deferredForwardSql?: string[];
|
|
198
|
+
onlineSql?: string[];
|
|
199
|
+
manualSql?: string[];
|
|
200
|
+
backwardSql?: string[];
|
|
201
|
+
warnings?: MigrationWarning[];
|
|
202
|
+
renameCandidates?: RenameCandidate[];
|
|
203
|
+
summary?: Partial<MigrationPlanSummary>;
|
|
204
|
+
}
|
|
205
|
+
export interface AttributeSnapshot {
|
|
206
|
+
type: string;
|
|
207
|
+
params?: Record<string, number>;
|
|
208
|
+
enumeration?: string[];
|
|
209
|
+
ref?: string | string[];
|
|
210
|
+
onRefDelete?: 'delete' | 'setNull' | 'ignore';
|
|
211
|
+
default?: string | number | boolean;
|
|
212
|
+
notNull: boolean;
|
|
213
|
+
unique: boolean;
|
|
214
|
+
sequence: boolean;
|
|
215
|
+
}
|
|
216
|
+
export interface IndexSnapshot {
|
|
217
|
+
name: string;
|
|
218
|
+
attributes: Array<{
|
|
219
|
+
name: string;
|
|
220
|
+
direction?: 'ASC' | 'DESC';
|
|
221
|
+
size?: number;
|
|
222
|
+
}>;
|
|
223
|
+
config: {
|
|
224
|
+
unique: boolean;
|
|
225
|
+
type: 'btree' | 'hash' | 'spatial' | 'fulltext';
|
|
226
|
+
parser?: string;
|
|
227
|
+
tsConfig?: string | string[];
|
|
228
|
+
chineseParser?: string;
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
export interface AttributeChangeClassification {
|
|
232
|
+
mode: 'none' | 'forward' | 'manual';
|
|
233
|
+
reason?: string;
|
|
234
|
+
enumAppendOnly?: true;
|
|
235
|
+
}
|
|
236
|
+
export interface SchemaMigrationAdapter<ED extends MigrationEntityDict = MigrationEntityDict> {
|
|
237
|
+
readonly dialect: 'mysql' | 'postgresql';
|
|
238
|
+
normalizeIdentifier(name: string): string;
|
|
239
|
+
getTableName(table: string, tableDef?: {
|
|
240
|
+
storageName?: string;
|
|
241
|
+
}): string;
|
|
242
|
+
buildPrepareSql(currentSchema: MigrationSchema<ED>, targetSchema: MigrationSchema<ED>): string[];
|
|
243
|
+
buildNewTablePlan(table: string, tableDef: MigrationTableDef): MigrationChangePlan;
|
|
244
|
+
buildDropTablePlan(tableName: string): MigrationChangePlan;
|
|
245
|
+
buildRenamePlan(tableName: string, from: string, to: string, oldAttr: Attribute, newAttr: Attribute): MigrationChangePlan;
|
|
246
|
+
buildColumnPlan(tableName: string, column: string, oldAttr: Attribute | undefined, newAttr: Attribute | undefined, classification: AttributeChangeClassification): MigrationChangePlan;
|
|
247
|
+
buildIndexPlan(tableName: string, table: string, oldIndex: MigrationIndexWithOrigin | undefined, newIndex: MigrationIndexWithOrigin | undefined, context: IndexPlanContext): MigrationChangePlan;
|
|
248
|
+
buildRenameIndexPlan(tableName: string, table: string, oldIndex: MigrationIndexWithOrigin, newIndex: MigrationIndexWithOrigin, context: IndexPlanContext): MigrationChangePlan;
|
|
249
|
+
buildForeignKeyPlan(tableName: string, oldForeignKey: ForeignKeyDef | undefined, newForeignKey: ForeignKeyDef | undefined, isNewTable: boolean): MigrationChangePlan;
|
|
250
|
+
getForeignKeys(table: string, tableDef: MigrationTableDef, fullSchema: MigrationSchema<ED>): Record<string, ForeignKeyDef>;
|
|
251
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { MigrationIndexWithOrigin } from '../types/migration';
|
|
2
|
+
export declare function getIndexOriginNames(index: MigrationIndexWithOrigin): string[];
|
|
3
|
+
export declare function mergeIndexOriginName(index: MigrationIndexWithOrigin, originName: string): void;
|
|
4
|
+
export declare function areEquivalentInspectedIndexes(left: MigrationIndexWithOrigin, right: MigrationIndexWithOrigin): boolean;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getIndexOriginNames = getIndexOriginNames;
|
|
4
|
+
exports.mergeIndexOriginName = mergeIndexOriginName;
|
|
5
|
+
exports.areEquivalentInspectedIndexes = areEquivalentInspectedIndexes;
|
|
6
|
+
function getIndexOriginNames(index) {
|
|
7
|
+
return index.__originNames?.length
|
|
8
|
+
? [...index.__originNames]
|
|
9
|
+
: (index.__originName
|
|
10
|
+
? [index.__originName]
|
|
11
|
+
: [index.name]);
|
|
12
|
+
}
|
|
13
|
+
function mergeIndexOriginName(index, originName) {
|
|
14
|
+
const originNames = new Set();
|
|
15
|
+
if (index.__originName) {
|
|
16
|
+
originNames.add(index.__originName);
|
|
17
|
+
}
|
|
18
|
+
(index.__originNames || []).forEach((name) => originNames.add(name));
|
|
19
|
+
originNames.add(originName);
|
|
20
|
+
const merged = Array.from(originNames);
|
|
21
|
+
delete index.__originName;
|
|
22
|
+
index.__originNames = merged;
|
|
23
|
+
}
|
|
24
|
+
function areEquivalentInspectedIndexes(left, right) {
|
|
25
|
+
return JSON.stringify({
|
|
26
|
+
attributes: left.attributes,
|
|
27
|
+
config: left.config || {},
|
|
28
|
+
}) === JSON.stringify({
|
|
29
|
+
attributes: right.attributes,
|
|
30
|
+
config: right.config || {},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function buildEntityIndexPrefix(name: string): string;
|
|
2
|
+
export declare function shortenIdentifierWithHash(identifier: string, maxLength: number): string;
|
|
3
|
+
export interface CompactIndexNameOptions {
|
|
4
|
+
entityName: string;
|
|
5
|
+
tableName?: string;
|
|
6
|
+
logicalName: string;
|
|
7
|
+
suffix?: string;
|
|
8
|
+
maxLength: number;
|
|
9
|
+
}
|
|
10
|
+
export interface LegacyIndexNameOptions extends CompactIndexNameOptions {
|
|
11
|
+
serverTruncatesWhenOverflow?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function buildCompactPhysicalIndexName(options: CompactIndexNameOptions): string;
|
|
14
|
+
export declare function buildLegacyPhysicalIndexNames(options: LegacyIndexNameOptions): string[];
|
|
15
|
+
export declare function matchesPhysicalIndexName(actualName: string, options: LegacyIndexNameOptions): boolean;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildEntityIndexPrefix = buildEntityIndexPrefix;
|
|
4
|
+
exports.shortenIdentifierWithHash = shortenIdentifierWithHash;
|
|
5
|
+
exports.buildCompactPhysicalIndexName = buildCompactPhysicalIndexName;
|
|
6
|
+
exports.buildLegacyPhysicalIndexNames = buildLegacyPhysicalIndexNames;
|
|
7
|
+
exports.matchesPhysicalIndexName = matchesPhysicalIndexName;
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
function uniqueNonEmpty(values) {
|
|
10
|
+
return Array.from(new Set(values.filter(Boolean)));
|
|
11
|
+
}
|
|
12
|
+
function splitIdentifier(name) {
|
|
13
|
+
const normalized = name
|
|
14
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
15
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
16
|
+
.replace(/[_\-\s]+/g, ' ')
|
|
17
|
+
.trim();
|
|
18
|
+
return normalized ? normalized.split(/\s+/).filter(Boolean) : [];
|
|
19
|
+
}
|
|
20
|
+
function stripLogicalIndexPrefix(logicalName, prefixes) {
|
|
21
|
+
for (const prefix of uniqueNonEmpty(prefixes)) {
|
|
22
|
+
if (logicalName.startsWith(`${prefix}_`)) {
|
|
23
|
+
return logicalName.slice(prefix.length + 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return logicalName;
|
|
27
|
+
}
|
|
28
|
+
function buildEntityIndexPrefix(name) {
|
|
29
|
+
const parts = splitIdentifier(name);
|
|
30
|
+
if (parts.length === 0) {
|
|
31
|
+
return 'i';
|
|
32
|
+
}
|
|
33
|
+
const [first, ...rest] = parts;
|
|
34
|
+
return `${first.slice(0, Math.min(3, first.length)).toLowerCase()}${rest.map((part) => part[0].toUpperCase()).join('')}`;
|
|
35
|
+
}
|
|
36
|
+
function shortenIdentifierWithHash(identifier, maxLength) {
|
|
37
|
+
if (identifier.length <= maxLength) {
|
|
38
|
+
return identifier;
|
|
39
|
+
}
|
|
40
|
+
const hash = (0, crypto_1.createHash)('sha1')
|
|
41
|
+
.update(identifier)
|
|
42
|
+
.digest('hex')
|
|
43
|
+
.slice(0, 8);
|
|
44
|
+
const prefixLength = Math.max(1, maxLength - hash.length - 1);
|
|
45
|
+
return `${identifier.slice(0, prefixLength)}_${hash}`;
|
|
46
|
+
}
|
|
47
|
+
function buildCompactPhysicalIndexName(options) {
|
|
48
|
+
const { entityName, tableName, logicalName, suffix = '', maxLength, } = options;
|
|
49
|
+
const compactPrefix = buildEntityIndexPrefix(entityName || tableName || logicalName);
|
|
50
|
+
const baseName = stripLogicalIndexPrefix(logicalName, uniqueNonEmpty([entityName, tableName]));
|
|
51
|
+
return shortenIdentifierWithHash(`${compactPrefix}_${baseName}${suffix}`, maxLength);
|
|
52
|
+
}
|
|
53
|
+
function buildLegacyPhysicalIndexNames(options) {
|
|
54
|
+
const { entityName, tableName, logicalName, suffix = '', maxLength, serverTruncatesWhenOverflow, } = options;
|
|
55
|
+
const names = new Set();
|
|
56
|
+
const prefixedBases = uniqueNonEmpty([entityName, tableName]);
|
|
57
|
+
names.add(`${logicalName}${suffix}`);
|
|
58
|
+
prefixedBases.forEach((base) => {
|
|
59
|
+
names.add(`${base}_${logicalName}${suffix}`);
|
|
60
|
+
names.add(logicalName.startsWith(`${base}_`)
|
|
61
|
+
? `${logicalName}${suffix}`
|
|
62
|
+
: `${base}_${logicalName}${suffix}`);
|
|
63
|
+
});
|
|
64
|
+
names.add(buildCompactPhysicalIndexName(options));
|
|
65
|
+
const resolved = new Set();
|
|
66
|
+
names.forEach((name) => {
|
|
67
|
+
resolved.add(name);
|
|
68
|
+
if (serverTruncatesWhenOverflow && name.length > maxLength) {
|
|
69
|
+
resolved.add(name.slice(0, maxLength));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return Array.from(resolved);
|
|
73
|
+
}
|
|
74
|
+
function matchesPhysicalIndexName(actualName, options) {
|
|
75
|
+
return buildLegacyPhysicalIndexNames(options).includes(actualName);
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Attribute } from 'oak-domain/lib/types';
|
|
2
|
+
import { MigrationEntityDict, MigrationSchema, MigrationTableDef } from '../types/migration';
|
|
3
|
+
export interface InspectionTypeHintRule {
|
|
4
|
+
actualTypes: string[];
|
|
5
|
+
semanticTypes: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function buildTargetTableHintMap<ED extends MigrationEntityDict>(schema: MigrationSchema<ED>): Map<string, {
|
|
8
|
+
key: string;
|
|
9
|
+
tableDef: MigrationTableDef;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function applyInspectionAttributeHint(attr: Attribute, hint: Attribute | undefined, rules: InspectionTypeHintRule[]): Attribute;
|
|
12
|
+
export declare function applyOakManagedAttributeFallback(attr: Attribute, columnName: string, hint?: Attribute): Attribute;
|
|
13
|
+
export declare function isOakManagedTable(attributes: Record<string, Attribute>, hasTableHint: boolean): boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildTargetTableHintMap = buildTargetTableHintMap;
|
|
4
|
+
exports.applyInspectionAttributeHint = applyInspectionAttributeHint;
|
|
5
|
+
exports.applyOakManagedAttributeFallback = applyOakManagedAttributeFallback;
|
|
6
|
+
exports.isOakManagedTable = isOakManagedTable;
|
|
7
|
+
const oakManagedColumns = ['id', '$$createAt$$', '$$updateAt$$', '$$deleteAt$$'];
|
|
8
|
+
const oakDatetimeColumns = new Set(['$$createAt$$', '$$updateAt$$', '$$deleteAt$$']);
|
|
9
|
+
function normalizeTypeName(type) {
|
|
10
|
+
return String(type).toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
function buildTargetTableHintMap(schema) {
|
|
13
|
+
const tableHints = new Map();
|
|
14
|
+
Object.keys(schema).forEach((table) => {
|
|
15
|
+
const tableDef = schema[table];
|
|
16
|
+
tableHints.set(tableDef.storageName || table, {
|
|
17
|
+
key: table,
|
|
18
|
+
tableDef,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
return tableHints;
|
|
22
|
+
}
|
|
23
|
+
function applyInspectionAttributeHint(attr, hint, rules) {
|
|
24
|
+
if (!hint) {
|
|
25
|
+
return attr;
|
|
26
|
+
}
|
|
27
|
+
const actualType = normalizeTypeName(attr.type);
|
|
28
|
+
const semanticType = normalizeTypeName(hint.type);
|
|
29
|
+
const matchedRule = rules.find((rule) => rule.actualTypes.includes(actualType)
|
|
30
|
+
&& rule.semanticTypes.includes(semanticType));
|
|
31
|
+
if (!matchedRule) {
|
|
32
|
+
return attr;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
...attr,
|
|
36
|
+
type: hint.type,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function applyOakManagedAttributeFallback(attr, columnName, hint) {
|
|
40
|
+
if (hint) {
|
|
41
|
+
return attr;
|
|
42
|
+
}
|
|
43
|
+
if (attr.type === 'bigint' && oakDatetimeColumns.has(columnName)) {
|
|
44
|
+
return {
|
|
45
|
+
...attr,
|
|
46
|
+
type: 'datetime',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return attr;
|
|
50
|
+
}
|
|
51
|
+
function isOakManagedTable(attributes, hasTableHint) {
|
|
52
|
+
if (hasTableHint) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return oakManagedColumns.every((column) => !!attributes[column]);
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oak-db",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "oak-db",
|
|
5
|
+
"oak": {
|
|
6
|
+
"package": true
|
|
7
|
+
},
|
|
5
8
|
"main": "lib/index",
|
|
6
9
|
"author": {
|
|
7
10
|
"name": "XuChang"
|
|
@@ -18,7 +21,7 @@
|
|
|
18
21
|
"lodash": "^4.17.21",
|
|
19
22
|
"mysql": "^2.18.1",
|
|
20
23
|
"mysql2": "^2.3.3",
|
|
21
|
-
"oak-domain": "
|
|
24
|
+
"oak-domain": "file:../oak-domain",
|
|
22
25
|
"pg": "^8.16.3",
|
|
23
26
|
"uuid": "^8.3.2"
|
|
24
27
|
},
|