gg-mysql-connector 1.0.35 → 1.0.37
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/dist/GGMySQLConnector/GGMySQLConnector.d.ts +72 -0
- package/dist/GGMySQLConnector/GGMySQLConnector.js +296 -0
- package/dist/ModelGenerator/ModelGenerator.d.ts +38 -0
- package/dist/ModelGenerator/ModelGenerator.js +299 -0
- package/dist/ModelGenerator/SeedGenerator.d.ts +1 -0
- package/dist/ModelGenerator/SeedGenerator.js +3 -0
- package/dist/ModelGenerator.js +1 -9
- package/dist/MyDBMigrator/MyDBMigrator.d.ts +22 -0
- package/dist/MyDBMigrator/MyDBMigrator.js +157 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/myModel.d.ts +2 -3
- package/package.json +1 -1
- package/src/{GGMySQLConnector.ts → GGMySQLConnector/GGMySQLConnector.ts} +1 -1
- package/src/{ModelGenerator.ts → ModelGenerator/ModelGenerator.ts} +72 -56
- package/src/ModelGenerator/SeedGenerator.ts +3 -0
- package/src/{MyDBMigration.ts → MyDBMigrator/MyDBMigrator.ts} +3 -3
- package/src/index.ts +3 -3
- package/src/myModel.ts +2 -4
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import mysql, { QueryResult, RowDataPacket } from "mysql2";
|
|
2
|
+
import { DatabaseConfigInterface } from "../ModelGenerator/ModelGenerator";
|
|
3
|
+
export interface ClassDBInterface<Main> {
|
|
4
|
+
connection: mysql.Connection;
|
|
5
|
+
query<T>(statement: string, parameter?: object, isPrint?: boolean): Promise<T | QueryResult>;
|
|
6
|
+
getColumnList(tableName: string): Promise<string[]>;
|
|
7
|
+
select<T extends keyof Main>(tableName: T extends string ? T : string): Promise<Main[T][]>;
|
|
8
|
+
selectByID<T extends keyof Main>(tableName: T extends string ? T : string, id: number): Promise<Main[T][]>;
|
|
9
|
+
selectByMatchParams<T extends keyof Main>(tableName: T extends string ? T : string, params: object): Promise<Main[T][]>;
|
|
10
|
+
selectBySearchTextInRow(tableName: string, textToSearch: string): Promise<RowDataPacket[]>;
|
|
11
|
+
insert<T extends keyof Main>(tableName: T, params: any): Promise<mysql.OkPacket>;
|
|
12
|
+
update(tableName: string, parameter: object | object[]): Promise<mysql.OkPacket>;
|
|
13
|
+
updateOnlyID<T extends keyof Main extends string ? keyof Main : string>(tableName: T, params: {
|
|
14
|
+
oldID: number;
|
|
15
|
+
newID: number;
|
|
16
|
+
}): Promise<mysql.OkPacket | null>;
|
|
17
|
+
deleteByID(tableName: string, id: number): Promise<mysql.OkPacket>;
|
|
18
|
+
deleteByMatchParams(tableName: string, parameter: any): Promise<mysql.OkPacket | null>;
|
|
19
|
+
getTableNameList(): Promise<string[]>;
|
|
20
|
+
getViewNameList(): Promise<string[]>;
|
|
21
|
+
getViewNameIfExist(tableName: string): Promise<string>;
|
|
22
|
+
loadTableAndViewNameList(): Promise<string[]>;
|
|
23
|
+
createDatabaseIfNotExist(tempConnection: mysql.Connection): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export type Unarray<T> = T extends Array<infer U> ? U : T;
|
|
26
|
+
export default class GGMySQLConnector<Main> implements ClassDBInterface<Main> {
|
|
27
|
+
DBInfo: DatabaseConfigInterface;
|
|
28
|
+
connection: mysql.Connection;
|
|
29
|
+
isPrintStatement: boolean;
|
|
30
|
+
tableAndViewNameList: string[];
|
|
31
|
+
columnAndTableListCache: {
|
|
32
|
+
COLUMN_NAME: string;
|
|
33
|
+
TABLE_NAME: string;
|
|
34
|
+
DATA_TYPE: string;
|
|
35
|
+
}[] | null;
|
|
36
|
+
isConnected: boolean;
|
|
37
|
+
constructor(DBInfo: DatabaseConfigInterface);
|
|
38
|
+
createDatabaseIfNotExist(tempConnection: mysql.Connection): Promise<void>;
|
|
39
|
+
selectBySearchTextInRow(tableName: string, textToSearch: string): Promise<mysql.RowDataPacket[]>;
|
|
40
|
+
loadTableAndViewNameList(): Promise<string[]>;
|
|
41
|
+
getViewNameIfExist(tableName: string): Promise<string>;
|
|
42
|
+
selectByMatchParams<T extends keyof Main>(tableName: T extends string ? T : string, params: Partial<{
|
|
43
|
+
[K in keyof Main[T]]: string | number | undefined | null;
|
|
44
|
+
}>): Promise<Main[T][]>;
|
|
45
|
+
selectByID<T extends keyof Main>(tableName: T extends string ? T : string, id: number): Promise<Main[T][]>;
|
|
46
|
+
select<T extends keyof Main>(tableName: T extends string ? T : string): Promise<Main[T][]>;
|
|
47
|
+
getTableNameList(): Promise<string[]>;
|
|
48
|
+
getViewNameList(): Promise<string[]>;
|
|
49
|
+
deleteByMatchParams<T extends keyof Main>(tableName: T extends string ? T : string, params: Partial<{
|
|
50
|
+
[K in keyof Main[T]]: T | string | number | undefined | null;
|
|
51
|
+
}>): Promise<mysql.OkPacket | null>;
|
|
52
|
+
deleteByID<T extends keyof Main>(tableName: T extends string ? T : string, id: number): Promise<mysql.OkPacket>;
|
|
53
|
+
insert<T extends keyof Main>(tableName: T, parameter: Partial<{
|
|
54
|
+
[R in keyof Unarray<Main[T]>]: any;
|
|
55
|
+
}> | Partial<{
|
|
56
|
+
[R in keyof Unarray<Main[T]>]: any;
|
|
57
|
+
}>[]): Promise<mysql.OkPacket>;
|
|
58
|
+
updateOnlyID<T extends keyof Main extends string ? keyof Main : string>(tableName: T, params: {
|
|
59
|
+
oldID: number;
|
|
60
|
+
newID: number;
|
|
61
|
+
}): Promise<mysql.OkPacket | null>;
|
|
62
|
+
update<T extends keyof Main>(tableName: T extends string ? T : string, parameter: Partial<{
|
|
63
|
+
[K in keyof Main[T]]: Main[T][K];
|
|
64
|
+
}>): Promise<mysql.OkPacket>;
|
|
65
|
+
init(): Promise<void>;
|
|
66
|
+
loadColumnListToCache(): Promise<void>;
|
|
67
|
+
getColumnList(tableName: string): Promise<string[]>;
|
|
68
|
+
getColumnFullList(tableName: string): Promise<mysql.RowDataPacket[]>;
|
|
69
|
+
waitUntilInitSuccess(interval: number): true | Promise<unknown>;
|
|
70
|
+
query<T>(statment: string, parameter?: any[], isPrint?: boolean): Promise<T | QueryResult>;
|
|
71
|
+
private printResultLength;
|
|
72
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const mysql2_1 = __importDefault(require("mysql2"));
|
|
9
|
+
class GGMySQLConnector {
|
|
10
|
+
constructor(DBInfo) {
|
|
11
|
+
this.printResultLength = (results, executionTime, queryResult) => {
|
|
12
|
+
if (results.length) {
|
|
13
|
+
const executionTimeMessage = executionTime > 1000
|
|
14
|
+
? chalk_1.default.bgRed(`${executionTime} ` + " ms")
|
|
15
|
+
: chalk_1.default.yellow(`${executionTime} ` + " ms");
|
|
16
|
+
console.log(`${this.DBInfo.database} > ${chalk_1.default.green(queryResult.sql)} ${chalk_1.default.cyan(`found ${results.length} rows.`)} ${executionTimeMessage}`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
this.isPrintStatement = false;
|
|
20
|
+
this.tableAndViewNameList = [];
|
|
21
|
+
this.columnAndTableListCache = null;
|
|
22
|
+
this.DBInfo = DBInfo;
|
|
23
|
+
this.isConnected = false;
|
|
24
|
+
}
|
|
25
|
+
async createDatabaseIfNotExist(tempConnection) {
|
|
26
|
+
const currentDatabaseName = this.DBInfo.database;
|
|
27
|
+
console.log("currentDatabaseName", currentDatabaseName);
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
tempConnection.query(`CREATE DATABASE IF NOT EXISTS ${currentDatabaseName}`, () => {
|
|
30
|
+
resolve();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async selectBySearchTextInRow(tableName, textToSearch) {
|
|
35
|
+
const result = (await this.query(`SELECT * FROM ${tableName}`));
|
|
36
|
+
const wordList = textToSearch.split(" ");
|
|
37
|
+
const temp = result.filter((row) => {
|
|
38
|
+
const rawText = Object.values(row).flat().join(" ");
|
|
39
|
+
const wordFilterResult = wordList.filter((wRow) => rawText.search(wRow) >= 0);
|
|
40
|
+
if (wordFilterResult.length === wordList.length)
|
|
41
|
+
return true;
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
return temp;
|
|
45
|
+
}
|
|
46
|
+
async loadTableAndViewNameList() {
|
|
47
|
+
let result = (await this.query("SELECT TABLE_NAME FROM information_schema.tables WHERE table_schema = ?", [this.DBInfo.database], false));
|
|
48
|
+
this.tableAndViewNameList = result.map((row) => row.TABLE_NAME);
|
|
49
|
+
return this.tableAndViewNameList;
|
|
50
|
+
}
|
|
51
|
+
async getViewNameIfExist(tableName) {
|
|
52
|
+
const tableList = this.tableAndViewNameList;
|
|
53
|
+
const viewPostFixArray = ["_view", "__view", "__view_list"];
|
|
54
|
+
for (let i = 0; i < viewPostFixArray.length; i++) {
|
|
55
|
+
const expectViewName = tableName + viewPostFixArray[i];
|
|
56
|
+
for (let j = 0; j < tableList.length; j++) {
|
|
57
|
+
const row = tableList[j];
|
|
58
|
+
if (row === expectViewName) {
|
|
59
|
+
return expectViewName;
|
|
60
|
+
}
|
|
61
|
+
else if (i == viewPostFixArray.length - 1 &&
|
|
62
|
+
j == tableList.length - 1) {
|
|
63
|
+
return tableName;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return tableName;
|
|
68
|
+
}
|
|
69
|
+
async selectByMatchParams(tableName, params) {
|
|
70
|
+
const columnList = await this.getColumnList(tableName);
|
|
71
|
+
const keyList = Object.keys(params);
|
|
72
|
+
const keyToSearchList = [];
|
|
73
|
+
const valueToSearchList = [];
|
|
74
|
+
for (const key of keyList) {
|
|
75
|
+
const temp = columnList.find((fkey) => key === fkey);
|
|
76
|
+
if (temp) {
|
|
77
|
+
keyToSearchList.push(` ${key} = ? `);
|
|
78
|
+
valueToSearchList.push(params[key]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (keyToSearchList.length === 0) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
let result = (await this.query(`SELECT * FROM ${tableName} WHERE ${keyToSearchList.join(" AND ")}`, valueToSearchList));
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
async selectByID(tableName, id) {
|
|
88
|
+
let result = (await this.query(`SELECT * FROM ${tableName} WHERE id = ? LIMIT 1`, [id]));
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
async select(tableName) {
|
|
92
|
+
let result = (await this.query(`SELECT * FROM ${tableName}`));
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
async getTableNameList() {
|
|
96
|
+
let data = [];
|
|
97
|
+
let result = (await this.query("SELECT * FROM information_schema.tables WHERE table_schema = ? AND TABLE_TYPE = 'BASE TABLE'", [this.DBInfo.database], false));
|
|
98
|
+
for (let row of result)
|
|
99
|
+
data.push(row.TABLE_NAME || row.row.table_name);
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
102
|
+
async getViewNameList() {
|
|
103
|
+
let data = [];
|
|
104
|
+
let result = (await this.query("SELECT * FROM information_schema.tables WHERE table_schema = ? AND TABLE_TYPE = 'VIEW'", [this.DBInfo.database], false));
|
|
105
|
+
for (let row of result)
|
|
106
|
+
data.push(row.TABLE_NAME || row.row.table_name);
|
|
107
|
+
return data;
|
|
108
|
+
}
|
|
109
|
+
async deleteByMatchParams(tableName, params) {
|
|
110
|
+
const columnList = await this.getColumnList(tableName);
|
|
111
|
+
const keyList = Object.keys(params);
|
|
112
|
+
const keyToSearchList = [];
|
|
113
|
+
const valueToSearchList = [];
|
|
114
|
+
for (const key of keyList) {
|
|
115
|
+
const temp = columnList.find((fkey) => key === fkey);
|
|
116
|
+
if (temp) {
|
|
117
|
+
keyToSearchList.push(` ${key} = ? `);
|
|
118
|
+
valueToSearchList.push(params[key]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (keyToSearchList.length === 0)
|
|
122
|
+
return null;
|
|
123
|
+
let result = (await this.query(`DELETE FROM ${tableName} WHERE ${keyToSearchList.join(" AND ")}`, valueToSearchList));
|
|
124
|
+
// this.webSocket.sendByURL("/websocket/watchTableChange", tableName)
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
async deleteByID(tableName, id) {
|
|
128
|
+
let result = (await this.query(`DELETE FROM ${tableName} WHERE id = ?`, [
|
|
129
|
+
id,
|
|
130
|
+
]));
|
|
131
|
+
console.log("Delete Success");
|
|
132
|
+
// this.webSocket.sendByURL("/websocket/watchTableChange", tableName)
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
async insert(tableName, parameter) {
|
|
136
|
+
let params = [];
|
|
137
|
+
if (Array.isArray(parameter) === true) {
|
|
138
|
+
params = parameter;
|
|
139
|
+
}
|
|
140
|
+
else
|
|
141
|
+
params = [parameter];
|
|
142
|
+
const columnList = await this.getColumnList(tableName);
|
|
143
|
+
const exampleParams = params[0];
|
|
144
|
+
const keyList = Object.keys(exampleParams);
|
|
145
|
+
const keyListToInsert = [];
|
|
146
|
+
for (const row of keyList) {
|
|
147
|
+
const temp = columnList.find((fRow) => row === fRow);
|
|
148
|
+
if (temp)
|
|
149
|
+
keyListToInsert.push(row);
|
|
150
|
+
}
|
|
151
|
+
const valueArrayToInsertInQuery = [];
|
|
152
|
+
for (let i = 0; i < params.length; i++) {
|
|
153
|
+
const valueListToInsert = [];
|
|
154
|
+
for (const row of keyList) {
|
|
155
|
+
const temp = columnList.find((fRow) => row === fRow);
|
|
156
|
+
if (temp) {
|
|
157
|
+
valueListToInsert.push(params[i][row]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
valueArrayToInsertInQuery.push(valueListToInsert);
|
|
161
|
+
}
|
|
162
|
+
console.log(valueArrayToInsertInQuery.slice(0, 5));
|
|
163
|
+
const result = (await this.query(`INSERT INTO ${tableName} (${keyListToInsert.join(",")}) VALUES ?`, [valueArrayToInsertInQuery]));
|
|
164
|
+
console.log("Insert Success");
|
|
165
|
+
// this.webSocket.sendByURL("/websocket/watchTableChange", String(tableName))
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
async updateOnlyID(tableName, params) {
|
|
169
|
+
const isNewIdExist = await this.query(`SELECT * FROM ${tableName} WHERE id = ? `, [params.newID]);
|
|
170
|
+
if (isNewIdExist.length >= 1) {
|
|
171
|
+
console.log(`error : newID (${params.newID}) is already exist`);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
let result = (await this.query(`UPDATE ${tableName} SET id = ? WHERE id = ?`, [params.newID, params.oldID], false));
|
|
175
|
+
console.log("UpdateOnlyID Success");
|
|
176
|
+
// this.webSocket.sendByURL("/websocket/watchTableChange", String(tableName))
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
async update(tableName, parameter) {
|
|
180
|
+
const columnList = await this.getColumnList(tableName);
|
|
181
|
+
console.log(parameter);
|
|
182
|
+
const keyList = Object.keys(parameter);
|
|
183
|
+
const keyListToInsert = [];
|
|
184
|
+
const valueListToInsert = [];
|
|
185
|
+
for (const row of keyList) {
|
|
186
|
+
const temp = columnList.find((fRow) => row === fRow);
|
|
187
|
+
if (temp) {
|
|
188
|
+
keyListToInsert.push(row);
|
|
189
|
+
valueListToInsert.push(parameter[row]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// build set sql
|
|
193
|
+
const temp = [];
|
|
194
|
+
let index = 0;
|
|
195
|
+
for (const row of keyListToInsert) {
|
|
196
|
+
if (valueListToInsert[index] === null) {
|
|
197
|
+
temp.push(`${row} = NULL`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
let value = valueListToInsert[index];
|
|
201
|
+
if (typeof value === "string") {
|
|
202
|
+
value = value.replace(/'/g, "");
|
|
203
|
+
}
|
|
204
|
+
temp.push(`${row} = '${value}'`);
|
|
205
|
+
}
|
|
206
|
+
index++;
|
|
207
|
+
}
|
|
208
|
+
let result = (await this.query(`UPDATE ${tableName} SET ${temp.join(",")} WHERE id = ${parameter.id}`, [], false));
|
|
209
|
+
console.log("Update Success");
|
|
210
|
+
// this.webSocket.sendByURL("/websocket/watchTableChange", String(tableName))
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
async init() {
|
|
214
|
+
const currentConnection = mysql2_1.default.createConnection({
|
|
215
|
+
host: this.DBInfo.host,
|
|
216
|
+
user: this.DBInfo.user,
|
|
217
|
+
password: this.DBInfo.password,
|
|
218
|
+
});
|
|
219
|
+
this.isConnected = true;
|
|
220
|
+
await this.createDatabaseIfNotExist(currentConnection);
|
|
221
|
+
// await currentConnection.end()
|
|
222
|
+
this.connection = mysql2_1.default.createConnection(Object.assign({}, this.DBInfo));
|
|
223
|
+
// await this.query("SET global sql_mode=''")
|
|
224
|
+
// await this.query("SET global query_cache_type='ON'")
|
|
225
|
+
// await this.query("SET global query_cache_size=16777216")
|
|
226
|
+
// await this.query("SET global query_cache_limit=16777216")
|
|
227
|
+
await this.loadTableAndViewNameList();
|
|
228
|
+
await this.loadColumnListToCache();
|
|
229
|
+
}
|
|
230
|
+
async loadColumnListToCache() {
|
|
231
|
+
const result = (await this.query(`SELECT COLUMN_NAME,TABLE_NAME,DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.DBInfo.database}'`, undefined, false));
|
|
232
|
+
this.columnAndTableListCache = result;
|
|
233
|
+
}
|
|
234
|
+
async getColumnList(tableName) {
|
|
235
|
+
if (this.columnAndTableListCache !== null) {
|
|
236
|
+
return this.columnAndTableListCache
|
|
237
|
+
.filter((row) => row.TABLE_NAME === tableName)
|
|
238
|
+
.map((row) => row.COLUMN_NAME);
|
|
239
|
+
}
|
|
240
|
+
const result = await this.query(`SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.DBInfo.database}' AND TABLE_NAME = '${tableName}'`, undefined, false);
|
|
241
|
+
return result.map((row) => row.COLUMN_NAME);
|
|
242
|
+
}
|
|
243
|
+
async getColumnFullList(tableName) {
|
|
244
|
+
const result = (await this.query(`SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.DBInfo.database}' AND TABLE_NAME = '${tableName}'`, undefined, false));
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
waitUntilInitSuccess(interval) {
|
|
248
|
+
if (this.isConnected === true)
|
|
249
|
+
return true;
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
setTimeout(() => {
|
|
252
|
+
if (this.isConnected === false) {
|
|
253
|
+
console.log(`${this.DBInfo.database} > wait for database connection . . .`);
|
|
254
|
+
this.waitUntilInitSuccess(interval);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
return resolve(true);
|
|
258
|
+
}
|
|
259
|
+
}, interval);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
async query(statment, parameter, isPrint) {
|
|
263
|
+
await this.waitUntilInitSuccess(1000);
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const uniqueTime = `${(0, crypto_1.randomUUID)()} ${statment}`;
|
|
266
|
+
console.time(uniqueTime);
|
|
267
|
+
const startTime = new Date();
|
|
268
|
+
const queryResult = this.connection.query(statment, parameter, (error, results, fields) => {
|
|
269
|
+
if (error) {
|
|
270
|
+
// _getCallerFile()
|
|
271
|
+
console.log(chalk_1.default.bgRed("Query Error"));
|
|
272
|
+
console.log(chalk_1.default.bgRed(error));
|
|
273
|
+
console.log(chalk_1.default.bgRed("Statement : "));
|
|
274
|
+
console.log(statment);
|
|
275
|
+
console.log(chalk_1.default.bgRed("Parameter : "));
|
|
276
|
+
console.log(parameter);
|
|
277
|
+
console.log("------------------------------------------------");
|
|
278
|
+
console.log(chalk_1.default.bgRed(queryResult.sql));
|
|
279
|
+
reject(error);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
const endTime = new Date();
|
|
283
|
+
const executionTime = endTime.getTime() - startTime.getTime();
|
|
284
|
+
if (isPrint === true) {
|
|
285
|
+
this.printResultLength(results, executionTime, queryResult);
|
|
286
|
+
}
|
|
287
|
+
else if (this.isPrintStatement) {
|
|
288
|
+
this.printResultLength(results, executionTime, queryResult);
|
|
289
|
+
}
|
|
290
|
+
resolve(results);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
exports.default = GGMySQLConnector;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import mysql from "mysql2/promise";
|
|
2
|
+
import { MyModel } from "../myModel";
|
|
3
|
+
export interface DatabaseConfigInterface {
|
|
4
|
+
host: string;
|
|
5
|
+
user: string;
|
|
6
|
+
password: string;
|
|
7
|
+
database: string;
|
|
8
|
+
}
|
|
9
|
+
export default class ModelGenerator {
|
|
10
|
+
model: MyModel[];
|
|
11
|
+
dbInfo: DatabaseConfigInterface;
|
|
12
|
+
connection: mysql.Connection;
|
|
13
|
+
isConnected: boolean;
|
|
14
|
+
constructor(dbInfo: DatabaseConfigInterface, model: MyModel[]);
|
|
15
|
+
init(): Promise<void>;
|
|
16
|
+
private createDatabaseIfNotExist;
|
|
17
|
+
pushModelToDB(): Promise<void>;
|
|
18
|
+
pushViewToDB(viewDirectory: string): Promise<void>;
|
|
19
|
+
waitUntilInitSuccess(interval: number): true | Promise<unknown>;
|
|
20
|
+
query<T>(statment: string, parameter?: any[], isPrint?: boolean): Promise<T>;
|
|
21
|
+
private printResultLength;
|
|
22
|
+
getTableNameList(): Promise<string[]>;
|
|
23
|
+
getViewNameList(): Promise<string[]>;
|
|
24
|
+
getColumnFullList(tableName: string): Promise<mysql.RowDataPacket[]>;
|
|
25
|
+
private isTableNameExistInModel;
|
|
26
|
+
private isColumnExistInModel;
|
|
27
|
+
getPossibleColumnValue(model: MyModel[], tableName: string, columnName: string): false | any[] | undefined;
|
|
28
|
+
generateModelInterface(params: {
|
|
29
|
+
appName: string;
|
|
30
|
+
model: MyModel[];
|
|
31
|
+
outputDirectory: string[];
|
|
32
|
+
}): Promise<string>;
|
|
33
|
+
generateModelConstStructure(params: {
|
|
34
|
+
appName: string;
|
|
35
|
+
model: MyModel[];
|
|
36
|
+
outputDirectory: string[];
|
|
37
|
+
}): Promise<string>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const MyDBMigrator_1 = __importDefault(require("../MyDBMigrator/MyDBMigrator"));
|
|
12
|
+
class ModelGenerator {
|
|
13
|
+
constructor(dbInfo, model) {
|
|
14
|
+
this.printResultLength = (results, executionTime, queryResult) => {
|
|
15
|
+
if (results.length) {
|
|
16
|
+
const executionTimeMessage = executionTime > 1000
|
|
17
|
+
? chalk_1.default.bgRed(`${executionTime} ` + " ms")
|
|
18
|
+
: chalk_1.default.yellow(`${executionTime} ` + " ms");
|
|
19
|
+
console.log(`${chalk_1.default.green(queryResult.sql)} ${chalk_1.default.cyan(`found ${results.length} rows.`)} ${executionTimeMessage}`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
this.model = model;
|
|
23
|
+
this.dbInfo = dbInfo;
|
|
24
|
+
this.isConnected = false;
|
|
25
|
+
}
|
|
26
|
+
async init() {
|
|
27
|
+
console.log("init");
|
|
28
|
+
this.connection = await promise_1.default.createConnection({
|
|
29
|
+
host: this.dbInfo.host,
|
|
30
|
+
user: this.dbInfo.user,
|
|
31
|
+
password: this.dbInfo.password,
|
|
32
|
+
});
|
|
33
|
+
this.isConnected = true;
|
|
34
|
+
await this.createDatabaseIfNotExist();
|
|
35
|
+
}
|
|
36
|
+
async createDatabaseIfNotExist() {
|
|
37
|
+
const databaseName = this.dbInfo.database;
|
|
38
|
+
await this.connection.query(`CREATE DATABASE IF NOT EXISTS ${databaseName}`);
|
|
39
|
+
await this.connection.query(`USE ${databaseName}`);
|
|
40
|
+
}
|
|
41
|
+
async pushModelToDB() {
|
|
42
|
+
const migrator = new MyDBMigrator_1.default(this);
|
|
43
|
+
await migrator.migrateTable(this.model);
|
|
44
|
+
}
|
|
45
|
+
async pushViewToDB(viewDirectory) {
|
|
46
|
+
const migrator = new MyDBMigrator_1.default(this);
|
|
47
|
+
await migrator.migrateView(viewDirectory);
|
|
48
|
+
}
|
|
49
|
+
waitUntilInitSuccess(interval) {
|
|
50
|
+
if (this.isConnected === true)
|
|
51
|
+
return true;
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
if (this.isConnected === false) {
|
|
55
|
+
console.log(`${this.dbInfo.database} > wait for database connection . . .`);
|
|
56
|
+
this.waitUntilInitSuccess(interval);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
return resolve(true);
|
|
60
|
+
}
|
|
61
|
+
}, interval);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async query(statment, parameter, isPrint) {
|
|
65
|
+
await this.waitUntilInitSuccess(1000);
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const uniqueTime = `${(0, crypto_1.randomUUID)()} ${statment}`;
|
|
68
|
+
console.time(uniqueTime);
|
|
69
|
+
const startTime = new Date();
|
|
70
|
+
const queryResult = this.connection
|
|
71
|
+
.query(statment, parameter)
|
|
72
|
+
.then((result) => {
|
|
73
|
+
const endTime = new Date();
|
|
74
|
+
const executionTime = endTime.getTime() - startTime.getTime();
|
|
75
|
+
if (isPrint === true)
|
|
76
|
+
this.printResultLength(result, executionTime, queryResult);
|
|
77
|
+
resolve(result[0]);
|
|
78
|
+
})
|
|
79
|
+
.catch((error) => {
|
|
80
|
+
// _getCallerFile()
|
|
81
|
+
console.log(chalk_1.default.bgRed("Query Error"));
|
|
82
|
+
console.log(chalk_1.default.bgRed(error));
|
|
83
|
+
console.log(chalk_1.default.bgRed("Statement : "));
|
|
84
|
+
console.log(statment);
|
|
85
|
+
console.log(chalk_1.default.bgRed("Parameter : "));
|
|
86
|
+
console.log(parameter);
|
|
87
|
+
console.log("------------------------------------------------");
|
|
88
|
+
console.log(chalk_1.default.bgRed(queryResult.sql));
|
|
89
|
+
reject(error);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async getTableNameList() {
|
|
94
|
+
let data = [];
|
|
95
|
+
let result = (await this.query("SELECT * FROM information_schema.tables WHERE table_schema = ? AND TABLE_TYPE = 'BASE TABLE'", [this.dbInfo.database], false));
|
|
96
|
+
for (let row of result)
|
|
97
|
+
data.push(row.TABLE_NAME || row.row.table_name);
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
async getViewNameList() {
|
|
101
|
+
let data = [];
|
|
102
|
+
let result = (await this.query("SELECT * FROM information_schema.tables WHERE table_schema = ? AND TABLE_TYPE = 'VIEW'", [this.dbInfo.database], false));
|
|
103
|
+
for (let row of result)
|
|
104
|
+
data.push(row.TABLE_NAME || row.row.table_name);
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
async getColumnFullList(tableName) {
|
|
108
|
+
const result = (await this.query(`SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbInfo.database}' AND TABLE_NAME = '${tableName}'`, undefined, false));
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
isTableNameExistInModel(model, tableName) {
|
|
112
|
+
if (tableName === "sessions")
|
|
113
|
+
return true;
|
|
114
|
+
const table = model.find((row) => row.tableName === tableName);
|
|
115
|
+
return table ? true : false;
|
|
116
|
+
}
|
|
117
|
+
isColumnExistInModel(model, tableName, columnName) {
|
|
118
|
+
if (tableName === "sessions")
|
|
119
|
+
return true;
|
|
120
|
+
const table = model.find((row) => row.tableName === tableName);
|
|
121
|
+
if (table) {
|
|
122
|
+
const column = table.columns.find((row) => row.COLUMN_NAME === columnName);
|
|
123
|
+
if (column)
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
getPossibleColumnValue(model, tableName, columnName) {
|
|
129
|
+
const foundTable = model.find((row) => row.tableName === tableName);
|
|
130
|
+
if (foundTable) {
|
|
131
|
+
const foundColumn = foundTable["columns"].find((row) => row.COLUMN_NAME === columnName);
|
|
132
|
+
if (foundColumn) {
|
|
133
|
+
if (foundColumn.POSSIBLE_VALUE)
|
|
134
|
+
return foundColumn.POSSIBLE_VALUE;
|
|
135
|
+
}
|
|
136
|
+
else
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async generateModelInterface(params) {
|
|
141
|
+
const tableList = [
|
|
142
|
+
...(await this.getTableNameList()),
|
|
143
|
+
...(await this.getViewNameList()),
|
|
144
|
+
];
|
|
145
|
+
const arrayOfColumnKeyValue = [];
|
|
146
|
+
const arrayOfSeedColumnKeyValue = [];
|
|
147
|
+
for (let i = 0; i < tableList.length; i++) {
|
|
148
|
+
const tableName = tableList[i];
|
|
149
|
+
if (this.isTableNameExistInModel(params.model, tableName) ||
|
|
150
|
+
tableName.search("_view") >= 0) {
|
|
151
|
+
const columnList = await this.getColumnFullList(tableName);
|
|
152
|
+
let stringOfRawColumnKeyAndValue = "";
|
|
153
|
+
for (const cRow of columnList) {
|
|
154
|
+
const isColumnExistInModel = this.isColumnExistInModel(params.model, tableName, cRow.COLUMN_NAME);
|
|
155
|
+
if (isColumnExistInModel || tableName.search("_view") >= 0) {
|
|
156
|
+
const possibleValue = this.getPossibleColumnValue(params.model, cRow.TABLE_NAME, cRow.COLUMN_NAME);
|
|
157
|
+
if (possibleValue) {
|
|
158
|
+
stringOfRawColumnKeyAndValue = `${stringOfRawColumnKeyAndValue} \n ${cRow.COLUMN_NAME}: ${possibleValue.map((row) => `"${row}"`).join(" | ")};`;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
stringOfRawColumnKeyAndValue = `${stringOfRawColumnKeyAndValue} \n ${cRow.COLUMN_NAME}: ${convertDataType(cRow.DATA_TYPE)};`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.log(`${tableName}-${cRow.COLUMN_NAME} not exist in model `);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// normal app model code
|
|
169
|
+
const str = ` ${tableName} : { ${stringOfRawColumnKeyAndValue} }`;
|
|
170
|
+
arrayOfColumnKeyValue.push(str);
|
|
171
|
+
// only seed model code
|
|
172
|
+
const tempSeedModel = params.model.find((row) => row.tableName === tableName && row.isSeed);
|
|
173
|
+
if (tempSeedModel)
|
|
174
|
+
arrayOfSeedColumnKeyValue.push(str);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// normal app model code
|
|
178
|
+
const fileName = `${params.appName}_INF.ts`;
|
|
179
|
+
const seedFileName = `seed_INF.ts`;
|
|
180
|
+
const code = `export default interface ${params.appName}_INF { ${arrayOfColumnKeyValue.join("\n")} }`;
|
|
181
|
+
const seedCode = `export default interface seed_INF { ${arrayOfSeedColumnKeyValue.join("\n")} }`;
|
|
182
|
+
for (let row of params.outputDirectory) {
|
|
183
|
+
// normal app model code
|
|
184
|
+
const serverFilePath = path_1.default.join(row, fileName);
|
|
185
|
+
await fs_1.default.writeFileSync(serverFilePath, code);
|
|
186
|
+
console.log("save app model to ", serverFilePath);
|
|
187
|
+
// only seed model code
|
|
188
|
+
const seedServerFilePath = path_1.default.join(row, seedFileName);
|
|
189
|
+
await fs_1.default.writeFileSync(seedServerFilePath, seedCode);
|
|
190
|
+
console.log("save seed model to ", seedServerFilePath);
|
|
191
|
+
}
|
|
192
|
+
console.log(`generate interface ${params.appName} ${chalk_1.default.bgGreen(" SUCCESS ")}`);
|
|
193
|
+
return code;
|
|
194
|
+
}
|
|
195
|
+
async generateModelConstStructure(params) {
|
|
196
|
+
const isTableNameExistInModel = (tableName) => {
|
|
197
|
+
if (tableName === "sessions")
|
|
198
|
+
return true;
|
|
199
|
+
const table = params.model.find((row) => row.tableName === tableName);
|
|
200
|
+
if (table)
|
|
201
|
+
return true;
|
|
202
|
+
return false;
|
|
203
|
+
};
|
|
204
|
+
const isColumnExistInModel = (tableName, columnName) => {
|
|
205
|
+
if (tableName === "sessions")
|
|
206
|
+
return true;
|
|
207
|
+
const table = params.model.find((row) => row.tableName === tableName);
|
|
208
|
+
if (table) {
|
|
209
|
+
const column = table.columns.find((row) => row.COLUMN_NAME === columnName);
|
|
210
|
+
if (column)
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
};
|
|
215
|
+
const getPossibleColumnValue = (tableName, columnName) => {
|
|
216
|
+
const foundTable = params.model.find((row) => row.tableName === tableName);
|
|
217
|
+
if (foundTable) {
|
|
218
|
+
const foundColumn = foundTable["columns"].find((row) => row.COLUMN_NAME === columnName);
|
|
219
|
+
if (foundColumn) {
|
|
220
|
+
if (foundColumn.POSSIBLE_VALUE)
|
|
221
|
+
return foundColumn.POSSIBLE_VALUE;
|
|
222
|
+
}
|
|
223
|
+
else
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
const tableList = [
|
|
228
|
+
...(await this.getTableNameList()),
|
|
229
|
+
...(await this.getViewNameList()),
|
|
230
|
+
];
|
|
231
|
+
const array = [];
|
|
232
|
+
for (let i = 0; i < tableList.length; i++) {
|
|
233
|
+
const tableName = tableList[i];
|
|
234
|
+
if (isTableNameExistInModel(tableName) ||
|
|
235
|
+
tableName.search("_view") >= 0) {
|
|
236
|
+
const columnList = await this.getColumnFullList(tableName);
|
|
237
|
+
let value = "";
|
|
238
|
+
for (const cRow of columnList) {
|
|
239
|
+
const columnChecker = isColumnExistInModel(tableName, cRow.COLUMN_NAME);
|
|
240
|
+
if (columnChecker || tableName.search("_view") >= 0) {
|
|
241
|
+
const possibleValue = getPossibleColumnValue(cRow.TABLE_NAME, cRow.COLUMN_NAME);
|
|
242
|
+
if (possibleValue) {
|
|
243
|
+
value = `${value} \n ${cRow.COLUMN_NAME}: [${possibleValue
|
|
244
|
+
.map((row) => {
|
|
245
|
+
if (typeof row === "string")
|
|
246
|
+
return `"${row}"`;
|
|
247
|
+
else if (typeof row === "number")
|
|
248
|
+
return `${row}`;
|
|
249
|
+
})
|
|
250
|
+
.join(" , ")}] as ${typeof possibleValue[0]}[],`;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
value = `${value} \n ${cRow.COLUMN_NAME}: "${convertDataType(cRow.DATA_TYPE)}",`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
console.log(`${tableName}-${cRow.COLUMN_NAME} not exist in model `);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const str = ` ${tableName} : { ${value} }`;
|
|
261
|
+
array.push(str);
|
|
262
|
+
// console.log(`generate interface from ${tableName}`)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const fileName = `${params.appName}_model_const.ts`;
|
|
266
|
+
const code = `
|
|
267
|
+
export const ${params.appName}_model_const = { ${array.join(",")} } as const`;
|
|
268
|
+
for (let row of params.outputDirectory) {
|
|
269
|
+
const serverFilePath = path_1.default.join(row, fileName);
|
|
270
|
+
await fs_1.default.writeFileSync(serverFilePath, code);
|
|
271
|
+
console.log("save to ", serverFilePath);
|
|
272
|
+
}
|
|
273
|
+
console.log(`generate interface ${params.appName} ${chalk_1.default.bgGreen(" SUCCESS ")}`);
|
|
274
|
+
return code;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
exports.default = ModelGenerator;
|
|
278
|
+
function convertDataType(inputDataType) {
|
|
279
|
+
let result = "string";
|
|
280
|
+
if (inputDataType === "int") {
|
|
281
|
+
result = "number";
|
|
282
|
+
}
|
|
283
|
+
else if (inputDataType === "tinyint") {
|
|
284
|
+
result = "number";
|
|
285
|
+
}
|
|
286
|
+
else if (inputDataType === "bigint") {
|
|
287
|
+
result = "number";
|
|
288
|
+
}
|
|
289
|
+
else if (inputDataType === "date") {
|
|
290
|
+
result = "string";
|
|
291
|
+
}
|
|
292
|
+
else if (inputDataType === "float") {
|
|
293
|
+
result = "number";
|
|
294
|
+
}
|
|
295
|
+
else if (inputDataType === "double") {
|
|
296
|
+
result = "number";
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/ModelGenerator.js
CHANGED
|
@@ -253,15 +253,7 @@ class ModelGenerator {
|
|
|
253
253
|
}
|
|
254
254
|
const fileName = `${params.appName}_model_const.ts`;
|
|
255
255
|
const code = `
|
|
256
|
-
|
|
257
|
-
interface constInterface {
|
|
258
|
-
[keytable: string]: {
|
|
259
|
-
[key: string]: paramType
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
export const ${params.appName}_model_const : constInterface = { ${array.join(",")} } as const`;
|
|
256
|
+
export const ${params.appName}_model_const = { ${array.join(",")} } as const`;
|
|
265
257
|
for (let row of params.outputDirectory) {
|
|
266
258
|
const serverFilePath = path_1.default.join(row, fileName);
|
|
267
259
|
await fs_1.default.writeFileSync(serverFilePath, code);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import ModelGenerator from "../ModelGenerator/ModelGenerator";
|
|
2
|
+
import { columnType, MyModel } from "../myModel";
|
|
3
|
+
export default class MyDBMigrator {
|
|
4
|
+
MyDB: ModelGenerator;
|
|
5
|
+
onetimeLoadColumnContent: {
|
|
6
|
+
COLUMN_NAME: string;
|
|
7
|
+
DATA_TYPE: columnType;
|
|
8
|
+
TABLE_NAME: string;
|
|
9
|
+
COLUMN_DEFAULT: string;
|
|
10
|
+
COLUMN_KEY: string;
|
|
11
|
+
}[];
|
|
12
|
+
constructor(connection: ModelGenerator);
|
|
13
|
+
migrateTable(model: MyModel[]): Promise<void>;
|
|
14
|
+
private createTableIfNotExist;
|
|
15
|
+
private columnInTableExist;
|
|
16
|
+
private loadColumnContentIfEmpty;
|
|
17
|
+
private checkIsColumnDataTypeChange;
|
|
18
|
+
alterUniqueKey(tableName: string, columnName: string, IS_UNIQUE: boolean | undefined): Promise<void>;
|
|
19
|
+
alterIndex(tableName: string, columnName: string, IS_INDEX: boolean | undefined): Promise<void>;
|
|
20
|
+
migrateView(viewDirectory: string): Promise<0 | undefined>;
|
|
21
|
+
checkIsPrimaryKey(databaseName: string, tableName: string, columnName: string): Promise<boolean>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
class MyDBMigrator {
|
|
10
|
+
constructor(connection) {
|
|
11
|
+
this.MyDB = connection;
|
|
12
|
+
this.onetimeLoadColumnContent = [];
|
|
13
|
+
}
|
|
14
|
+
async migrateTable(model) {
|
|
15
|
+
let data = model.filter((row) => row.tableName !== "sessions" && row.tableName.search("prisma") < 0);
|
|
16
|
+
let index = 1;
|
|
17
|
+
for (let row of data) {
|
|
18
|
+
console.log(chalk_1.default.bgBlue(`${index}/${data.length} ${row.tableName}`));
|
|
19
|
+
await this.createTableIfNotExist(row.tableName);
|
|
20
|
+
this.onetimeLoadColumnContent = [];
|
|
21
|
+
await this.loadColumnContentIfEmpty();
|
|
22
|
+
index++;
|
|
23
|
+
for (let col of row.columns) {
|
|
24
|
+
console.log(`${row.tableName} (${col.DATA_TYPE}) - ${col.COLUMN_NAME} `);
|
|
25
|
+
if (col.COLUMN_NAME === "id" && col.AUTO_INCREMENT === true) {
|
|
26
|
+
if ((await this.checkIsPrimaryKey(this.MyDB.dbInfo.database, row.tableName, col.COLUMN_NAME)) === false)
|
|
27
|
+
await this.MyDB.query(`ALTER TABLE ${row.tableName} ADD PRIMARY KEY(id);`);
|
|
28
|
+
}
|
|
29
|
+
const AutoIncrement = col.AUTO_INCREMENT ? "AUTO_INCREMENT" : "";
|
|
30
|
+
const columnDefault = col.COLUMN_DEFAULT
|
|
31
|
+
? `DEFAULT ${col.COLUMN_DEFAULT}`
|
|
32
|
+
: "";
|
|
33
|
+
const isColumnChange = await this.checkIsColumnDataTypeChange(row.tableName, col.COLUMN_NAME, col.DATA_TYPE, col.COLUMN_DEFAULT);
|
|
34
|
+
if (isColumnChange) {
|
|
35
|
+
const isColumnExist = await this.columnInTableExist(row.tableName, col.COLUMN_NAME);
|
|
36
|
+
if (!isColumnExist) {
|
|
37
|
+
await this.MyDB.query(`ALTER TABLE ${row.tableName} ADD COLUMN ${col.COLUMN_NAME} ${col.DATA_TYPE} ${AutoIncrement} ${columnDefault}`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
await this.MyDB.query(`ALTER TABLE ${row.tableName} MODIFY COLUMN ${col.COLUMN_NAME} ${col.DATA_TYPE} ${AutoIncrement} ${columnDefault}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await this.alterUniqueKey(row.tableName, col.COLUMN_NAME, col.IS_UNIQUE);
|
|
44
|
+
await this.alterIndex(row.tableName, col.COLUMN_NAME, col.IS_INDEX);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log("migrate table done");
|
|
48
|
+
}
|
|
49
|
+
async createTableIfNotExist(tableName) {
|
|
50
|
+
await this.MyDB.query(`CREATE TABLE IF NOT EXISTS ${tableName} (id INT(11) AUTO_INCREMENT PRIMARY KEY)`);
|
|
51
|
+
}
|
|
52
|
+
async columnInTableExist(tableName, columnName) {
|
|
53
|
+
await this.loadColumnContentIfEmpty();
|
|
54
|
+
let result = this.onetimeLoadColumnContent.filter((row) => row.COLUMN_NAME === columnName && row.TABLE_NAME === tableName);
|
|
55
|
+
if (result.length)
|
|
56
|
+
return true;
|
|
57
|
+
else
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
async loadColumnContentIfEmpty() {
|
|
61
|
+
if (this.onetimeLoadColumnContent.length === 0) {
|
|
62
|
+
this.onetimeLoadColumnContent = (await this.MyDB.query(`SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.MyDB.dbInfo.database}'`, undefined, false));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async checkIsColumnDataTypeChange(tableName, columnName, dataType, columnDefault) {
|
|
66
|
+
await this.loadColumnContentIfEmpty();
|
|
67
|
+
let temp = this.onetimeLoadColumnContent.filter((row) => row.TABLE_NAME === tableName && row.COLUMN_NAME === columnName);
|
|
68
|
+
if (columnDefault === undefined)
|
|
69
|
+
columnDefault = "NULL";
|
|
70
|
+
if (columnDefault === null)
|
|
71
|
+
columnDefault = "NULL";
|
|
72
|
+
if (temp.length > 0) {
|
|
73
|
+
if (temp[0].DATA_TYPE !== dataType) {
|
|
74
|
+
console.log(chalk_1.default.green `${temp[0].DATA_TYPE} change to ${dataType}`);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (temp[0].COLUMN_DEFAULT !== columnDefault) {
|
|
78
|
+
console.log(chalk_1.default.green `${temp[0].COLUMN_DEFAULT} change to ${columnDefault}`);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log(chalk_1.default.red `${columnName} not found`);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async alterUniqueKey(tableName, columnName, IS_UNIQUE) {
|
|
89
|
+
const data = this.onetimeLoadColumnContent.find((row) => row.COLUMN_NAME === columnName && row.TABLE_NAME === tableName);
|
|
90
|
+
if (data) {
|
|
91
|
+
if ((data.COLUMN_KEY === "UNI" && !IS_UNIQUE) ||
|
|
92
|
+
(data.COLUMN_KEY !== "UNI" && IS_UNIQUE === true))
|
|
93
|
+
if (IS_UNIQUE === true) {
|
|
94
|
+
await this.MyDB.query(`ALTER TABLE ${tableName} ADD UNIQUE (${columnName});`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
await this.MyDB.query(`ALTER TABLE ${tableName} DROP INDEX ${columnName};`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async alterIndex(tableName, columnName, IS_INDEX) {
|
|
102
|
+
const data = this.onetimeLoadColumnContent.find((row) => row.COLUMN_NAME === columnName && row.TABLE_NAME === tableName);
|
|
103
|
+
if (data) {
|
|
104
|
+
if ((data.COLUMN_KEY === "MUL" && !IS_INDEX) ||
|
|
105
|
+
(data.COLUMN_KEY !== "MUL" && IS_INDEX === true))
|
|
106
|
+
if (IS_INDEX === true) {
|
|
107
|
+
await this.MyDB.query(`ALTER TABLE ${tableName} ADD INDEX (${columnName});`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
await this.MyDB.query(`DROP INDEX ${columnName} ON ${tableName};`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async migrateView(viewDirectory) {
|
|
115
|
+
if (!fs_1.default.existsSync(viewDirectory)) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
let fileList = (await fs_1.default.readdirSync(path_1.default.join(viewDirectory)));
|
|
119
|
+
let unSuccessUpdateViewList = fileList;
|
|
120
|
+
let loopCount = 0;
|
|
121
|
+
while (unSuccessUpdateViewList.length > 0) {
|
|
122
|
+
for (const viewFileName of unSuccessUpdateViewList) {
|
|
123
|
+
const filePath = path_1.default.join(viewDirectory, viewFileName);
|
|
124
|
+
const content = fs_1.default.readFileSync(filePath, "utf8");
|
|
125
|
+
process.stdout.write(`loop : ${loopCount} unUpdate : ${unSuccessUpdateViewList.length} update view ${viewFileName}`);
|
|
126
|
+
await this.MyDB.query(content)
|
|
127
|
+
.then((result) => {
|
|
128
|
+
process.stdout.write(" " + chalk_1.default.bgGreen(`success`));
|
|
129
|
+
unSuccessUpdateViewList = unSuccessUpdateViewList.filter((row) => row !== viewFileName);
|
|
130
|
+
})
|
|
131
|
+
.catch((_error) => {
|
|
132
|
+
process.stdout.write(" " + chalk_1.default.bgRed(`fail`));
|
|
133
|
+
})
|
|
134
|
+
.finally(() => {
|
|
135
|
+
console.log("");
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async checkIsPrimaryKey(databaseName, tableName, columnName) {
|
|
141
|
+
let result = await this.MyDB.query(`SELECT *
|
|
142
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
143
|
+
WHERE TABLE_SCHEMA='${databaseName}'
|
|
144
|
+
AND TABLE_NAME='${tableName}'
|
|
145
|
+
AND COLUMN_NAME='${columnName}'`);
|
|
146
|
+
console.log("result", result);
|
|
147
|
+
if (result.length) {
|
|
148
|
+
if (result[0].COLUMN_KEY === "PRI")
|
|
149
|
+
return true;
|
|
150
|
+
else
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
else
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.default = MyDBMigrator;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { MyModel, columnContent, columnType } from "./myModel";
|
|
2
|
-
export { DatabaseConfigInterface } from "./ModelGenerator";
|
|
3
|
-
export { default as ModelGenerator } from "./ModelGenerator";
|
|
4
|
-
export { default as GGMySQLConnector } from "./GGMySQLConnector";
|
|
2
|
+
export { DatabaseConfigInterface } from "./ModelGenerator/ModelGenerator";
|
|
3
|
+
export { default as ModelGenerator } from "./ModelGenerator/ModelGenerator";
|
|
4
|
+
export { default as GGMySQLConnector } from "./GGMySQLConnector/GGMySQLConnector";
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GGMySQLConnector = exports.ModelGenerator = void 0;
|
|
7
|
-
var ModelGenerator_1 = require("./ModelGenerator");
|
|
7
|
+
var ModelGenerator_1 = require("./ModelGenerator/ModelGenerator");
|
|
8
8
|
Object.defineProperty(exports, "ModelGenerator", { enumerable: true, get: function () { return __importDefault(ModelGenerator_1).default; } });
|
|
9
|
-
var GGMySQLConnector_1 = require("./GGMySQLConnector");
|
|
9
|
+
var GGMySQLConnector_1 = require("./GGMySQLConnector/GGMySQLConnector");
|
|
10
10
|
Object.defineProperty(exports, "GGMySQLConnector", { enumerable: true, get: function () { return __importDefault(GGMySQLConnector_1).default; } });
|
package/dist/myModel.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk"
|
|
2
2
|
import { randomUUID } from "crypto"
|
|
3
3
|
import mysql, { QueryResult, RowDataPacket } from "mysql2"
|
|
4
|
-
import { DatabaseConfigInterface } from "
|
|
4
|
+
import { DatabaseConfigInterface } from "../ModelGenerator/ModelGenerator"
|
|
5
5
|
|
|
6
6
|
export interface ClassDBInterface<Main> {
|
|
7
7
|
connection: mysql.Connection
|
|
@@ -3,8 +3,8 @@ import { randomUUID } from "crypto"
|
|
|
3
3
|
import fs from "fs"
|
|
4
4
|
import mysql from "mysql2/promise"
|
|
5
5
|
import path from "path"
|
|
6
|
-
import
|
|
7
|
-
import { MyModel } from "
|
|
6
|
+
import MyDBMigrator from "../MyDBMigrator/MyDBMigrator"
|
|
7
|
+
import { MyModel } from "../myModel"
|
|
8
8
|
export interface DatabaseConfigInterface {
|
|
9
9
|
host: string
|
|
10
10
|
user: string
|
|
@@ -39,11 +39,11 @@ export default class ModelGenerator {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
async pushModelToDB() {
|
|
42
|
-
const migrator = new
|
|
42
|
+
const migrator = new MyDBMigrator(this)
|
|
43
43
|
await migrator.migrateTable(this.model)
|
|
44
44
|
}
|
|
45
45
|
async pushViewToDB(viewDirectory: string) {
|
|
46
|
-
const migrator = new
|
|
46
|
+
const migrator = new MyDBMigrator(this)
|
|
47
47
|
await migrator.migrateView(viewDirectory)
|
|
48
48
|
}
|
|
49
49
|
waitUntilInitSuccess(interval: number) {
|
|
@@ -142,70 +142,79 @@ export default class ModelGenerator {
|
|
|
142
142
|
)) as mysql.RowDataPacket[]
|
|
143
143
|
return result
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
private isTableNameExistInModel(model: MyModel[], tableName: string) {
|
|
147
|
+
if (tableName === "sessions") return true
|
|
148
|
+
const table = model.find((row) => row.tableName === tableName)
|
|
149
|
+
return table ? true : false
|
|
150
|
+
}
|
|
151
|
+
private isColumnExistInModel(
|
|
152
|
+
model: MyModel[],
|
|
153
|
+
tableName: string,
|
|
154
|
+
columnName: string
|
|
155
|
+
) {
|
|
156
|
+
if (tableName === "sessions") return true
|
|
157
|
+
const table = model.find((row) => row.tableName === tableName)
|
|
158
|
+
if (table) {
|
|
159
|
+
const column = table.columns.find((row) => row.COLUMN_NAME === columnName)
|
|
160
|
+
if (column) return true
|
|
161
|
+
}
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
getPossibleColumnValue(
|
|
165
|
+
model: MyModel[],
|
|
166
|
+
tableName: string,
|
|
167
|
+
columnName: string
|
|
168
|
+
) {
|
|
169
|
+
const foundTable = model.find((row) => row.tableName === tableName)
|
|
170
|
+
if (foundTable) {
|
|
171
|
+
const foundColumn = foundTable["columns"].find(
|
|
172
|
+
(row) => row.COLUMN_NAME === columnName
|
|
173
|
+
)
|
|
174
|
+
if (foundColumn) {
|
|
175
|
+
if (foundColumn.POSSIBLE_VALUE) return foundColumn.POSSIBLE_VALUE
|
|
176
|
+
} else return false
|
|
177
|
+
}
|
|
178
|
+
}
|
|
145
179
|
async generateModelInterface(params: {
|
|
146
180
|
appName: string
|
|
147
181
|
model: MyModel[]
|
|
148
182
|
outputDirectory: string[]
|
|
149
183
|
}) {
|
|
150
|
-
const isTableNameExistInModel = (tableName: string) => {
|
|
151
|
-
if (tableName === "sessions") return true
|
|
152
|
-
const table = params.model.find((row) => row.tableName === tableName)
|
|
153
|
-
if (table) return true
|
|
154
|
-
return false
|
|
155
|
-
}
|
|
156
|
-
const isColumnExistInModel = (tableName: string, columnName: string) => {
|
|
157
|
-
if (tableName === "sessions") return true
|
|
158
|
-
const table = params.model.find((row) => row.tableName === tableName)
|
|
159
|
-
if (table) {
|
|
160
|
-
const column = table.columns.find(
|
|
161
|
-
(row) => row.COLUMN_NAME === columnName
|
|
162
|
-
)
|
|
163
|
-
if (column) return true
|
|
164
|
-
}
|
|
165
|
-
return false
|
|
166
|
-
}
|
|
167
|
-
const getPossibleColumnValue = (tableName: string, columnName: string) => {
|
|
168
|
-
const foundTable = params.model.find((row) => row.tableName === tableName)
|
|
169
|
-
if (foundTable) {
|
|
170
|
-
const foundColumn = foundTable["columns"].find(
|
|
171
|
-
(row) => row.COLUMN_NAME === columnName
|
|
172
|
-
)
|
|
173
|
-
if (foundColumn) {
|
|
174
|
-
if (foundColumn.POSSIBLE_VALUE) return foundColumn.POSSIBLE_VALUE
|
|
175
|
-
} else return false
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
184
|
const tableList = [
|
|
179
185
|
...(await this.getTableNameList()),
|
|
180
186
|
...(await this.getViewNameList()),
|
|
181
187
|
]
|
|
182
188
|
|
|
183
|
-
const
|
|
189
|
+
const arrayOfColumnKeyValue = []
|
|
190
|
+
const arrayOfSeedColumnKeyValue = []
|
|
184
191
|
for (let i = 0; i < tableList.length; i++) {
|
|
185
192
|
const tableName = tableList[i]
|
|
186
193
|
if (
|
|
187
|
-
isTableNameExistInModel(tableName) ||
|
|
194
|
+
this.isTableNameExistInModel(params.model, tableName) ||
|
|
188
195
|
tableName.search("_view") >= 0
|
|
189
196
|
) {
|
|
190
197
|
const columnList = await this.getColumnFullList(tableName)
|
|
191
198
|
|
|
192
|
-
let
|
|
199
|
+
let stringOfRawColumnKeyAndValue = ""
|
|
193
200
|
for (const cRow of columnList) {
|
|
194
|
-
const
|
|
201
|
+
const isColumnExistInModel = this.isColumnExistInModel(
|
|
202
|
+
params.model,
|
|
195
203
|
tableName,
|
|
196
204
|
cRow.COLUMN_NAME
|
|
197
205
|
)
|
|
198
|
-
if (
|
|
199
|
-
const possibleValue = getPossibleColumnValue(
|
|
206
|
+
if (isColumnExistInModel || tableName.search("_view") >= 0) {
|
|
207
|
+
const possibleValue = this.getPossibleColumnValue(
|
|
208
|
+
params.model,
|
|
200
209
|
cRow.TABLE_NAME,
|
|
201
210
|
cRow.COLUMN_NAME
|
|
202
211
|
)
|
|
203
212
|
if (possibleValue) {
|
|
204
|
-
|
|
213
|
+
stringOfRawColumnKeyAndValue = `${stringOfRawColumnKeyAndValue} \n ${
|
|
205
214
|
cRow.COLUMN_NAME
|
|
206
215
|
}: ${possibleValue.map((row) => `"${row}"`).join(" | ")};`
|
|
207
216
|
} else {
|
|
208
|
-
|
|
217
|
+
stringOfRawColumnKeyAndValue = `${stringOfRawColumnKeyAndValue} \n ${
|
|
209
218
|
cRow.COLUMN_NAME
|
|
210
219
|
}: ${convertDataType(cRow.DATA_TYPE)};`
|
|
211
220
|
}
|
|
@@ -214,20 +223,35 @@ export default class ModelGenerator {
|
|
|
214
223
|
}
|
|
215
224
|
}
|
|
216
225
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
226
|
+
// normal app model code
|
|
227
|
+
const str = ` ${tableName} : { ${stringOfRawColumnKeyAndValue} }`
|
|
228
|
+
arrayOfColumnKeyValue.push(str)
|
|
229
|
+
// only seed model code
|
|
230
|
+
const tempSeedModel = params.model.find(
|
|
231
|
+
(row) => row.tableName === tableName && row.isSeed
|
|
232
|
+
)
|
|
233
|
+
if (tempSeedModel) arrayOfSeedColumnKeyValue.push(str)
|
|
220
234
|
}
|
|
221
235
|
}
|
|
236
|
+
// normal app model code
|
|
222
237
|
const fileName = `${params.appName}_INF.ts`
|
|
238
|
+
const seedFileName = `seed_INF.ts`
|
|
223
239
|
const code = `export default interface ${
|
|
224
240
|
params.appName
|
|
225
|
-
}_INF { ${
|
|
241
|
+
}_INF { ${arrayOfColumnKeyValue.join("\n")} }`
|
|
242
|
+
const seedCode = `export default interface seed_INF { ${arrayOfSeedColumnKeyValue.join(
|
|
243
|
+
"\n"
|
|
244
|
+
)} }`
|
|
226
245
|
|
|
227
246
|
for (let row of params.outputDirectory) {
|
|
247
|
+
// normal app model code
|
|
228
248
|
const serverFilePath = path.join(row, fileName)
|
|
229
249
|
await fs.writeFileSync(serverFilePath, code)
|
|
230
|
-
console.log("save to ", serverFilePath)
|
|
250
|
+
console.log("save app model to ", serverFilePath)
|
|
251
|
+
// only seed model code
|
|
252
|
+
const seedServerFilePath = path.join(row, seedFileName)
|
|
253
|
+
await fs.writeFileSync(seedServerFilePath, seedCode)
|
|
254
|
+
console.log("save seed model to ", seedServerFilePath)
|
|
231
255
|
}
|
|
232
256
|
|
|
233
257
|
console.log(
|
|
@@ -319,17 +343,9 @@ export default class ModelGenerator {
|
|
|
319
343
|
}
|
|
320
344
|
const fileName = `${params.appName}_model_const.ts`
|
|
321
345
|
const code = `
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
[key: string]: paramType
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
export const ${
|
|
331
|
-
params.appName
|
|
332
|
-
}_model_const : constInterface = { ${array.join(",")} } as const`
|
|
346
|
+
export const ${params.appName}_model_const = { ${array.join(
|
|
347
|
+
","
|
|
348
|
+
)} } as const`
|
|
333
349
|
|
|
334
350
|
for (let row of params.outputDirectory) {
|
|
335
351
|
const serverFilePath = path.join(row, fileName)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import chalk from "chalk"
|
|
2
2
|
import fs from "fs"
|
|
3
3
|
import path from "path"
|
|
4
|
-
import ModelGenerator from "
|
|
5
|
-
import { columnType, MyModel } from "
|
|
4
|
+
import ModelGenerator from "../ModelGenerator/ModelGenerator"
|
|
5
|
+
import { columnType, MyModel } from "../myModel"
|
|
6
6
|
|
|
7
|
-
export default class
|
|
7
|
+
export default class MyDBMigrator {
|
|
8
8
|
MyDB: ModelGenerator
|
|
9
9
|
onetimeLoadColumnContent: {
|
|
10
10
|
COLUMN_NAME: string
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { MyModel, columnContent, columnType } from "./myModel"
|
|
2
|
-
export { DatabaseConfigInterface } from "./ModelGenerator"
|
|
3
|
-
export { default as ModelGenerator } from "./ModelGenerator"
|
|
4
|
-
export { default as GGMySQLConnector } from "./GGMySQLConnector"
|
|
2
|
+
export { DatabaseConfigInterface } from "./ModelGenerator/ModelGenerator"
|
|
3
|
+
export { default as ModelGenerator } from "./ModelGenerator/ModelGenerator"
|
|
4
|
+
export { default as GGMySQLConnector } from "./GGMySQLConnector/GGMySQLConnector"
|
package/src/myModel.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
export type columnType =
|
|
3
2
|
| "int"
|
|
4
3
|
| "tinyint"
|
|
@@ -26,10 +25,9 @@ export interface columnContent {
|
|
|
26
25
|
IS_INDEX?: boolean
|
|
27
26
|
}
|
|
28
27
|
export interface MyModel {
|
|
28
|
+
isSeed: boolean
|
|
29
29
|
tableName: string
|
|
30
|
-
columns: [
|
|
31
|
-
...columnContent[]
|
|
32
|
-
]
|
|
30
|
+
columns: [...columnContent[]]
|
|
33
31
|
meaning?: string
|
|
34
32
|
}
|
|
35
33
|
// export interface MyModelView {
|