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,350 @@
|
|
|
1
|
+
# Read
|
|
2
|
+
|
|
3
|
+
> Retrieve single or multiple records from the database
|
|
4
|
+
|
|
5
|
+
Meadow provides two read methods: `doRead` for fetching a single record and `doReads` for fetching multiple records with pagination, filtering, and performance profiling.
|
|
6
|
+
|
|
7
|
+
## doRead - Single Record
|
|
8
|
+
|
|
9
|
+
### Method Signature
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
meadow.doRead(pQuery, fCallBack)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Callback
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
fCallBack(pError, pQuery, pRecord)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Parameter | Type | Description |
|
|
22
|
+
|-----------|------|-------------|
|
|
23
|
+
| `pError` | object/null | Error object if the operation failed, null on success |
|
|
24
|
+
| `pQuery` | object | The query that was executed |
|
|
25
|
+
| `pRecord` | object/undefined | The marshaled record, or `undefined` if not found |
|
|
26
|
+
|
|
27
|
+
### Basic Usage
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// Read a single book by ID
|
|
31
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 42),
|
|
32
|
+
(pError, pQuery, pRecord) =>
|
|
33
|
+
{
|
|
34
|
+
if (pError)
|
|
35
|
+
{
|
|
36
|
+
return console.log('Read error:', pError);
|
|
37
|
+
}
|
|
38
|
+
if (!pRecord)
|
|
39
|
+
{
|
|
40
|
+
return console.log('Book not found');
|
|
41
|
+
}
|
|
42
|
+
console.log('Found:', pRecord.Title, 'by', pRecord.Author);
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Reading by GUID
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
meadow.doRead(meadow.query.addFilter('GUIDBook', 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
|
|
50
|
+
(pError, pQuery, pRecord) =>
|
|
51
|
+
{
|
|
52
|
+
if (pRecord)
|
|
53
|
+
{
|
|
54
|
+
console.log('Found book:', pRecord.Title);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Reading with Multiple Filters
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const tmpQuery = meadow.query
|
|
63
|
+
.addFilter('Author', 'Asimov')
|
|
64
|
+
.addFilter('InPrint', 1);
|
|
65
|
+
|
|
66
|
+
meadow.doRead(tmpQuery,
|
|
67
|
+
(pError, pQuery, pRecord) =>
|
|
68
|
+
{
|
|
69
|
+
// Returns the first matching record
|
|
70
|
+
if (pRecord)
|
|
71
|
+
{
|
|
72
|
+
console.log('Found:', pRecord.Title);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### How It Works
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
1. Build Read Query
|
|
81
|
+
└── Apply filters, set dialect, generate SELECT SQL
|
|
82
|
+
2. Execute via Provider
|
|
83
|
+
└── Run query against database
|
|
84
|
+
3. Marshal Record
|
|
85
|
+
└── Convert DB row to plain JavaScript object using schema defaults
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The read operation returns only the first matching record. If no record matches, the callback receives `undefined` as the record parameter (not an error).
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## doReads - Multiple Records
|
|
93
|
+
|
|
94
|
+
### Method Signature
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
meadow.doReads(pQuery, fCallBack)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Callback
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
fCallBack(pError, pQuery, pRecords)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Parameter | Type | Description |
|
|
107
|
+
|-----------|------|-------------|
|
|
108
|
+
| `pError` | object/null | Error object if the operation failed, null on success |
|
|
109
|
+
| `pQuery` | object | The query that was executed |
|
|
110
|
+
| `pRecords` | array | Array of marshaled records (empty array if none found) |
|
|
111
|
+
|
|
112
|
+
### Basic Usage
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Read all books (up to default cap)
|
|
116
|
+
meadow.doReads(meadow.query,
|
|
117
|
+
(pError, pQuery, pRecords) =>
|
|
118
|
+
{
|
|
119
|
+
if (pError)
|
|
120
|
+
{
|
|
121
|
+
return console.log('Read error:', pError);
|
|
122
|
+
}
|
|
123
|
+
console.log('Found', pRecords.length, 'books');
|
|
124
|
+
pRecords.forEach((pBook) =>
|
|
125
|
+
{
|
|
126
|
+
console.log('-', pBook.Title);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Pagination
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// Page 1: first 25 records
|
|
135
|
+
meadow.doReads(meadow.query.setCap(25).setBegin(0),
|
|
136
|
+
(pError, pQuery, pRecords) =>
|
|
137
|
+
{
|
|
138
|
+
console.log('Page 1:', pRecords.length, 'records');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Page 2: next 25 records
|
|
142
|
+
meadow.doReads(meadow.query.setCap(25).setBegin(25),
|
|
143
|
+
(pError, pQuery, pRecords) =>
|
|
144
|
+
{
|
|
145
|
+
console.log('Page 2:', pRecords.length, 'records');
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Filtering
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Read books by a specific author
|
|
153
|
+
meadow.doReads(meadow.query.addFilter('Author', 'Philip K. Dick'),
|
|
154
|
+
(pError, pQuery, pRecords) =>
|
|
155
|
+
{
|
|
156
|
+
console.log('Found', pRecords.length, 'books by PKD');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Read with multiple filters
|
|
160
|
+
const tmpQuery = meadow.query
|
|
161
|
+
.addFilter('Author', 'Asimov')
|
|
162
|
+
.addFilter('PublishYear', 1950, '>')
|
|
163
|
+
.setCap(10);
|
|
164
|
+
|
|
165
|
+
meadow.doReads(tmpQuery,
|
|
166
|
+
(pError, pQuery, pRecords) =>
|
|
167
|
+
{
|
|
168
|
+
console.log('Found', pRecords.length, 'post-1950 Asimov books');
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Sorting
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
// Read books sorted by title
|
|
176
|
+
const tmpQuery = meadow.query
|
|
177
|
+
.addSort({ Column: 'Title', Direction: 'ASC' })
|
|
178
|
+
.setCap(50);
|
|
179
|
+
|
|
180
|
+
meadow.doReads(tmpQuery,
|
|
181
|
+
(pError, pQuery, pRecords) =>
|
|
182
|
+
{
|
|
183
|
+
pRecords.forEach((pBook) =>
|
|
184
|
+
{
|
|
185
|
+
console.log(pBook.Title);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Column Selection
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
// Only retrieve specific columns for efficiency
|
|
194
|
+
const tmpQuery = meadow.query
|
|
195
|
+
.setDataElements(['IDBook', 'Title', 'Author'])
|
|
196
|
+
.setCap(100);
|
|
197
|
+
|
|
198
|
+
meadow.doReads(tmpQuery,
|
|
199
|
+
(pError, pQuery, pRecords) =>
|
|
200
|
+
{
|
|
201
|
+
// pRecords still contain all schema default fields,
|
|
202
|
+
// but only the selected columns will have DB values
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Distinct Queries
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Get unique author/publisher combinations
|
|
210
|
+
const tmpQuery = meadow.query
|
|
211
|
+
.setDistinct(true)
|
|
212
|
+
.setDataElements(['Author', 'Publisher']);
|
|
213
|
+
|
|
214
|
+
meadow.doReads(tmpQuery,
|
|
215
|
+
(pError, pQuery, pRecords) =>
|
|
216
|
+
{
|
|
217
|
+
pRecords.forEach((pRow) =>
|
|
218
|
+
{
|
|
219
|
+
console.log(pRow.Author, '-', pRow.Publisher);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### How It Works
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
1. Build Read Query
|
|
228
|
+
└── Apply filters, pagination, sorting, set dialect, generate SELECT SQL
|
|
229
|
+
2. Execute via Provider
|
|
230
|
+
└── Run query against database, start performance timer
|
|
231
|
+
3. Marshal Each Record
|
|
232
|
+
└── Convert each DB row to plain JavaScript object
|
|
233
|
+
4. Performance Check
|
|
234
|
+
└── Log warning if query exceeded threshold (default 200ms)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Soft Delete Filtering
|
|
240
|
+
|
|
241
|
+
By default, both `doRead` and `doReads` automatically exclude soft-deleted records (where `Deleted = 1`). This filter is applied at the SQL dialect level.
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
// Default: only active records
|
|
245
|
+
meadow.doReads(meadow.query,
|
|
246
|
+
(pError, pQuery, pRecords) =>
|
|
247
|
+
{
|
|
248
|
+
// Soft-deleted records are excluded
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Include soft-deleted records
|
|
252
|
+
meadow.doReads(meadow.query.setDisableDeleteTracking(true),
|
|
253
|
+
(pError, pQuery, pRecords) =>
|
|
254
|
+
{
|
|
255
|
+
// ALL records returned, including soft-deleted ones
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Slow Query Profiling
|
|
260
|
+
|
|
261
|
+
The `doReads` method (and `doCount`) automatically profiles query execution time. If a query exceeds the configured threshold, a warning is logged:
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
// Configure the threshold (default: 200ms)
|
|
265
|
+
const _Fable = require('fable').new(
|
|
266
|
+
{
|
|
267
|
+
QueryThresholdWarnTime: 500 // Warn for queries over 500ms
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The slow query log includes the provider name, SQL body, parameters, and the fully interpolated query string for easy debugging.
|
|
272
|
+
|
|
273
|
+
## Raw Query Override
|
|
274
|
+
|
|
275
|
+
Both read operations respect the `Read` raw query override:
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
// Override the generated SELECT with custom SQL
|
|
279
|
+
meadow.rawQueries.setQuery('Read',
|
|
280
|
+
'SELECT b.*, a.Name AS AuthorName FROM Book b LEFT JOIN Author a ON b.IDAuthor = a.IDAuthor WHERE b.IDBook = :IDBook');
|
|
281
|
+
|
|
282
|
+
// This custom query is used for doRead
|
|
283
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 1),
|
|
284
|
+
(pError, pQuery, pRecord) =>
|
|
285
|
+
{
|
|
286
|
+
// pRecord will include the AuthorName from the JOIN
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
For `doReads`, use the `Reads` override key:
|
|
291
|
+
|
|
292
|
+
```javascript
|
|
293
|
+
meadow.rawQueries.setQuery('Reads',
|
|
294
|
+
'SELECT b.*, COUNT(r.IDReview) AS ReviewCount FROM Book b LEFT JOIN Review r ON b.IDBook = r.IDBook GROUP BY b.IDBook');
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Full Example
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
const libFable = require('fable').new(
|
|
301
|
+
{
|
|
302
|
+
QueryThresholdWarnTime: 300
|
|
303
|
+
});
|
|
304
|
+
const libMeadow = require('meadow');
|
|
305
|
+
|
|
306
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
307
|
+
.setProvider('MySQL')
|
|
308
|
+
.setDefaultIdentifier('IDBook')
|
|
309
|
+
.setSchema([
|
|
310
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
311
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
312
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
313
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
314
|
+
{ Column: 'PublishYear', Type: 'Numeric' },
|
|
315
|
+
{ Column: 'Price', Type: 'Decimal', Size: '18,2' },
|
|
316
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
317
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
318
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
319
|
+
]);
|
|
320
|
+
|
|
321
|
+
// Read a single book
|
|
322
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 1),
|
|
323
|
+
(pError, pQuery, pBook) =>
|
|
324
|
+
{
|
|
325
|
+
if (!pError && pBook)
|
|
326
|
+
{
|
|
327
|
+
console.log('Book:', pBook.Title);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Read a filtered, sorted, paginated list
|
|
332
|
+
const tmpQuery = meadow.query
|
|
333
|
+
.addFilter('PublishYear', 1980, '>=')
|
|
334
|
+
.addSort({ Column: 'Title', Direction: 'ASC' })
|
|
335
|
+
.setCap(10)
|
|
336
|
+
.setBegin(0);
|
|
337
|
+
|
|
338
|
+
meadow.doReads(tmpQuery,
|
|
339
|
+
(pError, pQuery, pBooks) =>
|
|
340
|
+
{
|
|
341
|
+
if (!pError)
|
|
342
|
+
{
|
|
343
|
+
console.log('Post-1980 books (page 1):');
|
|
344
|
+
pBooks.forEach((pBook) =>
|
|
345
|
+
{
|
|
346
|
+
console.log(` ${pBook.Title} (${pBook.PublishYear}) - $${pBook.Price}`);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
```
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Update
|
|
2
|
+
|
|
3
|
+
> Modify existing records in the database
|
|
4
|
+
|
|
5
|
+
The `doUpdate` method modifies an existing record and returns the fully hydrated updated object. Meadow automatically handles audit stamping, safely ignores identity and create-only fields, and reads back the record after updating to ensure you receive the current state.
|
|
6
|
+
|
|
7
|
+
## Method Signature
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
meadow.doUpdate(pQuery, fCallBack)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Callback
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
fCallBack(pError, pUpdateQuery, pReadQuery, pRecord)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Parameter | Type | Description |
|
|
20
|
+
|-----------|------|-------------|
|
|
21
|
+
| `pError` | object/null | Error object if the operation failed, null on success |
|
|
22
|
+
| `pUpdateQuery` | object | The query used for the UPDATE operation |
|
|
23
|
+
| `pReadQuery` | object | The query used to read back the updated record |
|
|
24
|
+
| `pRecord` | object | The fully hydrated record after update |
|
|
25
|
+
|
|
26
|
+
## Basic Usage
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
const tmpQuery = meadow.query
|
|
30
|
+
.addRecord(
|
|
31
|
+
{
|
|
32
|
+
IDBook: 42,
|
|
33
|
+
Title: 'Updated Title',
|
|
34
|
+
Price: 19.99
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
meadow.doUpdate(tmpQuery,
|
|
38
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
39
|
+
{
|
|
40
|
+
if (pError)
|
|
41
|
+
{
|
|
42
|
+
return console.log('Update failed:', pError);
|
|
43
|
+
}
|
|
44
|
+
console.log('Updated:', pRecord.Title, '- New price:', pRecord.Price);
|
|
45
|
+
console.log('Last modified:', pRecord.UpdateDate);
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## How It Works
|
|
50
|
+
|
|
51
|
+
The update operation follows a multi-step waterfall:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
1. Validate and Prepare
|
|
55
|
+
└── Verify record has default identifier, set user ID, add filters
|
|
56
|
+
2. Execute UPDATE
|
|
57
|
+
└── Run the UPDATE query via provider
|
|
58
|
+
3. Read Back Record
|
|
59
|
+
└── Fetch the updated record using the same filter
|
|
60
|
+
4. Marshal to Object
|
|
61
|
+
└── Convert DB result to a plain JavaScript object
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Record Identification
|
|
65
|
+
|
|
66
|
+
The record you pass to `addRecord()` **must** include the default identifier (e.g., `IDBook`). Meadow uses this to build the WHERE clause:
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// The default identifier must be present
|
|
70
|
+
const tmpQuery = meadow.query
|
|
71
|
+
.addRecord(
|
|
72
|
+
{
|
|
73
|
+
IDBook: 42, // Required: identifies which record to update
|
|
74
|
+
Title: 'New Title'
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
meadow.doUpdate(tmpQuery,
|
|
78
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
79
|
+
{
|
|
80
|
+
// Meadow automatically added: WHERE IDBook = 42
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If the default identifier is missing, the operation fails with an error to prevent accidental mass updates.
|
|
85
|
+
|
|
86
|
+
## Auto-Populated Fields
|
|
87
|
+
|
|
88
|
+
When your schema includes these column types, Meadow handles them automatically during updates:
|
|
89
|
+
|
|
90
|
+
| Schema Type | Behavior on Update |
|
|
91
|
+
|-------------|-------------------|
|
|
92
|
+
| `AutoIdentity` | **Ignored** - cannot modify the primary key |
|
|
93
|
+
| `AutoGUID` | **Ignored** - not modified on update |
|
|
94
|
+
| `CreateDate` | **Ignored** - creation timestamp is immutable |
|
|
95
|
+
| `CreateIDUser` | **Ignored** - creating user is immutable |
|
|
96
|
+
| `UpdateDate` | Set to `NOW()` automatically |
|
|
97
|
+
| `UpdateIDUser` | Set to the current user ID |
|
|
98
|
+
| `DeleteDate` | **Ignored** on standard update |
|
|
99
|
+
| `DeleteIDUser` | **Ignored** on standard update |
|
|
100
|
+
| `Deleted` | Included if present in the record |
|
|
101
|
+
|
|
102
|
+
## Partial Updates
|
|
103
|
+
|
|
104
|
+
Only fields present in the record are modified. Other columns remain unchanged:
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// Only update the Title -- Author, Price, etc. stay the same
|
|
108
|
+
const tmpQuery = meadow.query
|
|
109
|
+
.addRecord(
|
|
110
|
+
{
|
|
111
|
+
IDBook: 42,
|
|
112
|
+
Title: 'Just the Title Changes'
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
meadow.doUpdate(tmpQuery,
|
|
116
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
117
|
+
{
|
|
118
|
+
// pRecord.Author is unchanged
|
|
119
|
+
// pRecord.Price is unchanged
|
|
120
|
+
// pRecord.Title is 'Just the Title Changes'
|
|
121
|
+
// pRecord.UpdateDate is NOW()
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Disabling Auto-Stamps
|
|
126
|
+
|
|
127
|
+
For data migration or special operations, you can disable automatic timestamp and user stamping:
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const tmpQuery = meadow.query
|
|
131
|
+
.addRecord(
|
|
132
|
+
{
|
|
133
|
+
IDBook: 42,
|
|
134
|
+
Title: 'Migrated Data'
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Disable auto-date stamp (UpdateDate will not be set to NOW())
|
|
138
|
+
tmpQuery.query.disableAutoDateStamp = true;
|
|
139
|
+
|
|
140
|
+
// Disable auto-user stamp (UpdateIDUser will not be set)
|
|
141
|
+
tmpQuery.query.disableAutoUserStamp = true;
|
|
142
|
+
|
|
143
|
+
meadow.doUpdate(tmpQuery,
|
|
144
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
145
|
+
{
|
|
146
|
+
// UpdateDate and UpdatingIDUser were not modified
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Safety: Filter Requirement
|
|
151
|
+
|
|
152
|
+
Meadow requires at least one filter on the query before executing an update. The default identifier filter is added automatically from the record, but if somehow no filters are present, the operation aborts:
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// This will fail safely:
|
|
156
|
+
// "Automated update missing filters... aborting!"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
This prevents accidental `UPDATE ... SET ...` without a WHERE clause.
|
|
160
|
+
|
|
161
|
+
## Error Handling
|
|
162
|
+
|
|
163
|
+
Common error conditions:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
meadow.doUpdate(tmpQuery,
|
|
167
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
168
|
+
{
|
|
169
|
+
if (pError)
|
|
170
|
+
{
|
|
171
|
+
// Possible errors:
|
|
172
|
+
// - "No record submitted" (missing addRecord)
|
|
173
|
+
// - "Automated update missing default identifier"
|
|
174
|
+
// (record doesn't have IDBook or equivalent)
|
|
175
|
+
// - "Automated update missing filters... aborting!"
|
|
176
|
+
// (safety check failed)
|
|
177
|
+
// - "No record updated." (database returned no affected rows)
|
|
178
|
+
// - "No record found to update!" (read-back found nothing)
|
|
179
|
+
// - Provider-specific database errors
|
|
180
|
+
console.log('Error:', pError);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Raw Query Override
|
|
186
|
+
|
|
187
|
+
The Update operation does not support raw query overrides for the UPDATE step itself. However, the read-back step respects the `Read` override, so custom JOINs and computed columns appear in the returned record.
|
|
188
|
+
|
|
189
|
+
## Full Example
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
const libFable = require('fable').new();
|
|
193
|
+
const libMeadow = require('meadow');
|
|
194
|
+
|
|
195
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
196
|
+
.setProvider('MySQL')
|
|
197
|
+
.setDefaultIdentifier('IDBook')
|
|
198
|
+
.setSchema([
|
|
199
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
200
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
201
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
202
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
203
|
+
{ Column: 'Price', Type: 'Decimal', Size: '18,2' },
|
|
204
|
+
{ Column: 'InPrint', Type: 'Boolean' },
|
|
205
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
206
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
207
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
208
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
209
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
// Set the user performing the update
|
|
213
|
+
meadow.setIDUser(5);
|
|
214
|
+
|
|
215
|
+
// Update the price and print status of book 42
|
|
216
|
+
const tmpQuery = meadow.query
|
|
217
|
+
.addRecord(
|
|
218
|
+
{
|
|
219
|
+
IDBook: 42,
|
|
220
|
+
Price: 24.99,
|
|
221
|
+
InPrint: true
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
meadow.doUpdate(tmpQuery,
|
|
225
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
226
|
+
{
|
|
227
|
+
if (pError)
|
|
228
|
+
{
|
|
229
|
+
return console.log('Update failed:', pError);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// pRecord now contains the full updated record:
|
|
233
|
+
// {
|
|
234
|
+
// IDBook: 42, (unchanged)
|
|
235
|
+
// GUIDBook: '0x...', (unchanged)
|
|
236
|
+
// Title: 'Original Title', (unchanged)
|
|
237
|
+
// Author: 'Original Author', (unchanged)
|
|
238
|
+
// Price: 24.99, (updated)
|
|
239
|
+
// InPrint: true, (updated)
|
|
240
|
+
// CreateDate: '2024-01-15...', (unchanged)
|
|
241
|
+
// CreatingIDUser: 1, (unchanged)
|
|
242
|
+
// UpdateDate: '2024-06-20...', (auto: NOW())
|
|
243
|
+
// UpdatingIDUser: 5, (auto: from setIDUser)
|
|
244
|
+
// Deleted: 0 (unchanged)
|
|
245
|
+
// }
|
|
246
|
+
console.log('Updated book', pRecord.IDBook);
|
|
247
|
+
console.log('New price:', pRecord.Price);
|
|
248
|
+
console.log('Modified at:', pRecord.UpdateDate, 'by user', pRecord.UpdatingIDUser);
|
|
249
|
+
});
|
|
250
|
+
```
|