meadow 2.0.17 → 2.0.18
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 +174 -112
- package/docs/README.md +276 -0
- package/docs/_sidebar.md +38 -0
- package/docs/providers/README.md +253 -0
- package/docs/providers/alasql.md +271 -0
- package/docs/providers/mssql.md +296 -0
- package/docs/providers/mysql.md +260 -0
- package/docs/providers/sqlite.md +173 -0
- package/docs/query/README.md +175 -0
- package/docs/query/count.md +228 -0
- package/docs/query/create.md +226 -0
- package/docs/query/delete.md +264 -0
- package/docs/query/read.md +350 -0
- package/docs/query/update.md +250 -0
- package/docs/schema/README.md +408 -0
- package/package.json +16 -3
- package/scripts/mssql-test-db.sh +111 -0
- package/scripts/mysql-test-db.sh +108 -0
- package/source/Meadow.js +1 -0
- package/source/providers/Meadow-Provider-SQLite.js +235 -155
- package/test/Meadow-Provider-SQLite-AnimalReadQuery.sql +5 -0
- package/test/Meadow-Provider-SQLite_tests.js +931 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# MSSQL Provider
|
|
2
|
+
|
|
3
|
+
> Enterprise-grade SQL Server integration with prepared statements and automatic type mapping
|
|
4
|
+
|
|
5
|
+
The MSSQL provider connects Meadow to Microsoft SQL Server via the [mssql](https://github.com/tediousjs/node-mssql) library. It uses prepared statements for every operation, automatically maps FoxHound parameter types to MSSQL types, and handles identity column insertion with `SCOPE_IDENTITY()`.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### Install Dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install meadow meadow-connection-mssql
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Register the Connection
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const libFable = require('fable').new(
|
|
19
|
+
{
|
|
20
|
+
MSSQL:
|
|
21
|
+
{
|
|
22
|
+
server: 'localhost',
|
|
23
|
+
port: 1433,
|
|
24
|
+
user: 'sa',
|
|
25
|
+
password: 'YourPassword',
|
|
26
|
+
database: 'myapp'
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const libMeadowConnectionMSSQL = require('meadow-connection-mssql');
|
|
31
|
+
|
|
32
|
+
// Register the connection service
|
|
33
|
+
libFable.serviceManager.addServiceType('MeadowMSSQLProvider', libMeadowConnectionMSSQL);
|
|
34
|
+
const tmpConnection = libFable.serviceManager.instantiateServiceProvider('MeadowMSSQLProvider');
|
|
35
|
+
|
|
36
|
+
// Connect asynchronously (required for MSSQL)
|
|
37
|
+
tmpConnection.connectAsync(
|
|
38
|
+
(pError) =>
|
|
39
|
+
{
|
|
40
|
+
if (pError)
|
|
41
|
+
{
|
|
42
|
+
return console.error('MSSQL connection failed:', pError);
|
|
43
|
+
}
|
|
44
|
+
console.log('Connected to SQL Server');
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Create a Meadow DAL
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
const libMeadow = require('meadow');
|
|
52
|
+
|
|
53
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
54
|
+
.setProvider('MSSQL')
|
|
55
|
+
.setDefaultIdentifier('IDBook')
|
|
56
|
+
.setSchema([
|
|
57
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
58
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
59
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
60
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
61
|
+
{ Column: 'Price', Type: 'Decimal', Size: '12,2' },
|
|
62
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
63
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
64
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
65
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
66
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
67
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
68
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
69
|
+
]);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Connection Management
|
|
73
|
+
|
|
74
|
+
### Async Connection Model
|
|
75
|
+
|
|
76
|
+
Unlike MySQL, MSSQL connections are fully asynchronous. The connection module provides:
|
|
77
|
+
|
|
78
|
+
| Method | Description |
|
|
79
|
+
|--------|-------------|
|
|
80
|
+
| `connectAsync(fCallback)` | Async connection with callback on completion |
|
|
81
|
+
| `connect()` | Sync wrapper (warns about race conditions, delegates to `connectAsync`) |
|
|
82
|
+
|
|
83
|
+
### Connection Pool Settings
|
|
84
|
+
|
|
85
|
+
| Setting | Type | Default | Description |
|
|
86
|
+
|---------|------|---------|-------------|
|
|
87
|
+
| `MSSQL.server` | string | — | SQL Server hostname |
|
|
88
|
+
| `MSSQL.port` | number | `1433` | SQL Server port |
|
|
89
|
+
| `MSSQL.user` | string | — | Database user |
|
|
90
|
+
| `MSSQL.password` | string | — | Database password |
|
|
91
|
+
| `MSSQL.database` | string | — | Database name |
|
|
92
|
+
|
|
93
|
+
### Pool Configuration (Internal Defaults)
|
|
94
|
+
|
|
95
|
+
| Setting | Default | Description |
|
|
96
|
+
|---------|---------|-------------|
|
|
97
|
+
| `pool.max` | `10` | Maximum pool connections |
|
|
98
|
+
| `pool.min` | `0` | Minimum pool connections |
|
|
99
|
+
| `pool.idleTimeoutMillis` | `30000` | Idle connection timeout |
|
|
100
|
+
| `requestTimeout` | `80000` | Query request timeout (ms) |
|
|
101
|
+
| `connectionTimeout` | `80000` | Connection timeout (ms) |
|
|
102
|
+
| `trustServerCertificate` | `true` | Accept self-signed certificates |
|
|
103
|
+
|
|
104
|
+
## Prepared Statements
|
|
105
|
+
|
|
106
|
+
The MSSQL provider uses prepared statements for **every** CRUD operation. This provides:
|
|
107
|
+
|
|
108
|
+
- **Security** — Parameters are bound separately from SQL, preventing injection
|
|
109
|
+
- **Performance** — SQL Server can cache execution plans
|
|
110
|
+
- **Type Safety** — Parameters are explicitly typed
|
|
111
|
+
|
|
112
|
+
### Execution Lifecycle
|
|
113
|
+
|
|
114
|
+
Each operation follows this lifecycle:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
1. Build SQL via FoxHound
|
|
118
|
+
2. Create PreparedStatement from connection pool
|
|
119
|
+
3. Add typed parameters via input(name, type)
|
|
120
|
+
4. Prepare the statement
|
|
121
|
+
5. Execute with parameter values
|
|
122
|
+
6. Unprepare the statement
|
|
123
|
+
7. Return results via callback
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Type Mapping
|
|
127
|
+
|
|
128
|
+
FoxHound parameter types are automatically mapped to MSSQL types:
|
|
129
|
+
|
|
130
|
+
| FoxHound Type | MSSQL Type | Notes |
|
|
131
|
+
|---------------|------------|-------|
|
|
132
|
+
| `String` / `VarChar` | `MSSQL.VarChar(Max)` | Variable-length strings |
|
|
133
|
+
| `Text` | `MSSQL.VarChar(Max)` | Long text fields |
|
|
134
|
+
| `Decimal` | `MSSQL.Decimal(digits, decimalDigits)` | Precision from schema `Size` |
|
|
135
|
+
| Other types | `MSSQL[typeName]` | Direct lookup on MSSQL type object |
|
|
136
|
+
|
|
137
|
+
For Decimal columns, the precision and scale are read from the schema's `Size` property (e.g., `'12,2'` becomes `MSSQL.Decimal(12, 2)`).
|
|
138
|
+
|
|
139
|
+
## CRUD Operations
|
|
140
|
+
|
|
141
|
+
### Create
|
|
142
|
+
|
|
143
|
+
Executes an INSERT with `SCOPE_IDENTITY()` to capture the generated identity.
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
meadow.doCreate(
|
|
147
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' }),
|
|
148
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
149
|
+
{
|
|
150
|
+
console.log('New ID:', pRecord.IDBook);
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Internally:**
|
|
155
|
+
- Builds query: `pQuery.setDialect('MSSQL').buildCreateQuery()`
|
|
156
|
+
- Appends: `SELECT SCOPE_IDENTITY() AS value;`
|
|
157
|
+
- Extracts identity: `recordset[0].value`
|
|
158
|
+
|
|
159
|
+
### Identity Insert
|
|
160
|
+
|
|
161
|
+
For scenarios where you need to insert explicit identity values (e.g., data migration):
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const tmpQuery = meadow.query
|
|
165
|
+
.addRecord({ IDBook: 500, Title: 'Imported Book' });
|
|
166
|
+
tmpQuery.parameters.AllowIdentityInsert = true;
|
|
167
|
+
|
|
168
|
+
meadow.doCreate(tmpQuery,
|
|
169
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
170
|
+
{
|
|
171
|
+
console.log('Inserted with explicit ID:', pRecord.IDBook);
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
When `AllowIdentityInsert` is `true`, the provider wraps the INSERT with:
|
|
176
|
+
|
|
177
|
+
```sql
|
|
178
|
+
SET IDENTITY_INSERT [Book] ON;
|
|
179
|
+
INSERT INTO Book (...) VALUES (...); SELECT SCOPE_IDENTITY() AS value;
|
|
180
|
+
SET IDENTITY_INSERT [Book] OFF;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Read
|
|
184
|
+
|
|
185
|
+
Executes a SELECT and returns result rows from the recordset.
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
meadow.doRead(
|
|
189
|
+
meadow.query.addFilter('IDBook', 42),
|
|
190
|
+
(pError, pQuery, pRecord) =>
|
|
191
|
+
{
|
|
192
|
+
console.log('Title:', pRecord.Title);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
meadow.doReads(
|
|
196
|
+
meadow.query.setCap(25).setBegin(0),
|
|
197
|
+
(pError, pQuery, pRecords) =>
|
|
198
|
+
{
|
|
199
|
+
pRecords.forEach((pBook) => console.log(pBook.Title));
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Internally:**
|
|
204
|
+
- Builds query: `pQuery.setDialect('MSSQL').buildReadQuery()`
|
|
205
|
+
- Returns: `recordset` array from prepared statement execution
|
|
206
|
+
|
|
207
|
+
### Update
|
|
208
|
+
|
|
209
|
+
Executes an UPDATE with prepared statement parameters.
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
meadow.doUpdate(
|
|
213
|
+
meadow.query
|
|
214
|
+
.addFilter('IDBook', 42)
|
|
215
|
+
.addRecord({ Title: 'Updated Title' }),
|
|
216
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
217
|
+
{
|
|
218
|
+
console.log('Updated:', pRecord.Title);
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Delete
|
|
223
|
+
|
|
224
|
+
Executes a soft delete and returns the affected row count.
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
meadow.doDelete(
|
|
228
|
+
meadow.query.addFilter('IDBook', 42),
|
|
229
|
+
(pError, pQuery, pResult) =>
|
|
230
|
+
{
|
|
231
|
+
console.log('Deleted rows:', pResult);
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Internally:**
|
|
236
|
+
- Extracts affected rows: `rowsAffected[0]`
|
|
237
|
+
|
|
238
|
+
### Undelete
|
|
239
|
+
|
|
240
|
+
Reverses a soft delete.
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
meadow.doUndelete(
|
|
244
|
+
meadow.query.addFilter('IDBook', 42),
|
|
245
|
+
(pError, pQuery, pResult) =>
|
|
246
|
+
{
|
|
247
|
+
console.log('Restored rows:', pResult);
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Count
|
|
252
|
+
|
|
253
|
+
Returns the matching record count.
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
meadow.doCount(
|
|
257
|
+
meadow.query.addFilter('Author', 'Frank Herbert', '='),
|
|
258
|
+
(pError, pQuery, pCount) =>
|
|
259
|
+
{
|
|
260
|
+
console.log('Books by Herbert:', pCount);
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Internally:**
|
|
265
|
+
- Extracts count: `recordset[0].Row_Count`
|
|
266
|
+
- Falls back to `-1` if extraction fails
|
|
267
|
+
|
|
268
|
+
## Error Handling
|
|
269
|
+
|
|
270
|
+
The MSSQL provider has detailed error handling at each stage:
|
|
271
|
+
|
|
272
|
+
| Stage | Error Level | Message Pattern |
|
|
273
|
+
|-------|-------------|-----------------|
|
|
274
|
+
| Preparation | `log.error` | `"Create Error preparing prepared statement"` |
|
|
275
|
+
| Execution | `result.error` | Error stored on query result |
|
|
276
|
+
| Unprepare | `log.error` | `"Create Error unpreparing prepared statement"` |
|
|
277
|
+
| Marshalling | `log.warn` | Warning with query body for debugging |
|
|
278
|
+
|
|
279
|
+
## Docker Development
|
|
280
|
+
|
|
281
|
+
For local SQL Server development:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
docker run -d \
|
|
285
|
+
--name meadow-mssql \
|
|
286
|
+
-e ACCEPT_EULA=Y \
|
|
287
|
+
-e SA_PASSWORD=YourStrong!Password \
|
|
288
|
+
-p 1433:1433 \
|
|
289
|
+
mcr.microsoft.com/mssql/server:2022-latest
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Related Documentation
|
|
293
|
+
|
|
294
|
+
- [Providers Overview](providers/README.md) — Comparison of all providers
|
|
295
|
+
- [MySQL Provider](providers/mysql.md) — MySQL/MariaDB alternative
|
|
296
|
+
- [meadow-connection-mssql](https://github.com/stevenvelozo/meadow-connection-mssql) — Connection module source
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# MySQL Provider
|
|
2
|
+
|
|
3
|
+
> Production-ready MySQL/MariaDB integration with connection pooling and named placeholders
|
|
4
|
+
|
|
5
|
+
The MySQL provider connects Meadow to MySQL and MariaDB databases via the [mysql2](https://github.com/sidorares/node-mysql2) library. It uses connection pooling for efficient resource management and named placeholders for safe parameter binding.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### Install Dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install meadow meadow-connection-mysql
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Register the Connection
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const libFable = require('fable').new(
|
|
19
|
+
{
|
|
20
|
+
MySQL:
|
|
21
|
+
{
|
|
22
|
+
Server: 'localhost',
|
|
23
|
+
Port: 3306,
|
|
24
|
+
User: 'root',
|
|
25
|
+
Password: '',
|
|
26
|
+
Database: 'myapp',
|
|
27
|
+
ConnectionPoolLimit: 20
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const libMeadowConnectionMySQL = require('meadow-connection-mysql');
|
|
32
|
+
|
|
33
|
+
// Register and instantiate the connection service
|
|
34
|
+
libFable.serviceManager.addServiceType('MeadowMySQLProvider', libMeadowConnectionMySQL);
|
|
35
|
+
libFable.serviceManager.instantiateServiceProvider('MeadowMySQLProvider',
|
|
36
|
+
{ MeadowConnectionMySQLAutoConnect: true });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Create a Meadow DAL
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
const libMeadow = require('meadow');
|
|
43
|
+
|
|
44
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
45
|
+
.setProvider('MySQL')
|
|
46
|
+
.setDefaultIdentifier('IDBook')
|
|
47
|
+
.setSchema([
|
|
48
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
49
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
50
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
51
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
52
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
53
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
54
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
55
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
56
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
57
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
58
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
59
|
+
]);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Connection Management
|
|
63
|
+
|
|
64
|
+
### Connection Pool
|
|
65
|
+
|
|
66
|
+
The MySQL provider uses a connection pool managed by `meadow-connection-mysql`. Each CRUD operation:
|
|
67
|
+
|
|
68
|
+
1. Acquires a connection from the pool via `pool.getConnection()`
|
|
69
|
+
2. Executes the generated SQL query
|
|
70
|
+
3. Releases the connection back to the pool
|
|
71
|
+
|
|
72
|
+
This ensures efficient reuse of database connections across concurrent requests.
|
|
73
|
+
|
|
74
|
+
### Pool Access
|
|
75
|
+
|
|
76
|
+
The provider looks for the connection pool in two locations (for backward compatibility):
|
|
77
|
+
|
|
78
|
+
| Property | Description |
|
|
79
|
+
|----------|-------------|
|
|
80
|
+
| `fable.MeadowMySQLProvider.pool` | New-style: registered Fable service |
|
|
81
|
+
| `fable.MeadowMySQLConnectionPool` | Legacy: directly assigned pool |
|
|
82
|
+
|
|
83
|
+
### Connection Configuration
|
|
84
|
+
|
|
85
|
+
| Setting | Type | Default | Description |
|
|
86
|
+
|---------|------|---------|-------------|
|
|
87
|
+
| `MySQL.Server` | string | — | Database server hostname |
|
|
88
|
+
| `MySQL.Port` | number | `3306` | Database server port |
|
|
89
|
+
| `MySQL.User` | string | — | Database user |
|
|
90
|
+
| `MySQL.Password` | string | — | Database password |
|
|
91
|
+
| `MySQL.Database` | string | — | Database name |
|
|
92
|
+
| `MySQL.ConnectionPoolLimit` | number | `20` | Maximum pool connections |
|
|
93
|
+
|
|
94
|
+
### Named Placeholders
|
|
95
|
+
|
|
96
|
+
The MySQL provider enables `namedPlaceholders: true` on the connection pool, which allows FoxHound to generate queries with named parameter binding:
|
|
97
|
+
|
|
98
|
+
```sql
|
|
99
|
+
-- Generated by FoxHound with named placeholders:
|
|
100
|
+
SELECT IDBook, Title, Author FROM Book WHERE IDBook = :IDBook AND Deleted = 0
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## CRUD Operations
|
|
104
|
+
|
|
105
|
+
### Create
|
|
106
|
+
|
|
107
|
+
Executes an INSERT statement and returns the auto-generated identity value.
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
meadow.doCreate(
|
|
111
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' }),
|
|
112
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
113
|
+
{
|
|
114
|
+
console.log('New ID:', pRecord.IDBook);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Internally:**
|
|
119
|
+
- Builds query: `pQuery.setDialect('MySQL').buildCreateQuery()`
|
|
120
|
+
- Extracts identity: `result.insertId`
|
|
121
|
+
- If `insertId` extraction fails, logs a warning with the query body for debugging
|
|
122
|
+
|
|
123
|
+
### Read
|
|
124
|
+
|
|
125
|
+
Executes a SELECT statement and returns the result rows.
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
// Single record
|
|
129
|
+
meadow.doRead(
|
|
130
|
+
meadow.query.addFilter('IDBook', 42),
|
|
131
|
+
(pError, pQuery, pRecord) =>
|
|
132
|
+
{
|
|
133
|
+
console.log('Title:', pRecord.Title);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Multiple records
|
|
137
|
+
meadow.doReads(
|
|
138
|
+
meadow.query.setCap(25).setBegin(0).addSort({ Column: 'Title', Direction: 'ASC' }),
|
|
139
|
+
(pError, pQuery, pRecords) =>
|
|
140
|
+
{
|
|
141
|
+
pRecords.forEach((pBook) => console.log(pBook.Title));
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Internally:**
|
|
146
|
+
- Builds query: `pQuery.setDialect('MySQL').buildReadQuery()`
|
|
147
|
+
- Returns full result array
|
|
148
|
+
|
|
149
|
+
### Update
|
|
150
|
+
|
|
151
|
+
Executes an UPDATE statement.
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
meadow.doUpdate(
|
|
155
|
+
meadow.query
|
|
156
|
+
.addFilter('IDBook', 42)
|
|
157
|
+
.addRecord({ Title: 'Updated Title' }),
|
|
158
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
159
|
+
{
|
|
160
|
+
console.log('Updated:', pRecord.Title);
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Internally:**
|
|
165
|
+
- Builds query: `pQuery.setDialect('MySQL').buildUpdateQuery()`
|
|
166
|
+
|
|
167
|
+
### Delete
|
|
168
|
+
|
|
169
|
+
Executes a soft delete (UPDATE setting Deleted flag) and returns the affected row count.
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
meadow.doDelete(
|
|
173
|
+
meadow.query.addFilter('IDBook', 42),
|
|
174
|
+
(pError, pQuery, pResult) =>
|
|
175
|
+
{
|
|
176
|
+
console.log('Deleted rows:', pResult);
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Internally:**
|
|
181
|
+
- Builds query: `pQuery.setDialect('MySQL').buildDeleteQuery()`
|
|
182
|
+
- Extracts affected rows: `result.affectedRows`
|
|
183
|
+
|
|
184
|
+
### Undelete
|
|
185
|
+
|
|
186
|
+
Reverses a soft delete and returns the affected row count.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
meadow.doUndelete(
|
|
190
|
+
meadow.query.addFilter('IDBook', 42),
|
|
191
|
+
(pError, pQuery, pResult) =>
|
|
192
|
+
{
|
|
193
|
+
console.log('Restored rows:', pResult);
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Internally:**
|
|
198
|
+
- Builds query: `pQuery.setDialect('MySQL').buildUndeleteQuery()`
|
|
199
|
+
- Extracts affected rows: `result.affectedRows`
|
|
200
|
+
|
|
201
|
+
### Count
|
|
202
|
+
|
|
203
|
+
Executes a COUNT query and returns the integer count.
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
meadow.doCount(
|
|
207
|
+
meadow.query,
|
|
208
|
+
(pError, pQuery, pCount) =>
|
|
209
|
+
{
|
|
210
|
+
console.log('Total books:', pCount);
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Internally:**
|
|
215
|
+
- Builds query: `pQuery.setDialect('MySQL').buildCountQuery()`
|
|
216
|
+
- Extracts count: `result[0].RowCount`
|
|
217
|
+
|
|
218
|
+
## Query Logging
|
|
219
|
+
|
|
220
|
+
The MySQL provider supports query logging controlled by the `GlobalLogLevel` Fable setting:
|
|
221
|
+
|
|
222
|
+
| Level | Behavior |
|
|
223
|
+
|-------|----------|
|
|
224
|
+
| `0` (default) | No query logging |
|
|
225
|
+
| `> 0` | Logs trace-level messages for each query executed |
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const libFable = require('fable').new(
|
|
229
|
+
{
|
|
230
|
+
GlobalLogLevel: 1,
|
|
231
|
+
MySQL: { /* ... */ }
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Error Handling
|
|
236
|
+
|
|
237
|
+
All operations follow the same error handling pattern:
|
|
238
|
+
|
|
239
|
+
- Database errors are stored in `pQuery.parameters.result.error`
|
|
240
|
+
- If the identity/rowcount extraction fails after a successful query, a warning is logged with the original query body
|
|
241
|
+
- Connection errors bubble up through the callback as `pError`
|
|
242
|
+
|
|
243
|
+
## Docker Development
|
|
244
|
+
|
|
245
|
+
For local MySQL development:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
docker run -d \
|
|
249
|
+
--name meadow-mysql \
|
|
250
|
+
-e MYSQL_ROOT_PASSWORD=password \
|
|
251
|
+
-e MYSQL_DATABASE=myapp \
|
|
252
|
+
-p 3306:3306 \
|
|
253
|
+
mysql:8
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Related Documentation
|
|
257
|
+
|
|
258
|
+
- [Providers Overview](providers/README.md) — Comparison of all providers
|
|
259
|
+
- [MSSQL Provider](providers/mssql.md) — Microsoft SQL Server alternative
|
|
260
|
+
- [meadow-connection-mysql](https://github.com/stevenvelozo/meadow-connection-mysql) — Connection module source
|