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.
@@ -0,0 +1,226 @@
1
+ # Create
2
+
3
+ > Insert new records into the database
4
+
5
+ The `doCreate` method inserts a new record and returns the fully hydrated object, including any auto-generated fields like the primary key, GUID, and creation timestamps.
6
+
7
+ ## Method Signature
8
+
9
+ ```javascript
10
+ meadow.doCreate(pQuery, fCallBack)
11
+ ```
12
+
13
+ ### Callback
14
+
15
+ ```javascript
16
+ fCallBack(pError, pCreateQuery, pReadQuery, pRecord)
17
+ ```
18
+
19
+ | Parameter | Type | Description |
20
+ |-----------|------|-------------|
21
+ | `pError` | object/null | Error object if the operation failed, null on success |
22
+ | `pCreateQuery` | object | The query used for the INSERT operation |
23
+ | `pReadQuery` | object | The query used to read back the created record |
24
+ | `pRecord` | object | The fully hydrated record with all auto-generated fields |
25
+
26
+ ## Basic Usage
27
+
28
+ ```javascript
29
+ const tmpQuery = meadow.query.addRecord(
30
+ {
31
+ Title: 'Neuromancer',
32
+ Author: 'William Gibson',
33
+ PublishYear: 1984
34
+ });
35
+
36
+ meadow.doCreate(tmpQuery,
37
+ (pError, pCreateQuery, pReadQuery, pRecord) =>
38
+ {
39
+ if (pError)
40
+ {
41
+ return console.log('Create failed:', pError);
42
+ }
43
+ console.log('Created book:', pRecord.IDBook);
44
+ console.log('GUID:', pRecord.GUIDBook);
45
+ console.log('Created at:', pRecord.CreateDate);
46
+ });
47
+ ```
48
+
49
+ ## How It Works
50
+
51
+ The create operation follows a multi-step waterfall:
52
+
53
+ ```
54
+ 1. GUID Uniqueness Check (if GUID provided)
55
+ └── Queries DB to verify no existing record has this GUID
56
+ 2. Insert Record
57
+ └── Merges record with schema defaults, executes INSERT
58
+ 3. Validate Creation
59
+ └── Confirms the insert succeeded, extracts new ID
60
+ 4. Read Back Record
61
+ └── Fetches the complete record using the new ID
62
+ 5. Marshal to Object
63
+ └── Converts DB result to a plain JavaScript object
64
+ ```
65
+
66
+ ## GUID Uniqueness
67
+
68
+ If your record includes a GUID value (and the value is at least 5 characters), Meadow automatically checks for uniqueness before inserting:
69
+
70
+ ```javascript
71
+ const tmpQuery = meadow.query.addRecord(
72
+ {
73
+ GUIDBook: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
74
+ Title: 'Dune'
75
+ });
76
+
77
+ meadow.doCreate(tmpQuery,
78
+ (pError, pCreateQuery, pReadQuery, pRecord) =>
79
+ {
80
+ // If the GUID already exists, pError will contain:
81
+ // "Record with GUID a1b2c3d4-e5f6-7890-abcd-ef1234567890 already exists!"
82
+ });
83
+ ```
84
+
85
+ The GUID check searches across all records, including soft-deleted ones (it sets `disableDeleteTracking` to `true` for the uniqueness query). If you do not provide a GUID, the `AutoGUID` schema type generates one automatically.
86
+
87
+ ## Auto-Populated Fields
88
+
89
+ When your schema includes these column types, Meadow populates them automatically during creation:
90
+
91
+ | Schema Type | Behavior on Create |
92
+ |-------------|-------------------|
93
+ | `AutoIdentity` | Generated by the database (auto-increment) |
94
+ | `AutoGUID` | Generated if not provided in the record |
95
+ | `CreateDate` | Set to `NOW()` by the database |
96
+ | `CreateIDUser` | Set to the current user ID |
97
+ | `UpdateDate` | Not set on create |
98
+ | `UpdateIDUser` | Not set on create |
99
+ | `Deleted` | Set to `0` (from schema defaults) |
100
+
101
+ ## Setting User Identity
102
+
103
+ The creating user ID is determined in this order of precedence:
104
+
105
+ ```javascript
106
+ // 1. Set on the query object directly
107
+ const tmpQuery = meadow.query.addRecord({ Title: 'Dune' });
108
+ tmpQuery.query.IDUser = 42;
109
+
110
+ // 2. Set on the Meadow instance
111
+ meadow.setIDUser(42);
112
+ // This applies to all subsequent operations
113
+
114
+ // 3. Set via the userID property on the query parameters
115
+ const tmpQuery = meadow.query.addRecord({ Title: 'Dune' });
116
+ tmpQuery.parameters.userID = 42;
117
+ ```
118
+
119
+ ## Creating with Specific Fields
120
+
121
+ Only fields present in your schema are included in the INSERT statement. Extra fields are ignored:
122
+
123
+ ```javascript
124
+ const tmpQuery = meadow.query.addRecord(
125
+ {
126
+ Title: 'Foundation',
127
+ Author: 'Isaac Asimov',
128
+ NotASchemaField: 'this will be ignored'
129
+ });
130
+
131
+ meadow.doCreate(tmpQuery,
132
+ (pError, pCreateQuery, pReadQuery, pRecord) =>
133
+ {
134
+ // pRecord will not contain NotASchemaField
135
+ // It WILL contain all schema defaults plus auto-generated fields
136
+ });
137
+ ```
138
+
139
+ ## Error Handling
140
+
141
+ Common error conditions:
142
+
143
+ ```javascript
144
+ meadow.doCreate(tmpQuery,
145
+ (pError, pCreateQuery, pReadQuery, pRecord) =>
146
+ {
147
+ if (pError)
148
+ {
149
+ // Possible errors:
150
+ // - "No record submitted" (empty or missing addRecord)
151
+ // - "Creation failed" (database insert error)
152
+ // - "Record with GUID ... already exists!" (duplicate GUID)
153
+ // - Provider-specific database errors
154
+ console.log('Error:', pError);
155
+ }
156
+ });
157
+ ```
158
+
159
+ ## Raw Query Override
160
+
161
+ The Create operation does not support raw query overrides for the INSERT step. However, the read-back step (which fetches the newly created record) does respect the `Read` query override:
162
+
163
+ ```javascript
164
+ // This will be used when reading back the created record
165
+ meadow.rawQueries.setQuery('Read',
166
+ 'SELECT b.*, a.Name AS AuthorName FROM Book b JOIN Author a ON b.IDAuthor = a.IDAuthor WHERE b.IDBook = :IDBook');
167
+ ```
168
+
169
+ ## Full Example
170
+
171
+ ```javascript
172
+ const libFable = require('fable').new();
173
+ const libMeadow = require('meadow');
174
+
175
+ const meadow = libMeadow.new(libFable, 'Book')
176
+ .setProvider('MySQL')
177
+ .setDefaultIdentifier('IDBook')
178
+ .setSchema([
179
+ { Column: 'IDBook', Type: 'AutoIdentity' },
180
+ { Column: 'GUIDBook', Type: 'AutoGUID' },
181
+ { Column: 'Title', Type: 'String', Size: '255' },
182
+ { Column: 'Author', Type: 'String', Size: '128' },
183
+ { Column: 'Price', Type: 'Decimal', Size: '18,2' },
184
+ { Column: 'CreateDate', Type: 'CreateDate' },
185
+ { Column: 'CreatingIDUser', Type: 'CreateIDUser' },
186
+ { Column: 'UpdateDate', Type: 'UpdateDate' },
187
+ { Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
188
+ { Column: 'Deleted', Type: 'Deleted' }
189
+ ]);
190
+
191
+ // Set the user performing the operation
192
+ meadow.setIDUser(1);
193
+
194
+ // Build the create query
195
+ const tmpQuery = meadow.query.addRecord(
196
+ {
197
+ Title: 'Snow Crash',
198
+ Author: 'Neal Stephenson',
199
+ Price: 14.99
200
+ });
201
+
202
+ // Execute the create
203
+ meadow.doCreate(tmpQuery,
204
+ (pError, pCreateQuery, pReadQuery, pRecord) =>
205
+ {
206
+ if (pError)
207
+ {
208
+ return console.log('Failed to create book:', pError);
209
+ }
210
+
211
+ // pRecord now contains the full record:
212
+ // {
213
+ // IDBook: 1, (auto-generated)
214
+ // GUIDBook: '0x...', (auto-generated)
215
+ // Title: 'Snow Crash',
216
+ // Author: 'Neal Stephenson',
217
+ // Price: 14.99,
218
+ // CreateDate: '2024-01-15...', (auto-generated)
219
+ // CreatingIDUser: 1, (from setIDUser)
220
+ // UpdateDate: null,
221
+ // UpdatingIDUser: 0,
222
+ // Deleted: 0
223
+ // }
224
+ console.log('Book created:', pRecord.IDBook, '-', pRecord.Title);
225
+ });
226
+ ```
@@ -0,0 +1,264 @@
1
+ # Delete
2
+
3
+ > Soft delete, hard delete, and undelete records
4
+
5
+ Meadow supports both soft deletes (logical deletion) and hard deletes (physical removal), depending on your schema. When your schema includes a `Deleted` column, delete operations set a flag rather than removing the record. Meadow also provides an undelete operation to restore soft-deleted records.
6
+
7
+ ## doDelete
8
+
9
+ ### Method Signature
10
+
11
+ ```javascript
12
+ meadow.doDelete(pQuery, fCallBack)
13
+ ```
14
+
15
+ ### Callback
16
+
17
+ ```javascript
18
+ fCallBack(pError, pQuery, pCount)
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
+ | `pCount` | number | Number of affected records |
26
+
27
+ ### Basic Usage
28
+
29
+ ```javascript
30
+ meadow.doDelete(meadow.query.addFilter('IDBook', 42),
31
+ (pError, pQuery, pCount) =>
32
+ {
33
+ if (pError)
34
+ {
35
+ return console.log('Delete failed:', pError);
36
+ }
37
+ console.log('Deleted', pCount, 'record(s)');
38
+ });
39
+ ```
40
+
41
+ ## Soft Delete vs Hard Delete
42
+
43
+ The behavior depends on whether your schema includes a `Deleted` column:
44
+
45
+ ### Soft Delete (Schema has `Deleted` field)
46
+
47
+ When the schema includes `{ Column: 'Deleted', Type: 'Deleted' }`, the delete operation generates an UPDATE statement instead of a DELETE:
48
+
49
+ ```sql
50
+ -- What Meadow generates for soft delete:
51
+ UPDATE Book SET Deleted = 1, DeleteDate = NOW(), DeletingIDUser = :IDUser,
52
+ UpdateDate = NOW() WHERE IDBook = :IDBook
53
+ ```
54
+
55
+ The record remains in the database but is excluded from normal queries.
56
+
57
+ ```javascript
58
+ // Schema includes Deleted column
59
+ const meadow = libMeadow.new(libFable, 'Book')
60
+ .setDefaultIdentifier('IDBook')
61
+ .setSchema([
62
+ { Column: 'IDBook', Type: 'AutoIdentity' },
63
+ { Column: 'Title', Type: 'String', Size: '255' },
64
+ { Column: 'DeleteDate', Type: 'DeleteDate' },
65
+ { Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
66
+ { Column: 'UpdateDate', Type: 'UpdateDate' },
67
+ { Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
68
+ { Column: 'Deleted', Type: 'Deleted' }
69
+ ]);
70
+
71
+ meadow.setIDUser(5);
72
+
73
+ meadow.doDelete(meadow.query.addFilter('IDBook', 42),
74
+ (pError, pQuery, pCount) =>
75
+ {
76
+ // Record 42 now has:
77
+ // Deleted = 1
78
+ // DeleteDate = NOW()
79
+ // DeletingIDUser = 5
80
+ // UpdateDate = NOW()
81
+ // The record is still in the database
82
+ });
83
+ ```
84
+
85
+ ### Hard Delete (Schema without `Deleted` field)
86
+
87
+ When there is no `Deleted` column in the schema, the delete operation generates an actual DELETE statement:
88
+
89
+ ```sql
90
+ -- What Meadow generates for hard delete:
91
+ DELETE FROM Book WHERE IDBook = :IDBook
92
+ ```
93
+
94
+ The record is physically removed from the database.
95
+
96
+ ### Auto-Populated Fields on Soft Delete
97
+
98
+ | Schema Type | Behavior on Delete |
99
+ |-------------|-------------------|
100
+ | `Deleted` | Set to `1` |
101
+ | `DeleteDate` | Set to `NOW()` |
102
+ | `DeleteIDUser` | Set to the current user ID |
103
+ | `UpdateDate` | Set to `NOW()` (a delete is an update) |
104
+ | `UpdateIDUser` | Set to the current user ID |
105
+ | All others | **Not modified** |
106
+
107
+ ## Soft Delete and Read Queries
108
+
109
+ After soft deletion, the record is automatically excluded from normal reads:
110
+
111
+ ```javascript
112
+ // This will NOT find the soft-deleted book
113
+ meadow.doRead(meadow.query.addFilter('IDBook', 42),
114
+ (pError, pQuery, pRecord) =>
115
+ {
116
+ // pRecord is undefined -- the book appears "deleted"
117
+ });
118
+
119
+ // To find soft-deleted records, disable delete tracking
120
+ meadow.doRead(meadow.query.addFilter('IDBook', 42).setDisableDeleteTracking(true),
121
+ (pError, pQuery, pRecord) =>
122
+ {
123
+ // pRecord is the soft-deleted book with Deleted = 1
124
+ });
125
+ ```
126
+
127
+ ---
128
+
129
+ ## doUndelete
130
+
131
+ ### Method Signature
132
+
133
+ ```javascript
134
+ meadow.doUndelete(pQuery, fCallBack)
135
+ ```
136
+
137
+ ### Callback
138
+
139
+ ```javascript
140
+ fCallBack(pError, pQuery, pCount)
141
+ ```
142
+
143
+ | Parameter | Type | Description |
144
+ |-----------|------|-------------|
145
+ | `pError` | object/null | Error object if the operation failed, null on success |
146
+ | `pQuery` | object | The query that was executed |
147
+ | `pCount` | number | Number of affected records |
148
+
149
+ ### Basic Usage
150
+
151
+ ```javascript
152
+ meadow.doUndelete(meadow.query.addFilter('IDBook', 42),
153
+ (pError, pQuery, pCount) =>
154
+ {
155
+ if (pError)
156
+ {
157
+ return console.log('Undelete failed:', pError);
158
+ }
159
+ console.log('Restored', pCount, 'record(s)');
160
+ });
161
+ ```
162
+
163
+ ### How Undelete Works
164
+
165
+ The undelete operation generates an UPDATE statement that reverses the soft delete:
166
+
167
+ ```sql
168
+ UPDATE Book SET Deleted = 0, UpdateDate = NOW(), UpdatingIDUser = :IDUser
169
+ WHERE IDBook = :IDBook
170
+ ```
171
+
172
+ Note that `DeleteDate` and `DeletingIDUser` are **not cleared** -- they remain as a historical record of when the record was deleted.
173
+
174
+ ### Auto-Populated Fields on Undelete
175
+
176
+ | Schema Type | Behavior on Undelete |
177
+ |-------------|---------------------|
178
+ | `Deleted` | Set to `0` |
179
+ | `UpdateDate` | Set to `NOW()` |
180
+ | `UpdateIDUser` | Set to the current user ID |
181
+ | `DeleteDate` | **Not modified** (preserved as history) |
182
+ | `DeleteIDUser` | **Not modified** (preserved as history) |
183
+
184
+ ---
185
+
186
+ ## Raw Query Override
187
+
188
+ Both delete and undelete operations respect their respective raw query overrides:
189
+
190
+ ```javascript
191
+ // Override the delete query
192
+ meadow.rawQueries.setQuery('Delete',
193
+ 'UPDATE Book SET Deleted = 1, DeleteDate = NOW() WHERE IDBook = :IDBook AND IDCustomer = :IDCustomer');
194
+
195
+ // Override the undelete query
196
+ meadow.rawQueries.setQuery('Undelete',
197
+ 'UPDATE Book SET Deleted = 0, UpdateDate = NOW() WHERE IDBook = :IDBook AND IDCustomer = :IDCustomer');
198
+ ```
199
+
200
+ ## Full Example: Delete and Restore Workflow
201
+
202
+ ```javascript
203
+ const libFable = require('fable').new();
204
+ const libMeadow = require('meadow');
205
+
206
+ const meadow = libMeadow.new(libFable, 'Book')
207
+ .setProvider('MySQL')
208
+ .setDefaultIdentifier('IDBook')
209
+ .setSchema([
210
+ { Column: 'IDBook', Type: 'AutoIdentity' },
211
+ { Column: 'GUIDBook', Type: 'AutoGUID' },
212
+ { Column: 'Title', Type: 'String', Size: '255' },
213
+ { Column: 'CreateDate', Type: 'CreateDate' },
214
+ { Column: 'CreatingIDUser', Type: 'CreateIDUser' },
215
+ { Column: 'UpdateDate', Type: 'UpdateDate' },
216
+ { Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
217
+ { Column: 'DeleteDate', Type: 'DeleteDate' },
218
+ { Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
219
+ { Column: 'Deleted', Type: 'Deleted' }
220
+ ]);
221
+
222
+ meadow.setIDUser(5);
223
+
224
+ // Step 1: Soft delete book 42
225
+ meadow.doDelete(meadow.query.addFilter('IDBook', 42),
226
+ (pError, pQuery, pCount) =>
227
+ {
228
+ console.log('Soft deleted', pCount, 'record(s)');
229
+
230
+ // Step 2: Verify it's gone from normal queries
231
+ meadow.doRead(meadow.query.addFilter('IDBook', 42),
232
+ (pError, pQuery, pRecord) =>
233
+ {
234
+ console.log('Normal read found record:', !!pRecord);
235
+ // false -- record appears deleted
236
+
237
+ // Step 3: It's still there if we look for it
238
+ meadow.doRead(
239
+ meadow.query.addFilter('IDBook', 42).setDisableDeleteTracking(true),
240
+ (pError, pQuery, pRecord) =>
241
+ {
242
+ console.log('With delete tracking disabled:', pRecord.Title);
243
+ console.log('Deleted flag:', pRecord.Deleted);
244
+ // Deleted = 1, record is still in DB
245
+
246
+ // Step 4: Restore it
247
+ meadow.doUndelete(meadow.query.addFilter('IDBook', 42),
248
+ (pError, pQuery, pCount) =>
249
+ {
250
+ console.log('Restored', pCount, 'record(s)');
251
+
252
+ // Step 5: Now it shows up in normal queries again
253
+ meadow.doRead(meadow.query.addFilter('IDBook', 42),
254
+ (pError, pQuery, pRecord) =>
255
+ {
256
+ console.log('Restored:', pRecord.Title);
257
+ console.log('Deleted flag:', pRecord.Deleted);
258
+ // Deleted = 0, record is visible again
259
+ });
260
+ });
261
+ });
262
+ });
263
+ });
264
+ ```