meadow-connection-sqlite 1.0.12 → 1.0.14
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 +14 -8
- package/docs/README.md +9 -0
- package/docs/{cover.md → _cover.md} +6 -2
- package/docs/_sidebar.md +19 -2
- package/docs/_topbar.md +4 -1
- package/docs/api/SQLite.md +55 -0
- package/docs/api/connect.md +66 -0
- package/docs/api/connectAsync.md +121 -0
- package/docs/api/createTable.md +96 -0
- package/docs/api/createTables.md +91 -0
- package/docs/api/db.md +145 -0
- package/docs/api/generateCreateTableStatement.md +109 -0
- package/docs/api/generateDropTableStatement.md +74 -0
- package/docs/api/preparedStatement.md +67 -0
- package/docs/api/reference.md +192 -0
- package/docs/api.md +13 -252
- package/docs/architecture.md +250 -0
- package/docs/quickstart.md +197 -0
- package/docs/retold-catalog.json +22 -1
- package/docs/retold-keyword-index.json +5818 -5
- package/docs/schema.md +198 -0
- package/package.json +6 -6
package/docs/api.md
CHANGED
|
@@ -1,254 +1,15 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| `pManifest` | object | Service manifest / options (optional) |
|
|
17
|
-
| `pServiceHash` | string | Service identifier |
|
|
18
|
-
|
|
19
|
-
On construction:
|
|
20
|
-
|
|
21
|
-
- Sets `serviceType` to `'MeadowConnectionSQLite'`
|
|
22
|
-
- Sets `connected` to `false`
|
|
23
|
-
- Reads `SQLiteFilePath` from `fable.settings.SQLite` if available
|
|
24
|
-
|
|
25
|
-
The provider is not yet connected after construction — call `connectAsync()` to open the database.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Properties
|
|
30
|
-
|
|
31
|
-
### connected
|
|
32
|
-
|
|
33
|
-
Whether the database connection is open.
|
|
34
|
-
|
|
35
|
-
**Type:** `boolean`
|
|
36
|
-
|
|
37
|
-
### db
|
|
38
|
-
|
|
39
|
-
The raw `better-sqlite3` `Database` instance. Use this for all query operations. Returns `false` before `connectAsync()` is called.
|
|
40
|
-
|
|
41
|
-
**Type:** `Database | false`
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
let tmpDB = _Fable.MeadowSQLiteProvider.db;
|
|
45
|
-
|
|
46
|
-
// Synchronous query methods from better-sqlite3:
|
|
47
|
-
tmpDB.prepare(sql).run(params); // INSERT, UPDATE, DELETE
|
|
48
|
-
tmpDB.prepare(sql).get(params); // SELECT single row
|
|
49
|
-
tmpDB.prepare(sql).all(params); // SELECT all rows
|
|
50
|
-
tmpDB.exec(sql); // Execute raw SQL (multi-statement)
|
|
51
|
-
tmpDB.transaction(fn); // Wrap a function in a transaction
|
|
52
|
-
tmpDB.pragma(string); // Run a PRAGMA command
|
|
53
|
-
tmpDB.close(); // Close the database
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### SQLite
|
|
57
|
-
|
|
58
|
-
Reference to the `better-sqlite3` module constructor. Useful for accessing type constants or creating additional database instances.
|
|
59
|
-
|
|
60
|
-
**Type:** `function`
|
|
61
|
-
|
|
62
|
-
```javascript
|
|
63
|
-
let tmpSQLiteConstructor = _Fable.MeadowSQLiteProvider.SQLite;
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### serviceType
|
|
67
|
-
|
|
68
|
-
Always `'MeadowConnectionSQLite'`.
|
|
69
|
-
|
|
70
|
-
**Type:** `string`
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Methods
|
|
75
|
-
|
|
76
|
-
### connectAsync(fCallback)
|
|
77
|
-
|
|
78
|
-
Open a connection to the SQLite database file specified in configuration.
|
|
79
|
-
|
|
80
|
-
| Parameter | Type | Description |
|
|
81
|
-
|-----------|------|-------------|
|
|
82
|
-
| `fCallback` | function | Callback: `(error, database)` |
|
|
83
|
-
|
|
84
|
-
**Behavior:**
|
|
85
|
-
|
|
86
|
-
- If `SQLiteFilePath` is not configured, calls back with an error
|
|
87
|
-
- If already connected, calls back immediately with the existing database (idempotent)
|
|
88
|
-
- Creates the database file if it does not exist
|
|
89
|
-
- Enables WAL (Write-Ahead Logging) journal mode for performance
|
|
90
|
-
- Sets `this.connected = true` on success
|
|
91
|
-
|
|
92
|
-
```javascript
|
|
93
|
-
_Fable.MeadowSQLiteProvider.connectAsync(
|
|
94
|
-
(pError, pDatabase) =>
|
|
95
|
-
{
|
|
96
|
-
if (pError)
|
|
97
|
-
{
|
|
98
|
-
_Fable.log.error(`Connection failed: ${pError}`);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// pDatabase is the same as _Fable.MeadowSQLiteProvider.db
|
|
103
|
-
pDatabase.exec('CREATE TABLE IF NOT EXISTS Test (id INTEGER PRIMARY KEY)');
|
|
104
|
-
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### connect()
|
|
108
|
-
|
|
109
|
-
Synchronous wrapper that calls `connectAsync()` without a callback. Logs a warning about potential race conditions. Prefer `connectAsync()`.
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Configuration
|
|
114
|
-
|
|
115
|
-
### Fable Settings
|
|
116
|
-
|
|
117
|
-
```json
|
|
118
|
-
{
|
|
119
|
-
"SQLite":
|
|
120
|
-
{
|
|
121
|
-
"SQLiteFilePath": "./data/myapp.db"
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
| Setting | Type | Required | Description |
|
|
127
|
-
|---------|------|----------|-------------|
|
|
128
|
-
| `SQLiteFilePath` | string | Yes | File path or `:memory:` for in-memory databases |
|
|
129
|
-
|
|
130
|
-
Additional properties are passed through to the `better-sqlite3` constructor:
|
|
131
|
-
|
|
132
|
-
| Option | Type | Default | Description |
|
|
133
|
-
|--------|------|---------|-------------|
|
|
134
|
-
| `readonly` | boolean | `false` | Open the database in read-only mode |
|
|
135
|
-
| `fileMustExist` | boolean | `false` | Error if the file does not exist |
|
|
136
|
-
| `timeout` | number | `5000` | Milliseconds to wait when the database is locked |
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Service Registration
|
|
141
|
-
|
|
142
|
-
The provider integrates with Fable's service manager:
|
|
143
|
-
|
|
144
|
-
```javascript
|
|
145
|
-
const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
|
|
146
|
-
|
|
147
|
-
// Register the service type
|
|
148
|
-
_Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
|
|
149
|
-
|
|
150
|
-
// Instantiate (optionally with per-instance options)
|
|
151
|
-
_Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider',
|
|
152
|
-
{
|
|
153
|
-
SQLiteFilePath: './data/alternate.db',
|
|
154
|
-
readonly: true
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Access the provider
|
|
158
|
-
_Fable.MeadowSQLiteProvider.connectAsync((pError) => { /* ... */ });
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Constructor options override Fable settings, allowing multiple providers with different configurations.
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## better-sqlite3 Query API
|
|
166
|
-
|
|
167
|
-
After connecting, all queries go through the `db` getter. Here is a quick reference for the better-sqlite3 methods you will use most:
|
|
168
|
-
|
|
169
|
-
### Execute Raw SQL
|
|
170
|
-
|
|
171
|
-
```javascript
|
|
172
|
-
tmpDB.exec(`
|
|
173
|
-
CREATE TABLE IF NOT EXISTS Book (
|
|
174
|
-
IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
175
|
-
Title TEXT DEFAULT ''
|
|
176
|
-
);
|
|
177
|
-
CREATE INDEX IF NOT EXISTS idx_book_title ON Book(Title);
|
|
178
|
-
`);
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
`exec()` runs one or more SQL statements. It does not return data.
|
|
182
|
-
|
|
183
|
-
### Prepared Statements
|
|
184
|
-
|
|
185
|
-
```javascript
|
|
186
|
-
let tmpStmt = tmpDB.prepare('SELECT * FROM Book WHERE Author = ?');
|
|
187
|
-
|
|
188
|
-
// Single row
|
|
189
|
-
let tmpRow = tmpStmt.get('Frank Herbert');
|
|
190
|
-
|
|
191
|
-
// All rows
|
|
192
|
-
let tmpRows = tmpStmt.all('Frank Herbert');
|
|
193
|
-
|
|
194
|
-
// Modification
|
|
195
|
-
let tmpInsert = tmpDB.prepare('INSERT INTO Book (Title, Author) VALUES (?, ?)');
|
|
196
|
-
let tmpInfo = tmpInsert.run('Dune', 'Frank Herbert');
|
|
197
|
-
// tmpInfo.changes = 1, tmpInfo.lastInsertRowid = 1
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Named Parameters
|
|
201
|
-
|
|
202
|
-
```javascript
|
|
203
|
-
let tmpStmt = tmpDB.prepare('SELECT * FROM Book WHERE Author = @author AND YearPublished > @year');
|
|
204
|
-
let tmpRows = tmpStmt.all({ author: 'Isaac Asimov', year: 1950 });
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Transactions
|
|
208
|
-
|
|
209
|
-
```javascript
|
|
210
|
-
let tmpBulkInsert = tmpDB.transaction(
|
|
211
|
-
(pBooks) =>
|
|
212
|
-
{
|
|
213
|
-
let tmpStmt = tmpDB.prepare('INSERT INTO Book (Title, Author) VALUES (?, ?)');
|
|
214
|
-
for (let tmpBook of pBooks)
|
|
215
|
-
{
|
|
216
|
-
tmpStmt.run(tmpBook.Title, tmpBook.Author);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// The entire array is inserted atomically
|
|
221
|
-
tmpBulkInsert([
|
|
222
|
-
{ Title: 'Foundation', Author: 'Isaac Asimov' },
|
|
223
|
-
{ Title: 'Snow Crash', Author: 'Neal Stephenson' }
|
|
224
|
-
]);
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
If any statement inside the transaction throws, the entire transaction is rolled back automatically.
|
|
228
|
-
|
|
229
|
-
### Close
|
|
230
|
-
|
|
231
|
-
```javascript
|
|
232
|
-
tmpDB.close();
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## Logging
|
|
238
|
-
|
|
239
|
-
The provider logs connection events through the Fable logging system:
|
|
240
|
-
|
|
241
|
-
| Event | Level | Message |
|
|
242
|
-
|-------|-------|---------|
|
|
243
|
-
| Connecting | `info` | `Meadow-Connection-SQLite connecting to file [path].` |
|
|
244
|
-
| Connected | `info` | `Meadow-Connection-SQLite successfully connected to SQLite file [path].` |
|
|
245
|
-
| Already connected | `error` | `...is already connected - skipping the second connect call.` |
|
|
246
|
-
| Missing path | `error` | `...database file path is invalid; SQLiteFilePath must be in either...` |
|
|
247
|
-
| Connection error | `error` | `...error connecting to SQLite file [path]: [error]` |
|
|
248
|
-
| No callback | `error` | `...connect() called without a callback...` |
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## Known Limitations
|
|
253
|
-
|
|
254
|
-
- **No async queries** — better-sqlite3 is synchronous by design. For CPU-bound workloads, consider running queries in a worker thread.
|
|
3
|
+
The full API reference has moved to the [API Reference](api/reference.md) page.
|
|
4
|
+
|
|
5
|
+
## Individual Function Documentation
|
|
6
|
+
|
|
7
|
+
- [connectAsync](api/connectAsync.md) -- Open the database and enable WAL (recommended)
|
|
8
|
+
- [connect](api/connect.md) -- Synchronous convenience wrapper
|
|
9
|
+
- [db](api/db.md) -- Access the better-sqlite3 Database instance
|
|
10
|
+
- [SQLite](api/SQLite.md) -- Access the better-sqlite3 module constructor
|
|
11
|
+
- [preparedStatement](api/preparedStatement.md) -- Database handle getter with connection guard
|
|
12
|
+
- [generateCreateTableStatement](api/generateCreateTableStatement.md) -- Generate CREATE TABLE DDL
|
|
13
|
+
- [createTable](api/createTable.md) -- Generate and execute CREATE TABLE
|
|
14
|
+
- [createTables](api/createTables.md) -- Create multiple tables from a schema
|
|
15
|
+
- [generateDropTableStatement](api/generateDropTableStatement.md) -- Generate DROP TABLE DDL
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Architecture & Design
|
|
2
|
+
|
|
3
|
+
Meadow Connection SQLite connects Fable applications to SQLite databases through the service provider pattern. This page illustrates the system architecture, connection lifecycle, query model, and how the provider fits into the Meadow ecosystem.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## System Architecture
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TB
|
|
11
|
+
App["Fable Application"]
|
|
12
|
+
Settings["fable.settings.SQLite"]
|
|
13
|
+
Provider["MeadowConnectionSQLite<br/>(Fable Service Provider)"]
|
|
14
|
+
BetterSQLite["better-sqlite3<br/>(Native C++ Bindings)"]
|
|
15
|
+
File["SQLite Database File<br/>or :memory:"]
|
|
16
|
+
|
|
17
|
+
App -->|"reads config"| Settings
|
|
18
|
+
App -->|"connectAsync()"| Provider
|
|
19
|
+
Provider -->|"new Database(path)"| BetterSQLite
|
|
20
|
+
BetterSQLite -->|"reads/writes"| File
|
|
21
|
+
Provider -->|".db getter"| BetterSQLite
|
|
22
|
+
Settings -->|"SQLiteFilePath"| Provider
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Connection Lifecycle
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
sequenceDiagram
|
|
31
|
+
participant App as Application
|
|
32
|
+
participant SM as ServiceManager
|
|
33
|
+
participant Provider as MeadowConnectionSQLite
|
|
34
|
+
participant BS3 as better-sqlite3
|
|
35
|
+
participant File as Database File
|
|
36
|
+
|
|
37
|
+
App->>SM: addServiceType('MeadowSQLiteProvider', lib)
|
|
38
|
+
App->>SM: instantiateServiceProvider('MeadowSQLiteProvider')
|
|
39
|
+
SM->>Provider: new MeadowConnectionSQLite(fable, manifest, hash)
|
|
40
|
+
Note over Provider: connected = false
|
|
41
|
+
|
|
42
|
+
App->>Provider: connectAsync(callback)
|
|
43
|
+
|
|
44
|
+
alt SQLiteFilePath missing
|
|
45
|
+
Provider-->>App: callback(error)
|
|
46
|
+
else Already connected
|
|
47
|
+
Provider-->>App: callback(null, existingDB)
|
|
48
|
+
else Normal connect
|
|
49
|
+
Provider->>BS3: new Database(filePath, options)
|
|
50
|
+
BS3->>File: Open or create file
|
|
51
|
+
File-->>BS3: File handle
|
|
52
|
+
BS3-->>Provider: Database instance
|
|
53
|
+
Provider->>BS3: pragma('journal_mode = WAL')
|
|
54
|
+
Note over Provider: connected = true
|
|
55
|
+
Provider-->>App: callback(null, database)
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Synchronous Query Model
|
|
62
|
+
|
|
63
|
+
Unlike MySQL and MSSQL providers which use asynchronous APIs, the SQLite provider uses better-sqlite3's synchronous API. Every query blocks the event loop for the duration of the operation, but better-sqlite3's native C++ bindings make individual operations extremely fast.
|
|
64
|
+
|
|
65
|
+
```mermaid
|
|
66
|
+
flowchart LR
|
|
67
|
+
subgraph "better-sqlite3 Synchronous API"
|
|
68
|
+
EXEC["db.exec(sql)<br/>DDL / multi-statement"]
|
|
69
|
+
PREP["db.prepare(sql)"]
|
|
70
|
+
RUN["stmt.run(params)<br/>→ { changes, lastInsertRowid }"]
|
|
71
|
+
GET["stmt.get(params)<br/>→ object | undefined"]
|
|
72
|
+
ALL["stmt.all(params)<br/>→ array of objects"]
|
|
73
|
+
TXN["db.transaction(fn)<br/>→ atomic wrapper"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
PREP --> RUN
|
|
77
|
+
PREP --> GET
|
|
78
|
+
PREP --> ALL
|
|
79
|
+
|
|
80
|
+
style EXEC fill:#e8f4e8
|
|
81
|
+
style RUN fill:#e8f0f8
|
|
82
|
+
style GET fill:#e8f0f8
|
|
83
|
+
style ALL fill:#e8f0f8
|
|
84
|
+
style TXN fill:#f8f0e8
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Query Method Selection
|
|
88
|
+
|
|
89
|
+
| Goal | Method | Returns |
|
|
90
|
+
|------|--------|---------|
|
|
91
|
+
| Create tables, run DDL | `db.exec(sql)` | nothing |
|
|
92
|
+
| INSERT / UPDATE / DELETE | `db.prepare(sql).run(...)` | `{ changes, lastInsertRowid }` |
|
|
93
|
+
| SELECT one row | `db.prepare(sql).get(...)` | object or `undefined` |
|
|
94
|
+
| SELECT multiple rows | `db.prepare(sql).all(...)` | array of objects |
|
|
95
|
+
| Atomic batch operations | `db.transaction(fn)(args)` | return value of `fn` |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Connection Settings Flow
|
|
100
|
+
|
|
101
|
+
```mermaid
|
|
102
|
+
flowchart TD
|
|
103
|
+
FS["fable.settings.SQLite.SQLiteFilePath"]
|
|
104
|
+
CO["Constructor Options.SQLiteFilePath"]
|
|
105
|
+
Provider["MeadowConnectionSQLite"]
|
|
106
|
+
Decision{{"Which source?"}}
|
|
107
|
+
Use["Use resolved SQLiteFilePath"]
|
|
108
|
+
|
|
109
|
+
FS --> Decision
|
|
110
|
+
CO --> Decision
|
|
111
|
+
Decision -->|"Options override Settings"| Use
|
|
112
|
+
Use --> Provider
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Settings priority:
|
|
116
|
+
|
|
117
|
+
1. **Constructor options** — passed as the second argument to `instantiateServiceProvider()`
|
|
118
|
+
2. **Fable settings** — `fable.settings.SQLite.SQLiteFilePath`
|
|
119
|
+
|
|
120
|
+
Constructor options take priority, allowing multiple provider instances with different database files.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## DDL Generation Flow
|
|
125
|
+
|
|
126
|
+
```mermaid
|
|
127
|
+
flowchart LR
|
|
128
|
+
Schema["Meadow Table Schema<br/>{ TableName, Columns[] }"]
|
|
129
|
+
Gen["generateCreateTableStatement()"]
|
|
130
|
+
DDL["CREATE TABLE IF NOT EXISTS..."]
|
|
131
|
+
Exec["db.exec(ddl)"]
|
|
132
|
+
Table["SQLite Table"]
|
|
133
|
+
|
|
134
|
+
Schema --> Gen
|
|
135
|
+
Gen --> DDL
|
|
136
|
+
DDL --> Exec
|
|
137
|
+
Exec --> Table
|
|
138
|
+
|
|
139
|
+
style Schema fill:#f0f0f0
|
|
140
|
+
style DDL fill:#e8f4e8
|
|
141
|
+
style Table fill:#e8f0f8
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Each Meadow column type maps to a SQLite storage class:
|
|
145
|
+
|
|
146
|
+
| Meadow DataType | SQLite Column Definition |
|
|
147
|
+
|-----------------|--------------------------|
|
|
148
|
+
| `ID` | `INTEGER PRIMARY KEY AUTOINCREMENT` |
|
|
149
|
+
| `GUID` | `TEXT DEFAULT '00000000-0000-0000-0000-000000000000'` |
|
|
150
|
+
| `ForeignKey` | `INTEGER NOT NULL DEFAULT 0` |
|
|
151
|
+
| `Numeric` | `INTEGER NOT NULL DEFAULT 0` |
|
|
152
|
+
| `Decimal` | `REAL` |
|
|
153
|
+
| `String` | `TEXT NOT NULL DEFAULT ''` |
|
|
154
|
+
| `Text` | `TEXT` |
|
|
155
|
+
| `DateTime` | `TEXT` |
|
|
156
|
+
| `Boolean` | `INTEGER NOT NULL DEFAULT 0` |
|
|
157
|
+
|
|
158
|
+
See [Schema & Table Creation](schema.md) for full details.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Connection Safety
|
|
163
|
+
|
|
164
|
+
```mermaid
|
|
165
|
+
flowchart TD
|
|
166
|
+
Start["connectAsync() called"]
|
|
167
|
+
CheckPath{{"SQLiteFilePath<br/>configured?"}}
|
|
168
|
+
CheckConn{{"Already<br/>connected?"}}
|
|
169
|
+
Connect["Open database file"]
|
|
170
|
+
WAL["Enable WAL journaling"]
|
|
171
|
+
Done["callback(null, db)"]
|
|
172
|
+
ErrPath["callback(error)"]
|
|
173
|
+
ErrConn["Log error, callback(null, existingDB)"]
|
|
174
|
+
|
|
175
|
+
Start --> CheckPath
|
|
176
|
+
CheckPath -->|No| ErrPath
|
|
177
|
+
CheckPath -->|Yes| CheckConn
|
|
178
|
+
CheckConn -->|Yes| ErrConn
|
|
179
|
+
CheckConn -->|No| Connect
|
|
180
|
+
Connect --> WAL
|
|
181
|
+
WAL --> Done
|
|
182
|
+
|
|
183
|
+
style ErrPath fill:#f8e0e0
|
|
184
|
+
style ErrConn fill:#f8f0e0
|
|
185
|
+
style Done fill:#e0f8e0
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The provider guards against:
|
|
189
|
+
|
|
190
|
+
- **Missing file path** -- Returns an error immediately if `SQLiteFilePath` is not configured
|
|
191
|
+
- **Double connect** -- Logs an error and returns the existing database if already connected
|
|
192
|
+
- **File creation** -- The database file is created automatically by better-sqlite3 if it does not exist
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Meadow Ecosystem Integration
|
|
197
|
+
|
|
198
|
+
```mermaid
|
|
199
|
+
sequenceDiagram
|
|
200
|
+
participant App as Application
|
|
201
|
+
participant Meadow as Meadow ORM
|
|
202
|
+
participant FH as FoxHound
|
|
203
|
+
participant Provider as SQLite Provider
|
|
204
|
+
participant BS3 as better-sqlite3
|
|
205
|
+
participant File as .db File
|
|
206
|
+
|
|
207
|
+
App->>Provider: connectAsync()
|
|
208
|
+
Provider->>BS3: new Database(path)
|
|
209
|
+
BS3->>File: Open / create
|
|
210
|
+
Provider-->>App: Connected
|
|
211
|
+
|
|
212
|
+
App->>Meadow: new Meadow(fable, 'Entity')
|
|
213
|
+
App->>Meadow: setProvider('ALASQL')
|
|
214
|
+
App->>Meadow: setSchema(columns)
|
|
215
|
+
|
|
216
|
+
Note over App,Provider: For schema DDL
|
|
217
|
+
App->>Provider: createTable(schema)
|
|
218
|
+
Provider->>Provider: generateCreateTableStatement()
|
|
219
|
+
Provider->>BS3: db.exec(ddl)
|
|
220
|
+
|
|
221
|
+
Note over App,Provider: For direct queries
|
|
222
|
+
App->>Provider: .db getter
|
|
223
|
+
Provider-->>App: Database instance
|
|
224
|
+
App->>BS3: prepare(sql).all()
|
|
225
|
+
BS3-->>App: Results
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The SQLite connection provider serves two roles in the Meadow ecosystem:
|
|
229
|
+
|
|
230
|
+
1. **Schema DDL** -- Generates and executes `CREATE TABLE` statements from Meadow table schemas
|
|
231
|
+
2. **Direct query access** -- Exposes the `better-sqlite3` database for synchronous query execution
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Connector Comparison
|
|
236
|
+
|
|
237
|
+
| Feature | SQLite | MySQL | MSSQL | RocksDB |
|
|
238
|
+
|---------|--------|-------|-------|---------|
|
|
239
|
+
| **Server Required** | No | Yes | Yes | No |
|
|
240
|
+
| **Query API** | Synchronous | Async (callback) | Async (Promise) | Async (callback) |
|
|
241
|
+
| **File-based** | Yes | No | No | Yes |
|
|
242
|
+
| **In-memory mode** | `:memory:` | No | No | No |
|
|
243
|
+
| **WAL journaling** | Auto-enabled | N/A | N/A | Built-in |
|
|
244
|
+
| **Native driver** | better-sqlite3 | mysql2 | mssql (Tedious) | @nxtedition/rocksdb |
|
|
245
|
+
| **Connection pooling** | No (single file) | Yes | Yes | No (single handle) |
|
|
246
|
+
| **DDL generation** | Yes | Yes | Yes | No |
|
|
247
|
+
| **Prepared statements** | `db.prepare()` | Via pool | `ps.prepare()` | N/A |
|
|
248
|
+
| **Transactions** | `db.transaction(fn)` | Via pool | Via pool | Batch writes |
|
|
249
|
+
| **SQL support** | Full SQLite SQL | Full MySQL SQL | Full T-SQL | Key-value only |
|
|
250
|
+
| **Best for** | Local, embedded, test | Production servers | Enterprise | High-throughput KV |
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Get a SQLite database connected and running in five steps.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Step 1 — Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install meadow-connection-sqlite fable
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`better-sqlite3` compiles a native addon at install time. A C/C++ toolchain must be available on the host (GCC, Clang, or MSVC).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Step 2 — Configure and Connect
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
const libFable = require('fable');
|
|
21
|
+
const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
|
|
22
|
+
|
|
23
|
+
let _Fable = new libFable(
|
|
24
|
+
{
|
|
25
|
+
"Product": "BookstoreApp",
|
|
26
|
+
"ProductVersion": "1.0.0",
|
|
27
|
+
"SQLite":
|
|
28
|
+
{
|
|
29
|
+
"SQLiteFilePath": "./data/bookstore.db"
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
_Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
|
|
34
|
+
_Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
|
|
35
|
+
|
|
36
|
+
_Fable.MeadowSQLiteProvider.connectAsync(
|
|
37
|
+
(pError, pDatabase) =>
|
|
38
|
+
{
|
|
39
|
+
if (pError)
|
|
40
|
+
{
|
|
41
|
+
console.error('Connection failed:', pError);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log('Connected:', _Fable.MeadowSQLiteProvider.connected);
|
|
46
|
+
// => Connected: true
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The database file is created automatically if it does not exist. WAL journal mode is enabled on connection.
|
|
51
|
+
|
|
52
|
+
For in-memory databases (useful in tests), set `SQLiteFilePath` to `":memory:"`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Step 3 — Run Queries
|
|
57
|
+
|
|
58
|
+
All queries use the synchronous `better-sqlite3` API via the `db` getter:
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
let tmpDB = _Fable.MeadowSQLiteProvider.db;
|
|
62
|
+
|
|
63
|
+
// Create a table with raw SQL
|
|
64
|
+
tmpDB.exec(`
|
|
65
|
+
CREATE TABLE IF NOT EXISTS Book (
|
|
66
|
+
IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
+
Title TEXT DEFAULT '',
|
|
68
|
+
Author TEXT DEFAULT '',
|
|
69
|
+
Price REAL DEFAULT 0.0
|
|
70
|
+
)
|
|
71
|
+
`);
|
|
72
|
+
|
|
73
|
+
// Insert a row with a prepared statement
|
|
74
|
+
let tmpInsert = tmpDB.prepare(
|
|
75
|
+
'INSERT INTO Book (Title, Author, Price) VALUES (?, ?, ?)'
|
|
76
|
+
);
|
|
77
|
+
let tmpResult = tmpInsert.run('Dune', 'Frank Herbert', 14.99);
|
|
78
|
+
console.log('Inserted ID:', tmpResult.lastInsertRowid);
|
|
79
|
+
// => Inserted ID: 1
|
|
80
|
+
|
|
81
|
+
// Query all rows
|
|
82
|
+
let tmpBooks = tmpDB.prepare('SELECT * FROM Book').all();
|
|
83
|
+
console.log(tmpBooks);
|
|
84
|
+
// => [{ IDBook: 1, Title: 'Dune', Author: 'Frank Herbert', Price: 14.99 }]
|
|
85
|
+
|
|
86
|
+
// Query a single row
|
|
87
|
+
let tmpBook = tmpDB.prepare('SELECT * FROM Book WHERE IDBook = ?').get(1);
|
|
88
|
+
console.log(tmpBook.Title);
|
|
89
|
+
// => Dune
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Key Methods
|
|
93
|
+
|
|
94
|
+
| Method | Returns | Purpose |
|
|
95
|
+
|--------|---------|---------|
|
|
96
|
+
| `db.exec(sql)` | nothing | Run DDL or multi-statement SQL |
|
|
97
|
+
| `db.prepare(sql).run(...)` | `{ changes, lastInsertRowid }` | INSERT, UPDATE, DELETE |
|
|
98
|
+
| `db.prepare(sql).get(...)` | object or `undefined` | SELECT single row |
|
|
99
|
+
| `db.prepare(sql).all(...)` | array of objects | SELECT all matching rows |
|
|
100
|
+
| `db.transaction(fn)` | wrapped function | Atomic batch operations |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Step 4 — Create Tables from Schema
|
|
105
|
+
|
|
106
|
+
Instead of writing DDL by hand, pass a Meadow table schema to `createTable()`:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
let tmpBookSchema =
|
|
110
|
+
{
|
|
111
|
+
TableName: 'Book',
|
|
112
|
+
Columns:
|
|
113
|
+
[
|
|
114
|
+
{ Column: 'IDBook', DataType: 'ID' },
|
|
115
|
+
{ Column: 'GUIDBook', DataType: 'GUID' },
|
|
116
|
+
{ Column: 'Title', DataType: 'String', Size: '256' },
|
|
117
|
+
{ Column: 'Author', DataType: 'String', Size: '128' },
|
|
118
|
+
{ Column: 'YearPublished', DataType: 'Numeric' },
|
|
119
|
+
{ Column: 'Price', DataType: 'Decimal', Size: '10,2' },
|
|
120
|
+
{ Column: 'InPrint', DataType: 'Boolean' },
|
|
121
|
+
{ Column: 'CreateDate', DataType: 'DateTime' },
|
|
122
|
+
{ Column: 'UpdateDate', DataType: 'DateTime' }
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
_Fable.MeadowSQLiteProvider.createTable(tmpBookSchema,
|
|
127
|
+
(pError) =>
|
|
128
|
+
{
|
|
129
|
+
if (pError) { console.error('Create table failed:', pError); return; }
|
|
130
|
+
console.log('Table created!');
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
You can also generate the DDL string without executing it:
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
let tmpDDL = _Fable.MeadowSQLiteProvider.generateCreateTableStatement(tmpBookSchema);
|
|
138
|
+
console.log(tmpDDL);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Output:
|
|
142
|
+
|
|
143
|
+
```sql
|
|
144
|
+
CREATE TABLE IF NOT EXISTS Book (IDBook INTEGER PRIMARY KEY AUTOINCREMENT, GUIDBook TEXT DEFAULT '00000000-0000-0000-0000-000000000000', Title TEXT NOT NULL DEFAULT '', Author TEXT NOT NULL DEFAULT '', YearPublished INTEGER NOT NULL DEFAULT 0, Price REAL, InPrint INTEGER NOT NULL DEFAULT 0, CreateDate TEXT, UpdateDate TEXT);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Step 5 — Meadow ORM Integration
|
|
150
|
+
|
|
151
|
+
Wire the SQLite connection into Meadow for full ORM capabilities:
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
const libMeadow = require('meadow');
|
|
155
|
+
|
|
156
|
+
let tmpBookMeadow = libMeadow.new(_Fable, 'FableTest')
|
|
157
|
+
.setProvider('ALASQL')
|
|
158
|
+
.setDefaultIdentifier('IDBook')
|
|
159
|
+
.loadFromPackage(
|
|
160
|
+
{
|
|
161
|
+
scope: 'Book',
|
|
162
|
+
jsonschema:
|
|
163
|
+
{
|
|
164
|
+
title: 'Book',
|
|
165
|
+
properties:
|
|
166
|
+
{
|
|
167
|
+
IDBook: { type: 'integer' },
|
|
168
|
+
GUIDBook: { type: 'string' },
|
|
169
|
+
Title: { type: 'string' },
|
|
170
|
+
Author: { type: 'string' }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
> **Note:** Meadow uses FoxHound dialects for query generation. For SQLite-specific workloads that bypass Meadow's query layer, use the `db` getter directly. The ALASQL provider is commonly paired with SQLite for local Meadow operations.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Summary
|
|
181
|
+
|
|
182
|
+
| Step | What It Does |
|
|
183
|
+
|------|-------------|
|
|
184
|
+
| Install | `npm install meadow-connection-sqlite fable` |
|
|
185
|
+
| Configure | Set `SQLite.SQLiteFilePath` in Fable settings |
|
|
186
|
+
| Connect | `connectAsync()` opens the file and enables WAL |
|
|
187
|
+
| Query | `db.prepare().run/get/all()` for synchronous queries |
|
|
188
|
+
| Schema DDL | `createTable()` generates and executes SQLite DDL |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Next Steps
|
|
193
|
+
|
|
194
|
+
- [Architecture & Design](architecture.md) -- Connection lifecycle and data flow diagrams
|
|
195
|
+
- [Full Pipeline Example](examples-pipeline.md) -- End-to-end CRUD pipeline with transactions
|
|
196
|
+
- [API Reference](api/reference.md) -- Complete reference for every property and method
|
|
197
|
+
- [Schema & Table Creation](schema.md) -- Column type mapping and DDL generation
|