oak-db 3.3.13 → 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 +33 -14
- package/lib/MySQL/migration.d.ts +10 -0
- package/lib/MySQL/migration.js +649 -0
- package/lib/MySQL/store.d.ts +19 -2
- package/lib/MySQL/store.js +159 -110
- package/lib/MySQL/translator.d.ts +5 -1
- package/lib/MySQL/translator.js +47 -14
- package/lib/PostgreSQL/connector.d.ts +10 -0
- package/lib/PostgreSQL/connector.js +58 -51
- 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 +16 -2
- package/lib/PostgreSQL/store.js +196 -163
- package/lib/PostgreSQL/translator.d.ts +28 -8
- package/lib/PostgreSQL/translator.js +208 -226
- package/lib/index.d.ts +1 -0
- package/lib/migration.d.ts +27 -0
- package/lib/migration.js +1029 -0
- package/lib/sqlTranslator.d.ts +5 -1
- package/lib/sqlTranslator.js +12 -4
- package/lib/types/dbStore.d.ts +8 -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/sqlTranslator.d.ts
CHANGED
|
@@ -53,7 +53,11 @@ export declare abstract class SqlTranslator<ED extends EntityDict & BaseEntityDi
|
|
|
53
53
|
};
|
|
54
54
|
private translateSorter;
|
|
55
55
|
private translateProjection;
|
|
56
|
-
|
|
56
|
+
protected translateSelectInner<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], initialNumber: number, refAlias: Record<string, [string, keyof ED]>, option?: OP): {
|
|
57
|
+
filterStmt: string;
|
|
58
|
+
stmt: string;
|
|
59
|
+
currentNumber: number;
|
|
60
|
+
};
|
|
57
61
|
translateSelect<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], option?: OP): string;
|
|
58
62
|
translateWhere<T extends keyof ED, OP extends SqlSelectOption>(entity: T, selection: ED[T]['Selection'], option?: OP): string;
|
|
59
63
|
translateAggregate<T extends keyof ED, OP extends SqlSelectOption>(entity: T, aggregation: ED[T]['Aggregation'], option?: OP): string;
|
package/lib/sqlTranslator.js
CHANGED
|
@@ -564,10 +564,10 @@ class SqlTranslator {
|
|
|
564
564
|
translateFilter(entity, filter, aliasDict, filterRefAlias, initialNumber, option) {
|
|
565
565
|
const { schema } = this;
|
|
566
566
|
let currentNumber = initialNumber;
|
|
567
|
-
const translateInner = (entity2, path, filter2,
|
|
567
|
+
const translateInner = (entity2, path, filter2, skipDeletePhy) => {
|
|
568
568
|
const alias = aliasDict[path];
|
|
569
569
|
const { attributes } = schema[entity2];
|
|
570
|
-
let whereText =
|
|
570
|
+
let whereText = skipDeletePhy ? '' : this.getDefaultSelectFilter(alias, option);
|
|
571
571
|
if (filter2) {
|
|
572
572
|
const attrs = Object.keys(filter2).filter(ele => !ele.startsWith('#'));
|
|
573
573
|
attrs.forEach((attr) => {
|
|
@@ -582,7 +582,7 @@ class SqlTranslator {
|
|
|
582
582
|
case '$xor': {
|
|
583
583
|
const logicQueries = filter2[attr];
|
|
584
584
|
logicQueries.forEach((logicQuery, index) => {
|
|
585
|
-
const sql = translateInner(entity2, path, logicQuery,
|
|
585
|
+
const sql = translateInner(entity2, path, logicQuery, true); // 只要传个值就行了,应该无所谓
|
|
586
586
|
if (sql) {
|
|
587
587
|
whereText += ` (${sql})`;
|
|
588
588
|
if (index < logicQueries.length - 1) {
|
|
@@ -595,7 +595,7 @@ class SqlTranslator {
|
|
|
595
595
|
default: {
|
|
596
596
|
(0, assert_1.default)(attr === '$not');
|
|
597
597
|
const logicQuery = filter2[attr];
|
|
598
|
-
const sql = translateInner(entity2, path, logicQuery,
|
|
598
|
+
const sql = translateInner(entity2, path, logicQuery, true); // 只要传个值就行了,应该无所谓
|
|
599
599
|
if (sql) {
|
|
600
600
|
whereText += ` not (${sql})`;
|
|
601
601
|
break;
|
|
@@ -965,6 +965,10 @@ class SqlTranslator {
|
|
|
965
965
|
const { data, filter, sorter, indexFrom, count } = operation;
|
|
966
966
|
(0, assert_1.default)(!sorter, '当前remove不支持sorter行为');
|
|
967
967
|
const { aliasDict, filterRefAlias, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter });
|
|
968
|
+
// 对于limit,如果有indexForm则一定有count
|
|
969
|
+
if (typeof indexFrom === 'number') {
|
|
970
|
+
(0, assert_1.default)(typeof count === 'number' && count > 0);
|
|
971
|
+
}
|
|
968
972
|
const alias = aliasDict['./'];
|
|
969
973
|
// 这里原来includeDeleted传的是true,不知道原因,但不合理
|
|
970
974
|
const { stmt: filterText } = this.translateFilter(entity, filter, aliasDict, filterRefAlias, currentNumber, { includedDeleted: option?.includedDeleted });
|
|
@@ -992,6 +996,10 @@ class SqlTranslator {
|
|
|
992
996
|
const { filter, sorter, indexFrom, count, data } = operation;
|
|
993
997
|
(0, assert_1.default)(!sorter, '当前update不支持sorter行为');
|
|
994
998
|
const { aliasDict, filterRefAlias, from: fromText, currentNumber } = this.analyzeJoin(entity, { filter, sorter });
|
|
999
|
+
// 对于limit,如果有indexForm则一定有count
|
|
1000
|
+
if (typeof indexFrom === 'number') {
|
|
1001
|
+
(0, assert_1.default)(typeof count === 'number' && count > 0);
|
|
1002
|
+
}
|
|
995
1003
|
const alias = aliasDict['./'];
|
|
996
1004
|
let updateText = '';
|
|
997
1005
|
for (const attr in data) {
|
package/lib/types/dbStore.d.ts
CHANGED
|
@@ -1,23 +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> {
|
|
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>;
|
|
19
10
|
connect: () => Promise<void>;
|
|
20
11
|
disconnect: () => Promise<void>;
|
|
12
|
+
exec(script: string, txnId?: string): Promise<void>;
|
|
13
|
+
supportsTransactionalDdl(): boolean;
|
|
21
14
|
initialize(options: CreateEntityOption): Promise<void>;
|
|
22
15
|
aggregate<T extends keyof ED, OP extends SelectOption>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
|
|
23
16
|
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OperateOption): Promise<OperationResult<ED>>;
|
|
@@ -27,6 +20,6 @@ export interface DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends Asy
|
|
|
27
20
|
commit(txnId: string): Promise<void>;
|
|
28
21
|
rollback(txnId: string): Promise<void>;
|
|
29
22
|
readSchema(): Promise<StorageSchema<ED>>;
|
|
30
|
-
makeUpgradePlan(): Promise<Plan>;
|
|
31
|
-
diffSchema(schemaOld: StorageSchema<
|
|
23
|
+
makeUpgradePlan(options?: MigrationPlanningOptions): Promise<Plan>;
|
|
24
|
+
diffSchema(schemaOld: StorageSchema<ED>, schemaNew: StorageSchema<ED>, options?: MigrationPlanningOptions): Plan;
|
|
32
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
|
},
|