forked from nettofarah/mysql-schema-ts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmysql-client.ts
145 lines (122 loc) · 4.03 KB
/
mysql-client.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { createConnection, Connection, MysqlError } from 'mysql'
import { parse as urlParse } from 'url'
import { Table } from './typescript'
import { mapColumn } from './column-map'
import { SQL as sql, SQLStatement } from 'sql-template-strings'
function parseEnum(dbEnum: string): string[] {
return dbEnum.replace(/(^(enum|set)\('|'\)$)/gi, '').split(`','`)
}
function enumNameFromColumn(dataType: string, columnName: string): string {
return `${dataType}_${columnName}`
}
type EnumRecord = {
column_name: string
column_type: string
data_type: string
}
type TableColumnType = {
column_name: string
data_type: string
is_nullable: string
column_default: string | null
column_comment: string | null
}
type TableType = {
table_name: string
}
export type Enums = { [key: string]: string[] }
export function query<T>(conn: Connection, sql: SQLStatement): Promise<T[]> {
return new Promise((resolve, reject) => {
conn.query(sql.sql, sql.values, (error: MysqlError | null, results: Array<T>) => {
if (error) {
return reject(error)
}
return resolve(results)
})
})
}
export class MySQL {
private connection: Connection
private defaultSchema: string
constructor(connectionString: string) {
this.connection = createConnection(connectionString)
const database = urlParse(connectionString, true).pathname?.substr(1) || 'public'
this.defaultSchema = database
}
public async table(tableName: string): Promise<Table> {
const enumTypes = await this.enums(tableName)
const table = await this.getTable(tableName, this.schema())
return mapColumn(table, enumTypes)
}
public async allTables(): Promise<{ name: string; table: Table }[]> {
const names = await this.tableNames()
const nameMapping = names.map(async name => ({
name,
table: await this.table(name)
}))
return Promise.all(nameMapping)
}
private async tableNames(): Promise<string[]> {
const schemaTables = await query<TableType>(
this.connection,
sql`SELECT table_name as table_name
FROM information_schema.columns
WHERE table_schema = ${this.schema()}
GROUP BY table_name
`
)
return schemaTables.map(schemaItem => schemaItem.table_name)
}
public schema(): string {
return this.defaultSchema
}
private async enums(tableName: string): Promise<Enums> {
const enums: Enums = {}
const rawEnumRecords = await query<EnumRecord>(
this.connection,
sql`SELECT
column_name as column_name,
column_type as column_type,
data_type as data_type
FROM information_schema.columns
WHERE data_type IN ('enum', 'set')
AND table_schema = ${this.schema()}
AND table_name = ${tableName}`
)
rawEnumRecords.forEach(enumItem => {
const enumName = enumNameFromColumn(enumItem.data_type, enumItem.column_name)
const enumValues = parseEnum(enumItem.column_type)
enums[enumName] = enumValues
})
return enums
}
private async getTable(tableName: string, tableSchema: string): Promise<Table> {
const Table: Table = {}
const tableColumns = await query<TableColumnType>(
this.connection,
sql`SELECT
column_name as column_name,
data_type as data_type,
is_nullable as is_nullable,
column_default as column_default,
column_comment as column_comment
FROM information_schema.columns
WHERE table_name = ${tableName}
AND table_schema = ${tableSchema}`
)
tableColumns.forEach(schemaItem => {
const columnName = schemaItem.column_name
const dataType = schemaItem.data_type
const isEnum = /^(enum|set)$/i.test(dataType)
const nullable = schemaItem.is_nullable === 'YES'
Table[columnName] = {
udtName: isEnum ? enumNameFromColumn(dataType, columnName) : dataType,
comment: schemaItem.column_comment,
hasDefault: Boolean(schemaItem.column_default),
defaultValue: schemaItem.column_default,
nullable
}
})
return Table
}
}