@itrocks/mysql-maintainer 0.2.5 → 0.2.7
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 +61 -241
- package/package.json +2 -2
- package/cjs/contextual-connection.d.ts +0 -16
- package/cjs/contextual-connection.js +0 -59
- package/cjs/mysql-maintainer.d.ts +0 -18
- package/cjs/mysql-maintainer.js +0 -106
- package/cjs/mysql.d.ts +0 -26
- package/cjs/mysql.js +0 -92
package/README.md
CHANGED
|
@@ -8,267 +8,87 @@
|
|
|
8
8
|
|
|
9
9
|
Reactively maintains database structure by updating schema and retrying on MySQL errors.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Connect this package to [`@itrocks/mysql`](https://github.com/itrocks-ts/mysql), then keep using your MySQL data
|
|
12
|
+
sources normally. Their database structure is maintained automatically, as operations need it, when persistent object
|
|
13
|
+
structures evolve. There are no migration files to write and no maintenance API to call.
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Use with the it.rocks framework
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
22
|
-
|
|
23
|
-
`@itrocks/mysql-maintainer` provides two main entry points:
|
|
24
|
-
|
|
25
|
-
- the `Mysql` class, a drop‑in replacement for `@itrocks/mysql` that
|
|
26
|
-
automatically keeps your MySQL/MariaDB schema in sync with your
|
|
27
|
-
TypeScript models while you work with data;
|
|
28
|
-
- the `MysqlMaintainer` class, a lower‑level helper you can call
|
|
29
|
-
directly when you want to create or update tables yourself.
|
|
30
|
-
|
|
31
|
-
In both cases, the package reacts to common MySQL errors
|
|
32
|
-
(`ER_BAD_FIELD_ERROR`, `ER_CANNOT_ADD_FOREIGN`, `ER_NO_SUCH_TABLE`) and
|
|
33
|
-
updates the database structure, then transparently retries the failed
|
|
34
|
-
query.
|
|
35
|
-
|
|
36
|
-
### Minimal example: automatic schema maintenance
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import { Mysql } from '@itrocks/mysql-maintainer'
|
|
40
|
-
import { Entity } from '@itrocks/storage'
|
|
41
|
-
|
|
42
|
-
class User extends Entity<User>
|
|
43
|
-
{
|
|
44
|
-
name = ''
|
|
45
|
-
email = ''
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function main() {
|
|
49
|
-
const mysql = new Mysql({
|
|
50
|
-
// Same connection options as @itrocks/mysql / mariadb
|
|
51
|
-
host: 'localhost',
|
|
52
|
-
user: 'root',
|
|
53
|
-
database: 'demo',
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
// On first call, mysql-maintainer will create or update the
|
|
57
|
-
// corresponding table based on the User class definition, then retry
|
|
58
|
-
// the insert.
|
|
59
|
-
await mysql.insert(new User().assign({ name: 'Alice', email: 'a@example.com' }))
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
You can continue using the `Mysql` instance as you would use
|
|
64
|
-
`@itrocks/mysql`: methods such as `read`, `search`, `update`, and
|
|
65
|
-
collection helpers are forwarded to the base implementation, while
|
|
66
|
-
`mysql-maintainer` keeps track of the involved entities and adjusts the
|
|
67
|
-
schema when needed.
|
|
68
|
-
|
|
69
|
-
### Example: maintaining join tables on collection access
|
|
70
|
-
|
|
71
|
-
When you read many‑to‑many relationships defined as collections on your
|
|
72
|
-
entities, `Mysql` automatically creates or updates the implicit join
|
|
73
|
-
table.
|
|
17
|
+
An application based on [`@itrocks/framework`](https://github.com/itrocks-ts/framework) needs the framework, the MySQL
|
|
18
|
+
data source, and this package:
|
|
74
19
|
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
import { Entity } from '@itrocks/storage'
|
|
78
|
-
|
|
79
|
-
class Tag extends Entity<Tag>
|
|
80
|
-
{
|
|
81
|
-
name = ''
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
class Article extends Entity<Article>
|
|
85
|
-
{
|
|
86
|
-
title = ''
|
|
87
|
-
|
|
88
|
-
// Many-to-many with Tag; the framework will map this to a join table
|
|
89
|
-
tags: Tag[] = []
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function loadArticleTags(id: number) {
|
|
93
|
-
const mysql = new Mysql({ database: 'demo' })
|
|
94
|
-
|
|
95
|
-
const article = await mysql.read(Article, id)
|
|
96
|
-
|
|
97
|
-
// If the join table does not exist yet or is out of date, it will be
|
|
98
|
-
// created/updated before the query is retried.
|
|
99
|
-
const tags = await mysql.readCollection(article, 'tags')
|
|
100
|
-
return tags
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Example: using MysqlMaintainer directly
|
|
105
|
-
|
|
106
|
-
If you want to control when schema updates happen (for instance during a
|
|
107
|
-
deployment step or a migration command), you can work with
|
|
108
|
-
`MysqlMaintainer` and a `mariadb` connection.
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { createConnection } from 'mariadb'
|
|
112
|
-
import { MysqlMaintainer } from '@itrocks/mysql-maintainer'
|
|
113
|
-
import { Entity } from '@itrocks/storage'
|
|
114
|
-
|
|
115
|
-
class Product extends Entity<Product>
|
|
116
|
-
{
|
|
117
|
-
name = ''
|
|
118
|
-
price = 0
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function ensureSchema() {
|
|
122
|
-
const connection = await createConnection({ database: 'demo' })
|
|
123
|
-
const schemaManager = new MysqlMaintainer(connection)
|
|
124
|
-
|
|
125
|
-
// Create or update the table corresponding to Product
|
|
126
|
-
await schemaManager.updateTable(Product)
|
|
127
|
-
|
|
128
|
-
await connection.end()
|
|
129
|
-
}
|
|
20
|
+
```bash
|
|
21
|
+
npm i @itrocks/framework @itrocks/mysql @itrocks/mysql-maintainer
|
|
130
22
|
```
|
|
131
23
|
|
|
132
|
-
|
|
24
|
+
Add the composition and the main MySQL data source to the project's `config.yaml`:
|
|
133
25
|
|
|
134
|
-
|
|
26
|
+
```yaml
|
|
27
|
+
compose:
|
|
28
|
+
'@itrocks/mysql:Mysql': '@itrocks/mysql-maintainer:Mysql'
|
|
135
29
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#### Properties
|
|
143
|
-
|
|
144
|
-
- `connection?: ContextualConnection` – the underlying `mariadb`
|
|
145
|
-
connection wrapped with additional context information. You normally
|
|
146
|
-
do not interact with it directly.
|
|
147
|
-
|
|
148
|
-
#### Methods
|
|
149
|
-
|
|
150
|
-
All methods have the same signature and return types as in
|
|
151
|
-
`@itrocks/mysql.Mysql`; the extra behavior is that each call pushes the
|
|
152
|
-
relevant entity type or instance to an internal context stack before the
|
|
153
|
-
query, and pops it afterwards.
|
|
154
|
-
|
|
155
|
-
- `async connect(): Promise<ContextualConnection>`
|
|
156
|
-
|
|
157
|
-
Opens the connection and installs the contextual behavior so that
|
|
158
|
-
errors can trigger schema maintenance.
|
|
159
|
-
|
|
160
|
-
- `async insert<T extends object>(object: T): Promise<Entity<T>>`
|
|
161
|
-
|
|
162
|
-
Inserts a new row for `object`. If a “table does not exist” or
|
|
163
|
-
“column not found / foreign key failed” error occurs, the
|
|
164
|
-
corresponding table (and, for collections, the join table) is created
|
|
165
|
-
or updated and the query is retried.
|
|
166
|
-
|
|
167
|
-
- `async update<T extends object>(object: Entity<T>): Promise<Entity<T>>`
|
|
168
|
-
|
|
169
|
-
Updates an existing row; schema maintenance/automatic retry applies in
|
|
170
|
-
the same way as for `insert`.
|
|
171
|
-
|
|
172
|
-
- `async deleteId<T extends object>(type: ObjectOrType<T>, id: any, property?: KeyOf<Entity<T>>): Promise<void>`
|
|
173
|
-
|
|
174
|
-
Deletes a row by its identifier; the entity `type` is used as context
|
|
175
|
-
for potential schema updates.
|
|
176
|
-
|
|
177
|
-
- `async deleteRelatedId<T extends Entity>(object: T, property: KeyOf<T>, id: Identifier): Promise<void>`
|
|
178
|
-
|
|
179
|
-
Deletes a related entity (for example from a collection association).
|
|
180
|
-
|
|
181
|
-
- `async read<T extends object>(type: Type<T>, id: Identifier): Promise<Entity<T> | undefined>`
|
|
182
|
-
|
|
183
|
-
Reads a single entity instance by identifier.
|
|
184
|
-
|
|
185
|
-
- `async readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]>`
|
|
186
|
-
|
|
187
|
-
Reads several entities by their identifiers.
|
|
188
|
-
|
|
189
|
-
- `async readCollection<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Entity<PT>[]>`
|
|
190
|
-
|
|
191
|
-
Reads a collection relationship (for example `article.tags`). The
|
|
192
|
-
context includes both the owning object and the related type so that
|
|
193
|
-
the implicit join table can be created or updated if necessary.
|
|
194
|
-
|
|
195
|
-
- `async readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Identifier[]>`
|
|
196
|
-
|
|
197
|
-
Same as `readCollection`, but only retrieves identifiers.
|
|
198
|
-
|
|
199
|
-
- `async search<T extends object>(type: Type<T>, search?: SearchType<T>, options?: Options): Promise<Entity<T>[]>`
|
|
200
|
-
|
|
201
|
-
Searches entities; the entity `type` becomes the context for schema
|
|
202
|
-
maintenance.
|
|
203
|
-
|
|
204
|
-
### `class MysqlMaintainer`
|
|
205
|
-
|
|
206
|
-
Low‑level utility that knows how to translate your TypeScript entity
|
|
207
|
-
definitions (via reflection and schema packages) into MySQL tables and
|
|
208
|
-
how to react to common schema‑related errors.
|
|
209
|
-
|
|
210
|
-
#### Constructor
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
const maintainer = new MysqlMaintainer(connection)
|
|
30
|
+
dataSource:
|
|
31
|
+
engine: '@itrocks/mysql'
|
|
32
|
+
host: localhost
|
|
33
|
+
user: application
|
|
34
|
+
password: secret
|
|
35
|
+
database: application
|
|
214
36
|
```
|
|
215
37
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
#### Methods
|
|
38
|
+
The `compose` entry enriches `@itrocks/mysql` with the automatic maintenance behaviour. It uses the standard
|
|
39
|
+
[`@itrocks/compose`](https://github.com/itrocks-ts/compose) mechanism for enriching one module with another module's
|
|
40
|
+
features.
|
|
220
41
|
|
|
221
|
-
|
|
42
|
+
The `dataSource` entry above configures the project's main data source. You can instead create other MySQL data sources
|
|
43
|
+
as described in the [`@itrocks/mysql` documentation](https://github.com/itrocks-ts/mysql). Every data source based on
|
|
44
|
+
the composed `@itrocks/mysql` implementation is maintained automatically.
|
|
222
45
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
has been created.
|
|
46
|
+
That is the complete setup. Keep changing and using persistent object classes through the usual it.rocks storage
|
|
47
|
+
operations; the required database changes are applied when an operation first needs them.
|
|
226
48
|
|
|
227
|
-
|
|
49
|
+
## Use outside the it.rocks framework
|
|
228
50
|
|
|
229
|
-
|
|
230
|
-
table name can be resolved for the type.
|
|
51
|
+
The same integration principle can be used without `@itrocks/framework`:
|
|
231
52
|
|
|
232
|
-
|
|
53
|
+
1. Install [`@itrocks/mysql`](https://github.com/itrocks-ts/mysql) and a mixin-based composition system such as
|
|
54
|
+
[`@itrocks/compose`](https://github.com/itrocks-ts/compose). A wrapper implementing the same API as `@itrocks/mysql`
|
|
55
|
+
can be used instead.
|
|
56
|
+
2. Create the required `@itrocks/mysql` data sources as described in its
|
|
57
|
+
[documentation](https://github.com/itrocks-ts/mysql). This package does not change how data sources are configured.
|
|
58
|
+
3. Install `@itrocks/mysql-maintainer` in the project.
|
|
59
|
+
4. Use the chosen composition system to enrich the `Mysql` implementation from `@itrocks/mysql` with the behaviour
|
|
60
|
+
supplied by `@itrocks/mysql-maintainer`. Refer to the
|
|
61
|
+
[`@itrocks/compose` documentation](https://github.com/itrocks-ts/compose) when using that package.
|
|
62
|
+
5. Use the MySQL data sources normally. Their operations now trigger automatic maintenance when the current database
|
|
63
|
+
structure no longer supports the persistent object model.
|
|
233
64
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
the types is not mapped to a table.
|
|
65
|
+
The application integrator is responsible for performing the composition early enough and according to the chosen
|
|
66
|
+
composition system's loading rules.
|
|
237
67
|
|
|
238
|
-
|
|
68
|
+
## How it works
|
|
239
69
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
has been handled and the caller should retry the query, `false`
|
|
244
|
-
otherwise.
|
|
70
|
+
The package enriches the usual `Mysql` operations with the context required to identify the persistent classes involved.
|
|
71
|
+
It intercepts MySQL errors that indicate a missing or outdated table, column, or foreign key, derives the expected
|
|
72
|
+
schema from the object model, applies the required structural changes, and retries the original operation.
|
|
245
73
|
|
|
246
|
-
|
|
74
|
+
For example, a read follows this simplified sequence:
|
|
247
75
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
76
|
+
1. The application reads an object through a MySQL data source as usual.
|
|
77
|
+
2. The enriched data source records the class being read and executes the SQL query.
|
|
78
|
+
3. If MySQL reports a supported structure error, the maintainer compares the database with the schema derived from
|
|
79
|
+
that class and updates the affected table or relationship table.
|
|
80
|
+
4. The data source retries the same query and returns the read result to the application.
|
|
253
81
|
|
|
254
|
-
|
|
82
|
+
The exported classes that implement context tracking, error handling, schema updates, and retries are internal
|
|
83
|
+
mechanics of this integration. They are not a separate API that an application needs to call.
|
|
255
84
|
|
|
256
|
-
|
|
257
|
-
that should exist according to the entity `type`, then applies the
|
|
258
|
-
required SQL changes. Returns `true` when a change has been applied,
|
|
259
|
-
`false` when the schema was already up to date.
|
|
85
|
+
## Persistent model requirements
|
|
260
86
|
|
|
261
|
-
|
|
87
|
+
Automatic maintenance derives database structure from business classes and their reflection metadata. Those classes
|
|
88
|
+
must follow the it.rocks persistent object conventions, including the decorators and metadata used by its ORM packages.
|
|
262
89
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- Applications with many small deployments where you prefer incremental
|
|
269
|
-
schema evolution handled by the application itself.
|
|
270
|
-
- Projects using the `@itrocks/storage` / `@itrocks/mysql` stack that
|
|
271
|
-
want schema maintenance without writing migration scripts.
|
|
272
|
-
- Custom migration tools or CLI scripts built on top of
|
|
273
|
-
`MysqlMaintainer` to validate and update schema before a new version
|
|
274
|
-
of the application is started.
|
|
90
|
+
The current implementation is strongly coupled to the it.rocks ORM ecosystem. Its
|
|
91
|
+
[`npm dependencies`](https://www.npmjs.com/package/@itrocks/mysql-maintainer?activeTab=dependencies) show the
|
|
92
|
+
reflection, storage, schema conversion, and schema diff modules involved. Outside a full it.rocks application, the
|
|
93
|
+
integrator must provide a compatible model and metadata environment in addition to the MySQL and composition
|
|
94
|
+
integrations described above.
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"description": "Reactively maintains database structure by updating schema and retrying on MySQL errors",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"typescript": "^
|
|
20
|
+
"typescript": "^7.0"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=24"
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "tsc"
|
|
53
53
|
},
|
|
54
|
-
"version": "0.2.
|
|
54
|
+
"version": "0.2.7"
|
|
55
55
|
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { ObjectOrType } from '@itrocks/class-type';
|
|
2
|
-
import { Connection } from 'mariadb';
|
|
3
|
-
import { QueryOptions } from 'mariadb-share';
|
|
4
|
-
import { SqlError } from 'mariadb-share';
|
|
5
|
-
export type Context = ObjectOrType | ObjectOrType[];
|
|
6
|
-
export declare class Contextual implements Partial<Connection> {
|
|
7
|
-
[x: number]: (() => Promise<void>) | undefined;
|
|
8
|
-
contexts: Context[];
|
|
9
|
-
errorCount: Record<string, Record<string, Array<number>>>;
|
|
10
|
-
superQuery: <T = any>(sql: string | QueryOptions, values?: any) => Promise<T>;
|
|
11
|
-
applyTo(connection: ContextualConnection): Promise<ContextualConnection>;
|
|
12
|
-
incrementErrorCount(sql: string, error: any): this is Connection;
|
|
13
|
-
isSqlError(error: any): error is SqlError;
|
|
14
|
-
query<T = any>(sql: string | QueryOptions, values?: any): Promise<T>;
|
|
15
|
-
}
|
|
16
|
-
export type ContextualConnection = Connection & Contextual;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Contextual = void 0;
|
|
4
|
-
const mysql_maintainer_1 = require("./mysql-maintainer");
|
|
5
|
-
const mysql_maintainer_2 = require("./mysql-maintainer");
|
|
6
|
-
const MANAGED_ERROR_CODES = ['ER_BAD_FIELD_ERROR', 'ER_CANNOT_ADD_FOREIGN', 'ER_NO_SUCH_TABLE'];
|
|
7
|
-
class Contextual {
|
|
8
|
-
contexts = [];
|
|
9
|
-
errorCount = {};
|
|
10
|
-
superQuery = () => new Promise(() => { });
|
|
11
|
-
async applyTo(connection) {
|
|
12
|
-
connection.contexts = [];
|
|
13
|
-
connection.errorCount = {};
|
|
14
|
-
connection.incrementErrorCount = Contextual.prototype.incrementErrorCount;
|
|
15
|
-
connection.isSqlError = Contextual.prototype.isSqlError;
|
|
16
|
-
connection.superQuery = connection.query;
|
|
17
|
-
connection.query = Contextual.prototype.query;
|
|
18
|
-
return connection;
|
|
19
|
-
}
|
|
20
|
-
incrementErrorCount(sql, error) {
|
|
21
|
-
if (!this.errorCount[sql]) {
|
|
22
|
-
this.errorCount[sql] = {};
|
|
23
|
-
}
|
|
24
|
-
if (!this.errorCount[sql][error.message]) {
|
|
25
|
-
this.errorCount[sql][error.message] = [];
|
|
26
|
-
}
|
|
27
|
-
this.errorCount[sql][error.message].push(new Date().getTime());
|
|
28
|
-
console.log('incremented', this.errorCount[sql][error.message].length, ':', sql, error.message);
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
isSqlError(error) {
|
|
32
|
-
return (typeof error === 'object') && !!error.code;
|
|
33
|
-
}
|
|
34
|
-
async query(sql, values) {
|
|
35
|
-
try {
|
|
36
|
-
if (mysql_maintainer_1.DEBUG)
|
|
37
|
-
console.log(sql, values);
|
|
38
|
-
return await this.superQuery(sql, values);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
if (!this.isSqlError(error) || !MANAGED_ERROR_CODES.includes(error.code)) {
|
|
42
|
-
if (mysql_maintainer_1.DEBUG)
|
|
43
|
-
console.log('MAINTAINER: throw', error);
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
sql = (typeof sql === 'object') ? sql.sql : sql;
|
|
47
|
-
if (this.incrementErrorCount(sql, error)
|
|
48
|
-
&& (this.errorCount[sql][error.message].length < 5)
|
|
49
|
-
&& await new mysql_maintainer_2.MysqlMaintainer(this).manageError(error, this.contexts[this.contexts.length - 1], sql, values)) {
|
|
50
|
-
return this.query(sql, values);
|
|
51
|
-
}
|
|
52
|
-
if (mysql_maintainer_1.DEBUG)
|
|
53
|
-
console.log('MAINTAINER: throw (2)', error);
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
exports.Contextual = Contextual;
|
|
59
|
-
//# sourceMappingURL=contextual-connection.js.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ObjectOrType } from '@itrocks/class-type';
|
|
2
|
-
import { Type } from '@itrocks/class-type';
|
|
3
|
-
import { Connection } from 'mariadb';
|
|
4
|
-
import { QueryOptions } from 'mariadb-share';
|
|
5
|
-
import { SqlError } from 'mariadb-share';
|
|
6
|
-
import { Context } from './contextual-connection';
|
|
7
|
-
export { Mysql } from './mysql';
|
|
8
|
-
export declare const DEBUG = false;
|
|
9
|
-
export declare class MysqlMaintainer {
|
|
10
|
-
connection: Connection;
|
|
11
|
-
constructor(connection: Connection);
|
|
12
|
-
createImplicitTable(type1: ObjectOrType, type2: ObjectOrType): Promise<boolean>;
|
|
13
|
-
createTable(type: Type): Promise<boolean>;
|
|
14
|
-
implicitTableName(type1: ObjectOrType, type2: ObjectOrType): string;
|
|
15
|
-
manageError(error: SqlError, context: Context, _sql: string | QueryOptions, _values: any[]): Promise<boolean>;
|
|
16
|
-
updateContextTables(context: Context, tableName?: string): Promise<boolean>;
|
|
17
|
-
updateTable(type: Type): Promise<boolean>;
|
|
18
|
-
}
|
package/cjs/mysql-maintainer.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MysqlMaintainer = exports.DEBUG = exports.Mysql = void 0;
|
|
4
|
-
const class_type_1 = require("@itrocks/class-type");
|
|
5
|
-
const mysql_1 = require("@itrocks/mysql");
|
|
6
|
-
const mysql_to_schema_1 = require("@itrocks/mysql-to-schema");
|
|
7
|
-
const reflect_to_schema_1 = require("@itrocks/reflect-to-schema");
|
|
8
|
-
const schema_diff_1 = require("@itrocks/schema-diff");
|
|
9
|
-
const schema_diff_mysql_1 = require("@itrocks/schema-diff-mysql");
|
|
10
|
-
const schema_to_mysql_1 = require("@itrocks/schema-to-mysql");
|
|
11
|
-
const store_1 = require("@itrocks/store");
|
|
12
|
-
var mysql_2 = require("./mysql");
|
|
13
|
-
Object.defineProperty(exports, "Mysql", { enumerable: true, get: function () { return mysql_2.Mysql; } });
|
|
14
|
-
exports.DEBUG = false;
|
|
15
|
-
const DELETION = false;
|
|
16
|
-
class MysqlMaintainer {
|
|
17
|
-
connection;
|
|
18
|
-
constructor(connection) {
|
|
19
|
-
this.connection = connection;
|
|
20
|
-
}
|
|
21
|
-
async createImplicitTable(type1, type2) {
|
|
22
|
-
const joinTable = this.implicitTableName(type1, type2);
|
|
23
|
-
const table1 = (0, store_1.storeOf)(type1);
|
|
24
|
-
const table2 = (0, store_1.storeOf)(type2);
|
|
25
|
-
const query = `CREATE TABLE \`${joinTable}\` (
|
|
26
|
-
${table1}_id int unsigned NOT NULL,
|
|
27
|
-
${table2}_id int unsigned NOT NULL,
|
|
28
|
-
PRIMARY KEY (${table1}_id, ${table2}_id),
|
|
29
|
-
CONSTRAINT \`${joinTable}.${table1}_id\` FOREIGN KEY (${table1}_id) REFERENCES \`${table1}\` (id) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
30
|
-
CONSTRAINT \`${joinTable}.${table2}_id\` FOREIGN KEY (${table2}_id) REFERENCES \`${table2}\` (id) ON DELETE CASCADE ON UPDATE CASCADE
|
|
31
|
-
)`;
|
|
32
|
-
await this.connection.query(query);
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
async createTable(type) {
|
|
36
|
-
const tableName = (0, store_1.storeOf)(type);
|
|
37
|
-
if (!tableName) {
|
|
38
|
-
throw 'No table name for type';
|
|
39
|
-
}
|
|
40
|
-
const tableSchema = new reflect_to_schema_1.ReflectToTable().convert(type);
|
|
41
|
-
const schemaToMysql = new schema_to_mysql_1.SchemaToMysql();
|
|
42
|
-
const sql = schemaToMysql.sql(tableSchema);
|
|
43
|
-
await this.connection.query(sql);
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
implicitTableName(type1, type2) {
|
|
47
|
-
const table1 = (0, store_1.storeOf)(type1);
|
|
48
|
-
const table2 = (0, store_1.storeOf)(type2);
|
|
49
|
-
if (!table1 || !table2) {
|
|
50
|
-
throw 'Collection objects are not stored';
|
|
51
|
-
}
|
|
52
|
-
return (0, mysql_1.joinTableName)(table1, table2);
|
|
53
|
-
}
|
|
54
|
-
async manageError(error, context, _sql, _values) {
|
|
55
|
-
if (exports.DEBUG)
|
|
56
|
-
console.log('MAINTAINER: manageError', error);
|
|
57
|
-
switch (error.code) {
|
|
58
|
-
case 'ER_BAD_FIELD_ERROR':
|
|
59
|
-
case 'ER_CANNOT_ADD_FOREIGN':
|
|
60
|
-
return this.updateContextTables(context);
|
|
61
|
-
case 'ER_NO_SUCH_TABLE':
|
|
62
|
-
const tableName = /Table '.*?\.(.*?)'/.exec(error.message)?.[1];
|
|
63
|
-
return this.updateContextTables(context, tableName);
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
async updateContextTables(context, tableName) {
|
|
68
|
-
let result = false;
|
|
69
|
-
const contexts = Array.isArray(context) ? context : [context];
|
|
70
|
-
for (const context of contexts) {
|
|
71
|
-
const type = (0, class_type_1.typeOf)(context);
|
|
72
|
-
const tableName = (0, store_1.storeOf)(type);
|
|
73
|
-
if (!tableName) {
|
|
74
|
-
throw 'No table name for type';
|
|
75
|
-
}
|
|
76
|
-
const exists = (await this.connection.query('SHOW TABLES LIKE ?', tableName));
|
|
77
|
-
result ||= await (exists.length ? this.updateTable(type) : this.createTable(type));
|
|
78
|
-
}
|
|
79
|
-
if ((contexts.length === 2) && (this.implicitTableName(contexts[0], contexts[1]) === tableName)) {
|
|
80
|
-
result ||= await this.createImplicitTable(contexts[0], contexts[1]);
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
async updateTable(type) {
|
|
85
|
-
const tableName = (0, store_1.storeOf)(type);
|
|
86
|
-
if (!tableName) {
|
|
87
|
-
throw 'No table name for type';
|
|
88
|
-
}
|
|
89
|
-
const classTable = new reflect_to_schema_1.ReflectToTable().convert(type);
|
|
90
|
-
new mysql_to_schema_1.MysqlToTable(this.connection).normalize(classTable);
|
|
91
|
-
const mysqlTable = await ((new mysql_to_schema_1.MysqlToTable(this.connection)).convert(tableName));
|
|
92
|
-
const schemaDiff = new schema_diff_1.TableDiff(mysqlTable, classTable);
|
|
93
|
-
if (!schemaDiff.additions.length
|
|
94
|
-
&& !schemaDiff.changes.length
|
|
95
|
-
&& (!schemaDiff.deletions.length || !DELETION)
|
|
96
|
-
&& !schemaDiff.tableChanges()) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
const schemaDiffMysql = new schema_diff_mysql_1.SchemaDiffMysql();
|
|
100
|
-
const sql = schemaDiffMysql.sql(schemaDiff, DELETION);
|
|
101
|
-
await this.connection.query(sql);
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
exports.MysqlMaintainer = MysqlMaintainer;
|
|
106
|
-
//# sourceMappingURL=mysql-maintainer.js.map
|
package/cjs/mysql.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { ObjectOrType } from '@itrocks/class-type';
|
|
2
|
-
import { Type } from '@itrocks/class-type';
|
|
3
|
-
import { Mysql as M } from '@itrocks/mysql';
|
|
4
|
-
import { Entity } from '@itrocks/storage';
|
|
5
|
-
import { Identifier } from '@itrocks/storage';
|
|
6
|
-
import { MayEntity } from '@itrocks/storage';
|
|
7
|
-
import { Options } from '@itrocks/storage';
|
|
8
|
-
import { SearchType } from '@itrocks/storage';
|
|
9
|
-
import { ContextualConnection } from './contextual-connection';
|
|
10
|
-
export declare class Mysql extends M {
|
|
11
|
-
connection?: ContextualConnection;
|
|
12
|
-
connect(): Promise<ContextualConnection>;
|
|
13
|
-
deleteId<T extends object>(type: ObjectOrType<T>, id: any, property?: keyof Entity<T>): Promise<void>;
|
|
14
|
-
deleteRelatedId<T extends Entity>(object: T, property: keyof T, id: Identifier): Promise<void>;
|
|
15
|
-
insert<T extends object>(object: T): Promise<Entity<T>>;
|
|
16
|
-
insertRelatedId<T extends Entity>(object: T, property: keyof T, id: Identifier): Promise<void>;
|
|
17
|
-
read<T extends object>(type: Type<T>, id: Identifier): Promise<Entity<T>>;
|
|
18
|
-
readCollection<T extends object, PT extends object>(object: Entity<T>, property: keyof T, type?: Type<PT>): Promise<Awaited<PT & {
|
|
19
|
-
id: Identifier;
|
|
20
|
-
}>[]>;
|
|
21
|
-
readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: keyof T, type?: Type<PT>): Promise<Identifier[]>;
|
|
22
|
-
readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]>;
|
|
23
|
-
save<T extends object>(object: MayEntity<T>): Promise<Entity<T>>;
|
|
24
|
-
search<T extends object>(type: Type<T>, search?: SearchType<T>, options?: Options): Promise<Entity<T>[]>;
|
|
25
|
-
update<T extends object>(object: Entity<T>): Promise<Entity<T>>;
|
|
26
|
-
}
|
package/cjs/mysql.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Mysql = void 0;
|
|
4
|
-
const mysql_1 = require("@itrocks/mysql");
|
|
5
|
-
const reflect_1 = require("@itrocks/reflect");
|
|
6
|
-
const contextual_connection_1 = require("./contextual-connection");
|
|
7
|
-
class Mysql extends mysql_1.Mysql {
|
|
8
|
-
async connect() {
|
|
9
|
-
return contextual_connection_1.Contextual.prototype.applyTo(await super.connect());
|
|
10
|
-
}
|
|
11
|
-
async deleteId(type, id, property = 'id') {
|
|
12
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
13
|
-
contexts.push(type);
|
|
14
|
-
const result = await super.deleteId(type, id, property);
|
|
15
|
-
contexts.pop();
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
async deleteRelatedId(object, property, id) {
|
|
19
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
20
|
-
contexts.push(object);
|
|
21
|
-
const result = await super.deleteRelatedId(object, property, id);
|
|
22
|
-
contexts.pop();
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
25
|
-
async insert(object) {
|
|
26
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
27
|
-
contexts.push(object);
|
|
28
|
-
const result = await super.insert(object);
|
|
29
|
-
contexts.pop();
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
32
|
-
async insertRelatedId(object, property, id) {
|
|
33
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
34
|
-
contexts.push(object);
|
|
35
|
-
const result = await super.insertRelatedId(object, property, id);
|
|
36
|
-
contexts.pop();
|
|
37
|
-
return result;
|
|
38
|
-
}
|
|
39
|
-
async read(type, id) {
|
|
40
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
41
|
-
contexts.push(type);
|
|
42
|
-
const result = await super.read(type, id);
|
|
43
|
-
contexts.pop();
|
|
44
|
-
return result;
|
|
45
|
-
}
|
|
46
|
-
async readCollection(object, property, type) {
|
|
47
|
-
type ??= new reflect_1.ReflectProperty(object, property).collectionType.elementType.type;
|
|
48
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
49
|
-
contexts.push([object, type]);
|
|
50
|
-
const result = await super.readCollection(object, property, type);
|
|
51
|
-
contexts.pop();
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
54
|
-
async readCollectionIds(object, property, type) {
|
|
55
|
-
type ??= new reflect_1.ReflectProperty(object, property).collectionType.elementType.type;
|
|
56
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
57
|
-
contexts.push([object, type]);
|
|
58
|
-
const result = await super.readCollectionIds(object, property, type);
|
|
59
|
-
contexts.pop();
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
async readMultiple(type, ids) {
|
|
63
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
64
|
-
contexts.push(type);
|
|
65
|
-
const result = await super.readMultiple(type, ids);
|
|
66
|
-
contexts.pop();
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
async save(object) {
|
|
70
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
71
|
-
contexts.push(object);
|
|
72
|
-
const result = await super.save(object);
|
|
73
|
-
contexts.pop();
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
async search(type, search = {}, options) {
|
|
77
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
78
|
-
contexts.push(type);
|
|
79
|
-
const result = await super.search(type, search, options);
|
|
80
|
-
contexts.pop();
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
async update(object) {
|
|
84
|
-
const contexts = (this.connection ?? await this.connect()).contexts;
|
|
85
|
-
contexts.push(object);
|
|
86
|
-
const result = await super.update(object);
|
|
87
|
-
contexts.pop();
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
exports.Mysql = Mysql;
|
|
92
|
-
//# sourceMappingURL=mysql.js.map
|