meadow 2.0.22 → 2.0.26
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 +110 -141
- package/docs/README.md +34 -230
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +44 -12
- package/docs/_topbar.md +5 -0
- package/docs/api/doCount.md +109 -0
- package/docs/api/doCreate.md +132 -0
- package/docs/api/doDelete.md +101 -0
- package/docs/api/doRead.md +122 -0
- package/docs/api/doReads.md +136 -0
- package/docs/api/doUndelete.md +98 -0
- package/docs/api/doUpdate.md +129 -0
- package/docs/api/getRoleName.md +84 -0
- package/docs/api/loadFromPackage.md +153 -0
- package/docs/api/marshalRecordFromSourceToObject.md +92 -0
- package/docs/api/query.md +133 -0
- package/docs/api/rawQueries.md +197 -0
- package/docs/api/reference.md +117 -0
- package/docs/api/setAuthorizer.md +103 -0
- package/docs/api/setDefault.md +90 -0
- package/docs/api/setDefaultIdentifier.md +84 -0
- package/docs/api/setDomain.md +56 -0
- package/docs/api/setIDUser.md +91 -0
- package/docs/api/setJsonSchema.md +92 -0
- package/docs/api/setProvider.md +87 -0
- package/docs/api/setSchema.md +107 -0
- package/docs/api/setScope.md +68 -0
- package/docs/api/validateObject.md +119 -0
- package/docs/architecture.md +316 -0
- package/docs/audit-tracking.md +226 -0
- package/docs/configuration.md +317 -0
- package/docs/providers/meadow-endpoints.md +306 -0
- package/docs/providers/mongodb.md +319 -0
- package/docs/providers/postgresql.md +312 -0
- package/docs/providers/rocksdb.md +297 -0
- package/docs/query-dsl.md +269 -0
- package/docs/quick-start.md +384 -0
- package/docs/raw-queries.md +193 -0
- package/docs/retold-catalog.json +61 -1
- package/docs/retold-keyword-index.json +15860 -4839
- package/docs/soft-deletes.md +224 -0
- package/package.json +44 -27
- package/scripts/bookstore-seed-postgresql.sql +135 -0
- package/scripts/dgraph-test-db.sh +144 -0
- package/scripts/meadow-test-cleanup.sh +5 -1
- package/scripts/mongodb-test-db.sh +98 -0
- package/scripts/postgresql-test-db.sh +124 -0
- package/scripts/solr-test-db.sh +135 -0
- package/source/Meadow.js +5 -0
- package/source/providers/Meadow-Provider-DGraph.js +679 -0
- package/source/providers/Meadow-Provider-MongoDB.js +527 -0
- package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
- package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
- package/source/providers/Meadow-Provider-Solr.js +726 -0
- package/test/Meadow-Provider-DGraph_tests.js +741 -0
- package/test/Meadow-Provider-MongoDB_tests.js +661 -0
- package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
- package/test/Meadow-Provider-RocksDB_tests.js +887 -0
- package/test/Meadow-Provider-Solr_tests.js +679 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# MongoDB Provider
|
|
2
|
+
|
|
3
|
+
> Document-based storage with automatic counter management and sentinel value support
|
|
4
|
+
|
|
5
|
+
The MongoDB provider connects Meadow to MongoDB databases via the [meadow-connection-mongodb](https://github.com/stevenvelozo/meadow-connection-mongodb) module. It uses the FoxHound MongoDB dialect for query generation, providing document-based storage without schema migration. Auto-increment counters, GUID generation, and timestamp sentinels are handled automatically.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### Install Dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install meadow meadow-connection-mongodb
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Register the Connection
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const libFable = require('fable').new(
|
|
19
|
+
{
|
|
20
|
+
MongoDB:
|
|
21
|
+
{
|
|
22
|
+
URL: 'mongodb://localhost:27017',
|
|
23
|
+
Database: 'myapp'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const libMeadowConnectionMongoDB = require('meadow-connection-mongodb');
|
|
28
|
+
|
|
29
|
+
// Register the connection service
|
|
30
|
+
libFable.serviceManager.addServiceType('MeadowMongoDBProvider', libMeadowConnectionMongoDB);
|
|
31
|
+
libFable.serviceManager.instantiateServiceProvider('MeadowMongoDBProvider');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Create a Meadow DAL
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const libMeadow = require('meadow');
|
|
38
|
+
|
|
39
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
40
|
+
.setProvider('MongoDB')
|
|
41
|
+
.setDefaultIdentifier('IDBook')
|
|
42
|
+
.setSchema([
|
|
43
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
44
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
45
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
46
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
47
|
+
{ Column: 'PageCount', Type: 'Numeric' },
|
|
48
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
49
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
50
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
51
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
52
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
53
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
54
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
55
|
+
]);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Connection Management
|
|
59
|
+
|
|
60
|
+
### Connection Configuration
|
|
61
|
+
|
|
62
|
+
| Setting | Type | Default | Description |
|
|
63
|
+
|---------|------|---------|-------------|
|
|
64
|
+
| `MongoDB.URL` | string | -- | MongoDB connection URL |
|
|
65
|
+
| `MongoDB.Database` | string | -- | Database name |
|
|
66
|
+
|
|
67
|
+
### No Schema Migration
|
|
68
|
+
|
|
69
|
+
Unlike SQL-based providers, MongoDB does not require table creation or schema migration. Collections are created automatically when the first document is inserted. This makes MongoDB well-suited for rapid development and evolving data models.
|
|
70
|
+
|
|
71
|
+
## Document Storage
|
|
72
|
+
|
|
73
|
+
### The `_id` Field
|
|
74
|
+
|
|
75
|
+
MongoDB automatically assigns an `_id` field to every document. The Meadow MongoDB provider works alongside this native identifier while maintaining Meadow's own identity system (`IDBook`, `GUIDBook`, etc.). Your application code interacts with Meadow's identity columns as usual; the underlying `_id` is managed transparently.
|
|
76
|
+
|
|
77
|
+
### Auto-Increment Counters
|
|
78
|
+
|
|
79
|
+
MongoDB does not natively support auto-increment integer identifiers. The Meadow MongoDB provider implements auto-increment behavior using a dedicated `_meadow_counters` collection. When a record is created with an `AutoIdentity` column:
|
|
80
|
+
|
|
81
|
+
1. The provider queries the `_meadow_counters` collection for the current counter value
|
|
82
|
+
2. The counter is atomically incremented
|
|
83
|
+
3. The new value is assigned to the identity column on the inserted document
|
|
84
|
+
|
|
85
|
+
This ensures unique, sequential integer IDs across concurrent inserts.
|
|
86
|
+
|
|
87
|
+
### Sentinel Values
|
|
88
|
+
|
|
89
|
+
The MongoDB provider supports sentinel values that are resolved at write time:
|
|
90
|
+
|
|
91
|
+
| Sentinel | Resolved To | Description |
|
|
92
|
+
|----------|-------------|-------------|
|
|
93
|
+
| `$$NOW` | Current timestamp | Sets the field to the current date and time |
|
|
94
|
+
| `$$AUTOINCREMENT` | Next counter value | Assigns the next auto-increment integer from `_meadow_counters` |
|
|
95
|
+
| `$$AUTOGUID` | New GUID | Generates a new globally unique identifier |
|
|
96
|
+
|
|
97
|
+
Sentinels are placed in record fields and expanded by the provider before the document is written to MongoDB:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
meadow.doCreate(
|
|
101
|
+
meadow.query.addRecord(
|
|
102
|
+
{
|
|
103
|
+
Title: 'Dune',
|
|
104
|
+
Author: 'Frank Herbert',
|
|
105
|
+
CreateDate: '$$NOW',
|
|
106
|
+
GUIDBook: '$$AUTOGUID'
|
|
107
|
+
}),
|
|
108
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
109
|
+
{
|
|
110
|
+
console.log('Created at:', pRecord.CreateDate);
|
|
111
|
+
console.log('GUID:', pRecord.GUIDBook);
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## CRUD Operations
|
|
116
|
+
|
|
117
|
+
### Create
|
|
118
|
+
|
|
119
|
+
Inserts a new document into the collection and returns the auto-generated identity value.
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
meadow.doCreate(
|
|
123
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' }),
|
|
124
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
125
|
+
{
|
|
126
|
+
console.log('New ID:', pRecord.IDBook);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Internally:**
|
|
131
|
+
- Builds query: `pQuery.setDialect('MongoDB').buildCreateQuery()`
|
|
132
|
+
- Resolves sentinel values ($$NOW, $$AUTOINCREMENT, $$AUTOGUID)
|
|
133
|
+
- Increments the counter in `_meadow_counters`
|
|
134
|
+
- Inserts the document into the collection
|
|
135
|
+
|
|
136
|
+
### Read
|
|
137
|
+
|
|
138
|
+
Queries the collection and returns matching documents.
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
// Single record
|
|
142
|
+
meadow.doRead(
|
|
143
|
+
meadow.query.addFilter('IDBook', 42),
|
|
144
|
+
(pError, pQuery, pRecord) =>
|
|
145
|
+
{
|
|
146
|
+
console.log('Title:', pRecord.Title);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Multiple records
|
|
150
|
+
meadow.doReads(
|
|
151
|
+
meadow.query.setCap(25).setBegin(0).addSort({ Column: 'Title', Direction: 'ASC' }),
|
|
152
|
+
(pError, pQuery, pRecords) =>
|
|
153
|
+
{
|
|
154
|
+
pRecords.forEach((pBook) => console.log(pBook.Title));
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Internally:**
|
|
159
|
+
- Builds query: `pQuery.setDialect('MongoDB').buildReadQuery()`
|
|
160
|
+
- Translates filters to MongoDB query objects
|
|
161
|
+
- Applies sort, skip, and limit from the query parameters
|
|
162
|
+
|
|
163
|
+
### Update
|
|
164
|
+
|
|
165
|
+
Updates matching documents in the collection.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
meadow.doUpdate(
|
|
169
|
+
meadow.query
|
|
170
|
+
.addFilter('IDBook', 42)
|
|
171
|
+
.addRecord({ Title: 'Updated Title' }),
|
|
172
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
173
|
+
{
|
|
174
|
+
console.log('Updated:', pRecord.Title);
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Internally:**
|
|
179
|
+
- Builds query: `pQuery.setDialect('MongoDB').buildUpdateQuery()`
|
|
180
|
+
- Resolves sentinel values before applying the update
|
|
181
|
+
|
|
182
|
+
### Delete
|
|
183
|
+
|
|
184
|
+
Executes a soft delete (sets the Deleted flag) and returns the affected document count.
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
meadow.doDelete(
|
|
188
|
+
meadow.query.addFilter('IDBook', 42),
|
|
189
|
+
(pError, pQuery, pResult) =>
|
|
190
|
+
{
|
|
191
|
+
console.log('Deleted documents:', pResult);
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Internally:**
|
|
196
|
+
- Builds query: `pQuery.setDialect('MongoDB').buildDeleteQuery()`
|
|
197
|
+
- Updates the Deleted field rather than removing the document
|
|
198
|
+
|
|
199
|
+
### Undelete
|
|
200
|
+
|
|
201
|
+
Reverses a soft delete.
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
meadow.doUndelete(
|
|
205
|
+
meadow.query.addFilter('IDBook', 42),
|
|
206
|
+
(pError, pQuery, pResult) =>
|
|
207
|
+
{
|
|
208
|
+
console.log('Restored documents:', pResult);
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Count
|
|
213
|
+
|
|
214
|
+
Returns the count of matching documents.
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
meadow.doCount(
|
|
218
|
+
meadow.query.addFilter('Author', 'Frank Herbert', '='),
|
|
219
|
+
(pError, pQuery, pCount) =>
|
|
220
|
+
{
|
|
221
|
+
console.log('Books by Herbert:', pCount);
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Internally:**
|
|
226
|
+
- Builds query: `pQuery.setDialect('MongoDB').buildCountQuery()`
|
|
227
|
+
- Returns the integer count of matching documents
|
|
228
|
+
|
|
229
|
+
## Full Setup Example
|
|
230
|
+
|
|
231
|
+
A complete working example connecting to MongoDB and performing CRUD operations:
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
const libFable = require('fable').new(
|
|
235
|
+
{
|
|
236
|
+
MongoDB:
|
|
237
|
+
{
|
|
238
|
+
URL: 'mongodb://localhost:27017',
|
|
239
|
+
Database: 'bookstore'
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const libMeadow = require('meadow');
|
|
244
|
+
const libMeadowConnectionMongoDB = require('meadow-connection-mongodb');
|
|
245
|
+
|
|
246
|
+
// Register the connection service
|
|
247
|
+
libFable.serviceManager.addServiceType('MeadowMongoDBProvider', libMeadowConnectionMongoDB);
|
|
248
|
+
libFable.serviceManager.instantiateServiceProvider('MeadowMongoDBProvider');
|
|
249
|
+
|
|
250
|
+
// Create the DAL
|
|
251
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
252
|
+
.setProvider('MongoDB')
|
|
253
|
+
.setDefaultIdentifier('IDBook')
|
|
254
|
+
.setSchema([
|
|
255
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
256
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
257
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
258
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
259
|
+
{ Column: 'PageCount', Type: 'Numeric' },
|
|
260
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
261
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
262
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
263
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
264
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
265
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
266
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
267
|
+
]);
|
|
268
|
+
|
|
269
|
+
// Insert a record
|
|
270
|
+
meadow.doCreate(
|
|
271
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert', PageCount: 412 }),
|
|
272
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
273
|
+
{
|
|
274
|
+
console.log('Created book with ID:', pRecord.IDBook);
|
|
275
|
+
console.log('GUID:', pRecord.GUIDBook);
|
|
276
|
+
|
|
277
|
+
// Read it back
|
|
278
|
+
meadow.doRead(
|
|
279
|
+
meadow.query.addFilter('IDBook', pRecord.IDBook),
|
|
280
|
+
(pError, pQuery, pRecord) =>
|
|
281
|
+
{
|
|
282
|
+
console.log('Read back:', pRecord.Title, 'by', pRecord.Author);
|
|
283
|
+
|
|
284
|
+
// Count all books
|
|
285
|
+
meadow.doCount(
|
|
286
|
+
meadow.query,
|
|
287
|
+
(pError, pQuery, pCount) =>
|
|
288
|
+
{
|
|
289
|
+
console.log('Total books in collection:', pCount);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Error Handling
|
|
296
|
+
|
|
297
|
+
All operations follow the same error handling pattern:
|
|
298
|
+
|
|
299
|
+
- Database errors are stored in `pQuery.parameters.result.error`
|
|
300
|
+
- Connection errors bubble up through the callback as `pError`
|
|
301
|
+
- Counter increment failures are logged and reported through the error callback
|
|
302
|
+
|
|
303
|
+
## Docker Development
|
|
304
|
+
|
|
305
|
+
For local MongoDB development:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
docker run -d \
|
|
309
|
+
--name meadow-mongodb \
|
|
310
|
+
-p 27017:27017 \
|
|
311
|
+
mongo:7
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Related Documentation
|
|
315
|
+
|
|
316
|
+
- [Providers Overview](providers/README.md) -- Comparison of all providers
|
|
317
|
+
- [MySQL Provider](providers/mysql.md) -- Relational database alternative
|
|
318
|
+
- [ALASQL Provider](providers/alasql.md) -- In-memory alternative
|
|
319
|
+
- [meadow-connection-mongodb](https://github.com/stevenvelozo/meadow-connection-mongodb) -- Connection module source
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# PostgreSQL Provider
|
|
2
|
+
|
|
3
|
+
> Production-ready PostgreSQL integration with connection pooling and named parameter binding
|
|
4
|
+
|
|
5
|
+
The PostgreSQL provider connects Meadow to PostgreSQL databases via the [meadow-connection-postgresql](https://github.com/stevenvelozo/meadow-connection-postgresql) module. It uses the FoxHound PostgreSQL dialect for query generation and supports named parameter binding with positional placeholders ($1, $2, etc.).
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### Install Dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install meadow meadow-connection-postgresql
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Register the Connection
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const libFable = require('fable').new(
|
|
19
|
+
{
|
|
20
|
+
PostgreSQL:
|
|
21
|
+
{
|
|
22
|
+
Host: 'localhost',
|
|
23
|
+
Port: 5432,
|
|
24
|
+
User: 'postgres',
|
|
25
|
+
Password: '',
|
|
26
|
+
Database: 'myapp',
|
|
27
|
+
ConnectionPoolLimit: 20
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const libMeadowConnectionPostgreSQL = require('meadow-connection-postgresql');
|
|
32
|
+
|
|
33
|
+
// Register the connection service
|
|
34
|
+
libFable.serviceManager.addServiceType('MeadowPostgreSQLProvider', libMeadowConnectionPostgreSQL);
|
|
35
|
+
const tmpConnection = libFable.serviceManager.instantiateServiceProvider('MeadowPostgreSQLProvider');
|
|
36
|
+
|
|
37
|
+
// Connect asynchronously
|
|
38
|
+
tmpConnection.connectAsync(
|
|
39
|
+
(pError) =>
|
|
40
|
+
{
|
|
41
|
+
if (pError)
|
|
42
|
+
{
|
|
43
|
+
return console.error('PostgreSQL connection failed:', pError);
|
|
44
|
+
}
|
|
45
|
+
console.log('Connected to PostgreSQL');
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Create a Meadow DAL
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const libMeadow = require('meadow');
|
|
53
|
+
|
|
54
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
55
|
+
.setProvider('PostgreSQL')
|
|
56
|
+
.setDefaultIdentifier('IDBook')
|
|
57
|
+
.setSchema([
|
|
58
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
59
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
60
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
61
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
62
|
+
{ Column: 'Price', Type: 'Decimal', Size: '12,2' },
|
|
63
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
64
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
65
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
66
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
67
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
68
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
69
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
70
|
+
]);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Connection Management
|
|
74
|
+
|
|
75
|
+
### Async Connection Model
|
|
76
|
+
|
|
77
|
+
The PostgreSQL provider uses an asynchronous connection model. The connection module provides:
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|--------|-------------|
|
|
81
|
+
| `connectAsync(fCallback)` | Async connection with callback on completion |
|
|
82
|
+
|
|
83
|
+
### Connection Pool Settings
|
|
84
|
+
|
|
85
|
+
| Setting | Type | Default | Description |
|
|
86
|
+
|---------|------|---------|-------------|
|
|
87
|
+
| `PostgreSQL.Host` | string | -- | Database server hostname |
|
|
88
|
+
| `PostgreSQL.Port` | number | `5432` | Database server port |
|
|
89
|
+
| `PostgreSQL.User` | string | -- | Database user |
|
|
90
|
+
| `PostgreSQL.Password` | string | -- | Database password |
|
|
91
|
+
| `PostgreSQL.Database` | string | -- | Database name |
|
|
92
|
+
| `PostgreSQL.ConnectionPoolLimit` | number | `20` | Maximum pool connections |
|
|
93
|
+
|
|
94
|
+
### Named Parameter Binding
|
|
95
|
+
|
|
96
|
+
The PostgreSQL provider uses positional parameter placeholders ($1, $2, etc.) as required by the PostgreSQL wire protocol. FoxHound's PostgreSQL dialect generates queries with these positional bindings:
|
|
97
|
+
|
|
98
|
+
```sql
|
|
99
|
+
-- Generated by FoxHound with positional placeholders:
|
|
100
|
+
SELECT "IDBook", "Title", "Author" FROM "Book" WHERE "IDBook" = $1 AND "Deleted" = 0
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## CRUD Operations
|
|
104
|
+
|
|
105
|
+
### Create
|
|
106
|
+
|
|
107
|
+
Executes an INSERT statement with a RETURNING clause to capture the generated identity.
|
|
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('PostgreSQL').buildCreateQuery()`
|
|
120
|
+
- Extracts identity from the RETURNING clause result
|
|
121
|
+
|
|
122
|
+
### Read
|
|
123
|
+
|
|
124
|
+
Executes a SELECT statement and returns result rows.
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// Single record
|
|
128
|
+
meadow.doRead(
|
|
129
|
+
meadow.query.addFilter('IDBook', 42),
|
|
130
|
+
(pError, pQuery, pRecord) =>
|
|
131
|
+
{
|
|
132
|
+
console.log('Title:', pRecord.Title);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Multiple records
|
|
136
|
+
meadow.doReads(
|
|
137
|
+
meadow.query.setCap(25).setBegin(0).addSort({ Column: 'Title', Direction: 'ASC' }),
|
|
138
|
+
(pError, pQuery, pRecords) =>
|
|
139
|
+
{
|
|
140
|
+
pRecords.forEach((pBook) => console.log(pBook.Title));
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Internally:**
|
|
145
|
+
- Builds query: `pQuery.setDialect('PostgreSQL').buildReadQuery()`
|
|
146
|
+
- Returns full result rows array
|
|
147
|
+
|
|
148
|
+
### Update
|
|
149
|
+
|
|
150
|
+
Executes an UPDATE statement.
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
meadow.doUpdate(
|
|
154
|
+
meadow.query
|
|
155
|
+
.addFilter('IDBook', 42)
|
|
156
|
+
.addRecord({ Title: 'Updated Title' }),
|
|
157
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
158
|
+
{
|
|
159
|
+
console.log('Updated:', pRecord.Title);
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Internally:**
|
|
164
|
+
- Builds query: `pQuery.setDialect('PostgreSQL').buildUpdateQuery()`
|
|
165
|
+
|
|
166
|
+
### Delete
|
|
167
|
+
|
|
168
|
+
Executes a soft delete (UPDATE setting Deleted flag) and returns the affected row count.
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
meadow.doDelete(
|
|
172
|
+
meadow.query.addFilter('IDBook', 42),
|
|
173
|
+
(pError, pQuery, pResult) =>
|
|
174
|
+
{
|
|
175
|
+
console.log('Deleted rows:', pResult);
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Internally:**
|
|
180
|
+
- Builds query: `pQuery.setDialect('PostgreSQL').buildDeleteQuery()`
|
|
181
|
+
- Extracts affected rows from the result
|
|
182
|
+
|
|
183
|
+
### Undelete
|
|
184
|
+
|
|
185
|
+
Reverses a soft delete and returns the affected row count.
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
meadow.doUndelete(
|
|
189
|
+
meadow.query.addFilter('IDBook', 42),
|
|
190
|
+
(pError, pQuery, pResult) =>
|
|
191
|
+
{
|
|
192
|
+
console.log('Restored rows:', pResult);
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Internally:**
|
|
197
|
+
- Builds query: `pQuery.setDialect('PostgreSQL').buildUndeleteQuery()`
|
|
198
|
+
- Extracts affected rows from the result
|
|
199
|
+
|
|
200
|
+
### Count
|
|
201
|
+
|
|
202
|
+
Executes a COUNT query and returns the integer count.
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
meadow.doCount(
|
|
206
|
+
meadow.query.addFilter('Author', 'Frank Herbert', '='),
|
|
207
|
+
(pError, pQuery, pCount) =>
|
|
208
|
+
{
|
|
209
|
+
console.log('Books by Herbert:', pCount);
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Internally:**
|
|
214
|
+
- Builds query: `pQuery.setDialect('PostgreSQL').buildCountQuery()`
|
|
215
|
+
- Extracts count from the result
|
|
216
|
+
|
|
217
|
+
## Full Setup Example
|
|
218
|
+
|
|
219
|
+
A complete working example connecting to PostgreSQL and performing CRUD operations:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
const libFable = require('fable').new(
|
|
223
|
+
{
|
|
224
|
+
PostgreSQL:
|
|
225
|
+
{
|
|
226
|
+
Host: 'localhost',
|
|
227
|
+
Port: 5432,
|
|
228
|
+
User: 'postgres',
|
|
229
|
+
Password: 'secret',
|
|
230
|
+
Database: 'bookstore',
|
|
231
|
+
ConnectionPoolLimit: 10
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const libMeadow = require('meadow');
|
|
236
|
+
const libMeadowConnectionPostgreSQL = require('meadow-connection-postgresql');
|
|
237
|
+
|
|
238
|
+
// Register and connect
|
|
239
|
+
libFable.serviceManager.addServiceType('MeadowPostgreSQLProvider', libMeadowConnectionPostgreSQL);
|
|
240
|
+
const tmpConnection = libFable.serviceManager.instantiateServiceProvider('MeadowPostgreSQLProvider');
|
|
241
|
+
|
|
242
|
+
tmpConnection.connectAsync(
|
|
243
|
+
(pError) =>
|
|
244
|
+
{
|
|
245
|
+
if (pError)
|
|
246
|
+
{
|
|
247
|
+
return console.error('Connection failed:', pError);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Create the DAL
|
|
251
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
252
|
+
.setProvider('PostgreSQL')
|
|
253
|
+
.setDefaultIdentifier('IDBook')
|
|
254
|
+
.setSchema([
|
|
255
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
256
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
257
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
258
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
259
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
260
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
261
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
262
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
263
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
264
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
265
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
266
|
+
]);
|
|
267
|
+
|
|
268
|
+
// Insert a record
|
|
269
|
+
meadow.doCreate(
|
|
270
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' }),
|
|
271
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
272
|
+
{
|
|
273
|
+
console.log('Created book with ID:', pRecord.IDBook);
|
|
274
|
+
|
|
275
|
+
// Read it back
|
|
276
|
+
meadow.doRead(
|
|
277
|
+
meadow.query.addFilter('IDBook', pRecord.IDBook),
|
|
278
|
+
(pError, pQuery, pRecord) =>
|
|
279
|
+
{
|
|
280
|
+
console.log('Read back:', pRecord.Title, 'by', pRecord.Author);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Error Handling
|
|
287
|
+
|
|
288
|
+
All operations follow the same error handling pattern:
|
|
289
|
+
|
|
290
|
+
- Database errors are stored in `pQuery.parameters.result.error`
|
|
291
|
+
- Identity/rowcount extraction failures are logged as warnings
|
|
292
|
+
- Connection errors bubble up through the callback as `pError`
|
|
293
|
+
|
|
294
|
+
## Docker Development
|
|
295
|
+
|
|
296
|
+
For local PostgreSQL development:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
docker run -d \
|
|
300
|
+
--name meadow-postgresql \
|
|
301
|
+
-e POSTGRES_PASSWORD=secret \
|
|
302
|
+
-e POSTGRES_DB=myapp \
|
|
303
|
+
-p 5432:5432 \
|
|
304
|
+
postgres:16
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Related Documentation
|
|
308
|
+
|
|
309
|
+
- [Providers Overview](providers/README.md) -- Comparison of all providers
|
|
310
|
+
- [MySQL Provider](providers/mysql.md) -- MySQL/MariaDB alternative
|
|
311
|
+
- [MSSQL Provider](providers/mssql.md) -- Microsoft SQL Server alternative
|
|
312
|
+
- [meadow-connection-postgresql](https://github.com/stevenvelozo/meadow-connection-postgresql) -- Connection module source
|