@travetto/model-mysql 7.0.0-rc.2 → 7.0.0-rc.3
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 +2 -2
- package/package.json +7 -7
- package/src/connection.ts +7 -6
- package/src/dialect.ts +81 -2
- package/support/service.mysql.ts +3 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-mysql",
|
|
3
|
-
"version": "7.0.0-rc.
|
|
3
|
+
"version": "7.0.0-rc.3",
|
|
4
4
|
"description": "MySQL backing for the travetto model module, with real-time modeling support for SQL schemas.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sql",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"directory": "module/model-mysql"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/cli": "^7.0.0-rc.
|
|
31
|
-
"@travetto/config": "^7.0.0-rc.
|
|
32
|
-
"@travetto/context": "^7.0.0-rc.
|
|
33
|
-
"@travetto/model": "^7.0.0-rc.
|
|
34
|
-
"@travetto/model-query": "^7.0.0-rc.
|
|
35
|
-
"@travetto/model-sql": "^7.0.0-rc.
|
|
30
|
+
"@travetto/cli": "^7.0.0-rc.3",
|
|
31
|
+
"@travetto/config": "^7.0.0-rc.3",
|
|
32
|
+
"@travetto/context": "^7.0.0-rc.3",
|
|
33
|
+
"@travetto/model": "^7.0.0-rc.3",
|
|
34
|
+
"@travetto/model-query": "^7.0.0-rc.3",
|
|
35
|
+
"@travetto/model-sql": "^7.0.0-rc.3",
|
|
36
36
|
"mysql2": "^3.15.3"
|
|
37
37
|
},
|
|
38
38
|
"travetto": {
|
package/src/connection.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createPool } from 'mysql2';
|
|
2
2
|
import { PoolConnection, Pool, OkPacket, ResultSetHeader } from 'mysql2/promise';
|
|
3
3
|
|
|
4
|
-
import { castTo, ShutdownManager } from '@travetto/runtime';
|
|
4
|
+
import { castTo, JSONUtil, ShutdownManager } from '@travetto/runtime';
|
|
5
5
|
import { AsyncContext } from '@travetto/context';
|
|
6
6
|
import { ExistsError } from '@travetto/model';
|
|
7
7
|
import { Connection, SQLModelConfig } from '@travetto/model-sql';
|
|
@@ -52,7 +52,7 @@ export class MySQLConnection extends Connection<PoolConnection> {
|
|
|
52
52
|
if (typeof result === 'string' && (field && typeof field === 'object' && 'type' in field) && (field.type === 'JSON' || field.type === 'BLOB')) {
|
|
53
53
|
if (result.charAt(0) === '{' && result.charAt(result.length - 1) === '}') {
|
|
54
54
|
try {
|
|
55
|
-
return
|
|
55
|
+
return JSONUtil.parseSafe(result);
|
|
56
56
|
} catch { }
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -76,10 +76,11 @@ export class MySQLConnection extends Connection<PoolConnection> {
|
|
|
76
76
|
}
|
|
77
77
|
} catch (error) {
|
|
78
78
|
console.debug('Failed query', { error, query });
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
throw
|
|
79
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
|
|
80
|
+
switch (code) {
|
|
81
|
+
case 'ER_DUP_ENTRY': throw new ExistsError('query', query);
|
|
82
|
+
case 'ER_DUP_KEYNAME': throw new ExistsError('index', query);
|
|
83
|
+
default: throw error;
|
|
83
84
|
}
|
|
84
85
|
} finally {
|
|
85
86
|
try {
|
package/src/dialect.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { Injectable } from '@travetto/di';
|
|
|
3
3
|
import { AsyncContext } from '@travetto/context';
|
|
4
4
|
import { WhereClause } from '@travetto/model-query';
|
|
5
5
|
import { castTo, Class } from '@travetto/runtime';
|
|
6
|
-
import { ModelType } from '@travetto/model';
|
|
7
|
-
import { SQLModelConfig, SQLDialect, VisitStack } from '@travetto/model-sql';
|
|
6
|
+
import { ModelType, type IndexConfig } from '@travetto/model';
|
|
7
|
+
import { SQLModelConfig, SQLDialect, VisitStack, type SQLTableDescription, SQLModelUtil } from '@travetto/model-sql';
|
|
8
8
|
|
|
9
9
|
import { MySQLConnection } from './connection.ts';
|
|
10
10
|
|
|
@@ -61,6 +61,85 @@ export class MySQLDialect extends SQLDialect {
|
|
|
61
61
|
return `SHA2('${value}', '256')`;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Get DROP INDEX sql
|
|
66
|
+
*/
|
|
67
|
+
getDropIndexSQL<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string): string {
|
|
68
|
+
const constraint = typeof idx === 'string' ? idx : this.getIndexName(cls, idx);
|
|
69
|
+
return `DROP INDEX ${this.identifier(constraint)} ON ${this.table(SQLModelUtil.classToStack(cls))};`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async describeTable(table: string): Promise<SQLTableDescription | undefined> {
|
|
73
|
+
const IGNORE_FIELDS = [this.pathField.name, this.parentPathField.name, this.idxField.name].map(field => `'${field}'`);
|
|
74
|
+
const [columns, foreignKeys, indices] = await Promise.all([
|
|
75
|
+
// 1. Columns
|
|
76
|
+
this.executeSQL<{ name: string, type: string, is_notnull: boolean }>(`
|
|
77
|
+
SELECT
|
|
78
|
+
COLUMN_NAME AS name,
|
|
79
|
+
COLUMN_TYPE AS type,
|
|
80
|
+
IS_NULLABLE <> 'YES' AS is_notnull
|
|
81
|
+
FROM information_schema.COLUMNS
|
|
82
|
+
WHERE TABLE_NAME = '${table}'
|
|
83
|
+
AND TABLE_SCHEMA = DATABASE()
|
|
84
|
+
AND COLUMN_NAME NOT IN (${IGNORE_FIELDS.join(',')})
|
|
85
|
+
ORDER BY ORDINAL_POSITION
|
|
86
|
+
`),
|
|
87
|
+
|
|
88
|
+
// 2. Foreign Keys
|
|
89
|
+
this.executeSQL<{ name: string, from_column: string, to_column: string, to_table: string }>(`
|
|
90
|
+
SELECT
|
|
91
|
+
CONSTRAINT_NAME AS name,
|
|
92
|
+
COLUMN_NAME AS from_column,
|
|
93
|
+
REFERENCED_COLUMN_NAME AS to_column,
|
|
94
|
+
REFERENCED_TABLE_NAME AS to_table
|
|
95
|
+
FROM information_schema.KEY_COLUMN_USAGE
|
|
96
|
+
WHERE TABLE_NAME = '${table}'
|
|
97
|
+
AND TABLE_SCHEMA = DATABASE()
|
|
98
|
+
AND REFERENCED_TABLE_NAME IS NOT NULL
|
|
99
|
+
`),
|
|
100
|
+
|
|
101
|
+
// 3. Indices
|
|
102
|
+
this.executeSQL<{ name: string, is_unique: number, columns: string }>(`
|
|
103
|
+
SELECT
|
|
104
|
+
stat.INDEX_NAME AS name,
|
|
105
|
+
stat.NON_UNIQUE = 0 AS is_unique,
|
|
106
|
+
GROUP_CONCAT(CONCAT(stat.COLUMN_NAME, ' ', stat.COLLATION, ' ') ORDER BY stat.SEQ_IN_INDEX) AS columns
|
|
107
|
+
FROM information_schema.STATISTICS stat
|
|
108
|
+
LEFT OUTER JOIN information_schema.TABLE_CONSTRAINTS AS tc
|
|
109
|
+
ON tc.CONSTRAINT_NAME = stat.INDEX_NAME
|
|
110
|
+
AND tc.TABLE_NAME = stat.TABLE_NAME
|
|
111
|
+
AND tc.TABLE_SCHEMA = stat.TABLE_SCHEMA
|
|
112
|
+
WHERE
|
|
113
|
+
stat.TABLE_NAME = '${table}'
|
|
114
|
+
AND stat.TABLE_SCHEMA = DATABASE()
|
|
115
|
+
AND tc.CONSTRAINT_TYPE IS NULL
|
|
116
|
+
AND stat.COLUMN_NAME NOT IN (${IGNORE_FIELDS.join(',')})
|
|
117
|
+
GROUP BY stat.INDEX_NAME, stat.NON_UNIQUE
|
|
118
|
+
`)
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
if (!columns.count) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
columns: columns.records.map(col => ({
|
|
127
|
+
...col,
|
|
128
|
+
type: col.type.toUpperCase(),
|
|
129
|
+
is_notnull: !!col.is_notnull
|
|
130
|
+
})),
|
|
131
|
+
foreignKeys: foreignKeys.records,
|
|
132
|
+
indices: indices.records.map(idx => ({
|
|
133
|
+
name: idx.name,
|
|
134
|
+
is_unique: !!idx.is_unique,
|
|
135
|
+
columns: idx.columns
|
|
136
|
+
.split(',')
|
|
137
|
+
.map(column => column.split(' '))
|
|
138
|
+
.map(([name, desc]) => ({ name, desc: desc === 'D' }))
|
|
139
|
+
}))
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
64
143
|
/**
|
|
65
144
|
* Create table, adding in specific engine options
|
|
66
145
|
*/
|
package/support/service.mysql.ts
CHANGED
|
@@ -8,7 +8,9 @@ export const service: ServiceDescriptor = {
|
|
|
8
8
|
image: `mysql:${version}`,
|
|
9
9
|
port: 3306,
|
|
10
10
|
env: {
|
|
11
|
-
|
|
11
|
+
MYSQL_RANDOM_ROOT_PASSWORD: '1',
|
|
12
|
+
MYSQL_PASSWORD: 'travetto',
|
|
13
|
+
MYSQL_USER: 'travetto',
|
|
12
14
|
MYSQL_DATABASE: 'app'
|
|
13
15
|
},
|
|
14
16
|
};
|