@travetto/model-mysql 3.0.0-rc.9 → 3.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 +4 -0
- package/package.json +8 -8
- package/src/connection.ts +21 -7
- package/src/dialect.ts +20 -8
- package/support/service.mysql.ts +3 -3
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
**Install: @travetto/model-mysql**
|
|
7
7
|
```bash
|
|
8
8
|
npm install @travetto/model-mysql
|
|
9
|
+
|
|
10
|
+
# or
|
|
11
|
+
|
|
12
|
+
yarn add @travetto/model-mysql
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
This module provides a [MySQL](https://www.mysql.com/)-based implementation for the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module. This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [SQL](https://en.wikipedia.org/wiki/SQL) databases. In development mode, the [SQLModelService](https://github.com/travetto/travetto/tree/main/module/model-sql/src/service.ts#L38) will also modify the database schema in real time to minimize impact to development.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-mysql",
|
|
3
|
-
"version": "3.0.0
|
|
3
|
+
"version": "3.0.0",
|
|
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,16 +27,16 @@
|
|
|
27
27
|
"directory": "module/model-mysql"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/config": "^3.0.0
|
|
31
|
-
"@travetto/context": "^3.0.0
|
|
32
|
-
"@travetto/model": "^3.0.0
|
|
33
|
-
"@travetto/model-query": "^3.0.0
|
|
34
|
-
"@travetto/model-sql": "^3.0.0
|
|
30
|
+
"@travetto/config": "^3.0.0",
|
|
31
|
+
"@travetto/context": "^3.0.0",
|
|
32
|
+
"@travetto/model": "^3.0.0",
|
|
33
|
+
"@travetto/model-query": "^3.0.0",
|
|
34
|
+
"@travetto/model-sql": "^3.0.0",
|
|
35
35
|
"@types/mysql": "^2.15.21",
|
|
36
|
-
"
|
|
36
|
+
"mysql2": "^3.1.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@travetto/command": "^3.0.0
|
|
39
|
+
"@travetto/command": "^3.0.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@travetto/command": {
|
package/src/connection.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import mysql from '
|
|
1
|
+
import mysql, { OkPacket, ResultSetHeader } from 'mysql2';
|
|
2
2
|
|
|
3
3
|
import { ShutdownManager } from '@travetto/base';
|
|
4
4
|
import { AsyncContext } from '@travetto/context';
|
|
5
5
|
import { ExistsError } from '@travetto/model';
|
|
6
6
|
import { Connection, SQLModelConfig } from '@travetto/model-sql';
|
|
7
7
|
|
|
8
|
+
function isSimplePacket(o: unknown): o is OkPacket | ResultSetHeader {
|
|
9
|
+
return o !== null && o !== undefined && typeof o === 'object' && 'constructor' in o && (
|
|
10
|
+
o.constructor.name === 'OkPacket' || o.constructor.name === 'ResultSetHeader'
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* Connection support for mysql
|
|
10
16
|
*/
|
|
@@ -28,7 +34,7 @@ export class MySQLConnection extends Connection<mysql.PoolConnection> {
|
|
|
28
34
|
database: this.#config.database,
|
|
29
35
|
host: this.#config.host,
|
|
30
36
|
port: this.#config.port,
|
|
31
|
-
timezone: '
|
|
37
|
+
timezone: '+00:00',
|
|
32
38
|
typeCast: this.typeCast.bind(this),
|
|
33
39
|
...(this.#config.options || {})
|
|
34
40
|
});
|
|
@@ -40,9 +46,9 @@ export class MySQLConnection extends Connection<mysql.PoolConnection> {
|
|
|
40
46
|
/**
|
|
41
47
|
* Support some basic type support for JSON data
|
|
42
48
|
*/
|
|
43
|
-
typeCast(field:
|
|
49
|
+
typeCast(field: unknown, next: () => unknown): unknown {
|
|
44
50
|
const res = next();
|
|
45
|
-
if (typeof res === 'string' && (field.type === 'JSON' || field.type === 'BLOB')) {
|
|
51
|
+
if (typeof res === 'string' && (field && typeof field === 'object' && 'type' in field) && (field.type === 'JSON' || field.type === 'BLOB')) {
|
|
46
52
|
if (res.charAt(0) === '{' && res.charAt(res.length - 1) === '}') {
|
|
47
53
|
try {
|
|
48
54
|
return JSON.parse(res);
|
|
@@ -58,14 +64,22 @@ export class MySQLConnection extends Connection<mysql.PoolConnection> {
|
|
|
58
64
|
conn.query(query, (err, results, fields) => {
|
|
59
65
|
if (err) {
|
|
60
66
|
console.debug('Failed query', { error: err, query });
|
|
61
|
-
if (err.message.startsWith('
|
|
67
|
+
if (err.message.startsWith('Duplicate entry')) {
|
|
62
68
|
rej(new ExistsError('query', query));
|
|
63
69
|
} else {
|
|
64
70
|
rej(err);
|
|
65
71
|
}
|
|
66
72
|
} else {
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
if (isSimplePacket(results)) {
|
|
74
|
+
return res({ records: [], count: results.affectedRows });
|
|
75
|
+
} else {
|
|
76
|
+
if (isSimplePacket(results[0])) {
|
|
77
|
+
return res({ records: [], count: results[0].affectedRows });
|
|
78
|
+
}
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
80
|
+
const records: T[] = [...results].map(v => ({ ...v }) as T);
|
|
81
|
+
return res({ records, count: records.length });
|
|
82
|
+
}
|
|
69
83
|
}
|
|
70
84
|
});
|
|
71
85
|
});
|
package/src/dialect.ts
CHANGED
|
@@ -16,18 +16,12 @@ import { MySQLConnection } from './connection';
|
|
|
16
16
|
export class MySQLDialect extends SQLDialect {
|
|
17
17
|
|
|
18
18
|
conn: MySQLConnection;
|
|
19
|
-
tablePostfix =
|
|
19
|
+
tablePostfix = 'COLLATE=utf8mb4_bin ENGINE=InnoDB';
|
|
20
20
|
|
|
21
21
|
constructor(context: AsyncContext, public config: SQLModelConfig) {
|
|
22
22
|
super(config.namespace);
|
|
23
23
|
this.conn = new MySQLConnection(context, config);
|
|
24
24
|
|
|
25
|
-
// Customer operators
|
|
26
|
-
Object.assign(this.SQL_OPS, {
|
|
27
|
-
$regex: 'REGEXP BINARY',
|
|
28
|
-
$iregex: 'REGEXP'
|
|
29
|
-
});
|
|
30
|
-
|
|
31
25
|
// Custom types
|
|
32
26
|
Object.assign(this.COLUMN_TYPES, {
|
|
33
27
|
TIMESTAMP: 'DATETIME(3)',
|
|
@@ -35,7 +29,6 @@ export class MySQLDialect extends SQLDialect {
|
|
|
35
29
|
});
|
|
36
30
|
|
|
37
31
|
// Word boundary
|
|
38
|
-
this.regexWordBoundary = '([[:<:]]|[[:>:]])';
|
|
39
32
|
// Field maxlength
|
|
40
33
|
this.idField.minlength = this.idField.maxlength = { n: this.KEY_LEN };
|
|
41
34
|
|
|
@@ -44,6 +37,25 @@ export class MySQLDialect extends SQLDialect {
|
|
|
44
37
|
*/
|
|
45
38
|
if (/^5[.][56]/.test(this.config.version)) {
|
|
46
39
|
this.DEFAULT_STRING_LEN = 191; // Mysql limitation with utf8 and keys
|
|
40
|
+
} else {
|
|
41
|
+
this.DEFAULT_STRING_LEN = 3072 / 4 - 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (/^5[.].*/.test(this.config.version)) {
|
|
45
|
+
// Customer operators
|
|
46
|
+
Object.assign(this.SQL_OPS, {
|
|
47
|
+
$regex: 'REGEXP BINARY',
|
|
48
|
+
$iregex: 'REGEXP'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
this.regexWordBoundary = '([[:<:]]|[[:>:]])';
|
|
52
|
+
} else {
|
|
53
|
+
// Customer operators
|
|
54
|
+
Object.assign(this.SQL_OPS, {
|
|
55
|
+
$regex: 'REGEXP',
|
|
56
|
+
});
|
|
57
|
+
// Double escape
|
|
58
|
+
this.regexWordBoundary = '\\\\b';
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
|
package/support/service.mysql.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Env } from '@travetto/base';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CommandService } from '@travetto/command';
|
|
3
3
|
|
|
4
|
-
const version = Env.get('MYSQL_VERSION', '
|
|
4
|
+
const version = Env.get('MYSQL_VERSION', '8.0');
|
|
5
5
|
|
|
6
|
-
export const service:
|
|
6
|
+
export const service: CommandService = {
|
|
7
7
|
name: 'mysql',
|
|
8
8
|
version,
|
|
9
9
|
image: `mysql:${version}`,
|