meadow-connection-sqlite 1.0.10 → 1.0.12

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,50 @@
1
+ # Contributing to Retold
2
+
3
+ We welcome contributions to Retold and its modules. This guide covers the expectations and process for contributing.
4
+
5
+ ## Code of Conduct
6
+
7
+ The Retold community values **empathy**, **equity**, **kindness**, and **thoughtfulness**. We expect all participants to treat each other with respect, assume good intent, and engage constructively. These values apply to all interactions: pull requests, issues, discussions, and code review.
8
+
9
+ ## How to Contribute
10
+
11
+ ### Pull Requests
12
+
13
+ Pull requests are the preferred method for contributing changes. To submit one:
14
+
15
+ 1. Fork the module repository you want to change
16
+ 2. Create a branch for your work
17
+ 3. Make your changes, following the code style of the module you are editing
18
+ 4. Ensure your changes have test coverage (see below)
19
+ 5. Open a pull request against the module's main branch
20
+
21
+ **Submitting a pull request does not guarantee it will be accepted.** Maintainers review contributions for fit, quality, and alignment with the project's direction. A PR may be declined, or you may be asked to revise it. This is normal and not a reflection on the quality of your effort.
22
+
23
+ ### Reporting Issues
24
+
25
+ If you find a bug or have a feature suggestion, open an issue on the relevant module's repository. Include enough detail to reproduce the problem or understand the proposal.
26
+
27
+ ## Test Coverage
28
+
29
+ Every commit must include test coverage for the changes it introduces. Retold modules use Mocha in TDD style. Before submitting:
30
+
31
+ - **Write tests** for any new functionality or bug fixes
32
+ - **Run the existing test suite** with `npm test` and confirm all tests pass
33
+ - **Check coverage** with `npm run coverage` if the module supports it
34
+
35
+ Pull requests that break existing tests or lack coverage for new code will not be merged.
36
+
37
+ ## Code Style
38
+
39
+ Follow the conventions of the module you are working in. The general Retold style is:
40
+
41
+ - **Tabs** for indentation, never spaces
42
+ - **Plain JavaScript** only (no TypeScript)
43
+ - **Allman-style braces** (opening brace on its own line)
44
+ - **Variable naming:** `pVariable` for parameters, `tmpVariable` for temporaries, `libSomething` for imports
45
+
46
+ When in doubt, match what the surrounding code does.
47
+
48
+ ## Repository Structure
49
+
50
+ Each module is its own git repository. The [retold](https://github.com/stevenvelozo/retold) repository tracks module organization but does not contain module source code. Direct your pull request to the specific module repository where your change belongs.
package/README.md CHANGED
@@ -131,15 +131,15 @@ Generate a `DROP TABLE IF EXISTS` SQL statement for the given table name.
131
131
 
132
132
  | Meadow Type | SQLite Column |
133
133
  |-------------|---------------|
134
- | `ID` | `INT NOT NULL IDENTITY PRIMARY KEY` |
135
- | `GUID` | `VARCHAR(254)` with default GUID |
136
- | `ForeignKey` | `INT UNSIGNED NOT NULL DEFAULT 0` |
137
- | `Numeric` | `INT NOT NULL DEFAULT 0` |
138
- | `Decimal` | `DECIMAL(size)` |
139
- | `String` | `VARCHAR(size) DEFAULT ''` |
134
+ | `ID` | `INTEGER PRIMARY KEY AUTOINCREMENT` |
135
+ | `GUID` | `TEXT DEFAULT '0000...'` |
136
+ | `ForeignKey` | `INTEGER NOT NULL DEFAULT 0` |
137
+ | `Numeric` | `INTEGER NOT NULL DEFAULT 0` |
138
+ | `Decimal` | `REAL` |
139
+ | `String` | `TEXT NOT NULL DEFAULT ''` |
140
140
  | `Text` | `TEXT` |
141
- | `DateTime` | `DATETIME` |
142
- | `Boolean` | `TINYINT DEFAULT 0` |
141
+ | `DateTime` | `TEXT` |
142
+ | `Boolean` | `INTEGER NOT NULL DEFAULT 0` |
143
143
 
144
144
  ## Part of the Retold Framework
145
145
 
@@ -165,10 +165,15 @@ Run with coverage:
165
165
  npm run coverage
166
166
  ```
167
167
 
168
+ ## Related Packages
169
+
170
+ - [meadow](https://github.com/stevenvelozo/meadow) - Data access and ORM
171
+ - [fable](https://github.com/stevenvelozo/fable) - Application services framework
172
+
168
173
  ## License
169
174
 
170
- MIT - See [LICENSE](LICENSE) for details.
175
+ MIT
171
176
 
172
- ## Author
177
+ ## Contributing
173
178
 
174
- Steven Velozo - [steven@velozo.com](mailto:steven@velozo.com)
179
+ Pull requests are welcome. For details on our code of conduct, contribution process, and testing requirements, see the [Retold Contributing Guide](https://github.com/stevenvelozo/retold/blob/main/docs/contributing.md).
package/docs/api.md CHANGED
@@ -251,6 +251,4 @@ The provider logs connection events through the Fable logging system:
251
251
 
252
252
  ## Known Limitations
253
253
 
254
- - **Table generation** — The `generateCreateTableStatement()` and `createTable()` methods produce MSSQL-syntax SQL that is not compatible with SQLite. Use `db.exec()` with SQLite-native `CREATE TABLE` statements instead.
255
- - **Prepared statement getter** — The `preparedStatement` property references an uninitialized connection pool. Use `db.prepare()` directly for prepared statements.
256
254
  - **No async queries** — better-sqlite3 is synchronous by design. For CPU-bound workloads, consider running queries in a worker thread.
@@ -54,7 +54,39 @@ The database file is created automatically if it does not exist. WAL journal mod
54
54
 
55
55
  ## Step 2: Create a Table
56
56
 
57
- Use SQLite-native `CREATE TABLE` syntax through `db.exec()`:
57
+ ### Using createTable()
58
+
59
+ Pass a Meadow table schema object to `createTable()` and it generates the correct SQLite DDL automatically:
60
+
61
+ ```javascript
62
+ let tmpBookSchema =
63
+ {
64
+ TableName: 'Book',
65
+ Columns:
66
+ [
67
+ { Column: 'IDBook', DataType: 'ID' },
68
+ { Column: 'GUIDBook', DataType: 'GUID' },
69
+ { Column: 'Title', DataType: 'String', Size: '256' },
70
+ { Column: 'Author', DataType: 'String', Size: '128' },
71
+ { Column: 'YearPublished', DataType: 'Numeric' },
72
+ { Column: 'Price', DataType: 'Decimal', Size: '10,2' },
73
+ { Column: 'InPrint', DataType: 'Boolean' },
74
+ { Column: 'CreateDate', DataType: 'DateTime' },
75
+ { Column: 'UpdateDate', DataType: 'DateTime' }
76
+ ]
77
+ };
78
+
79
+ _Fable.MeadowSQLiteProvider.createTable(tmpBookSchema,
80
+ (pError) =>
81
+ {
82
+ if (pError) { _Fable.log.error(`Create table failed: ${pError}`); return; }
83
+ // Table is ready for queries
84
+ });
85
+ ```
86
+
87
+ ### Using raw db.exec()
88
+
89
+ For tables with custom constraints, expressions, or indexes, use `db.exec()` directly:
58
90
 
59
91
  ```javascript
60
92
  tmpDB.exec(`
@@ -1,5 +1,5 @@
1
1
  {
2
- "Generated": "2026-02-15T22:10:52.774Z",
2
+ "Generated": "2026-02-18T03:34:33.223Z",
3
3
  "GitHubOrg": "stevenvelozo",
4
4
  "DefaultBranch": "master",
5
5
  "Groups": [
@@ -19,6 +19,25 @@
19
19
  "DocFiles": []
20
20
  }
21
21
  ]
22
+ },
23
+ {
24
+ "Name": "Docs",
25
+ "Key": "docs",
26
+ "Description": "",
27
+ "Modules": [
28
+ {
29
+ "Name": "css",
30
+ "Repo": "css",
31
+ "Group": "docs",
32
+ "Branch": "master",
33
+ "HasDocs": true,
34
+ "HasCover": false,
35
+ "Sidebar": [],
36
+ "DocFiles": [
37
+ "css/docuserve.css"
38
+ ]
39
+ }
40
+ ]
22
41
  }
23
42
  ]
24
43
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "Generated": "2026-02-15T22:10:52.851Z",
2
+ "Generated": "2026-02-18T03:34:33.304Z",
3
3
  "DocumentCount": 0,
4
4
  "LunrIndex": {
5
5
  "version": "2.3.9",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow-connection-sqlite",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Meadow SQLite Plugin",
5
5
  "main": "source/Meadow-Connection-SQLite.js",
6
6
  "scripts": {
@@ -42,11 +42,11 @@
42
42
  },
43
43
  "homepage": "https://github.com/stevenvelozo/meadow-connection-sqlite",
44
44
  "devDependencies": {
45
- "quackage": "^1.0.48",
46
- "retold-harness": "^1.0.6"
45
+ "quackage": "^1.0.51",
46
+ "retold-harness": "^1.1.0"
47
47
  },
48
48
  "dependencies": {
49
- "better-sqlite3": "^12.3.0",
50
- "fable-serviceproviderbase": "^3.0.16"
49
+ "better-sqlite3": "^12.6.2",
50
+ "fable-serviceproviderbase": "^3.0.18"
51
51
  }
52
52
  }
@@ -36,19 +36,16 @@ class MeadowConnectionSQLite extends libFableServiceProviderBase
36
36
 
37
37
  generateDropTableStatement(pTableName)
38
38
  {
39
- let tmpDropTableStatement = `IF OBJECT_ID('dbo.[${pTableName}]', 'U') IS NOT NULL\n`;
40
- tmpDropTableStatement += ` DROP TABLE dbo.[${pTableName}];\n`;
41
- tmpDropTableStatement += `GO`;
42
- return tmpDropTableStatement;
39
+ return `DROP TABLE IF EXISTS ${pTableName};`;
43
40
  }
44
41
 
45
42
  generateCreateTableStatement(pMeadowTableSchema)
46
43
  {
47
- this.log.info(`--> Building the table create string for ${pMeadowTableSchema} ...`);
44
+ this.log.info(`--> Building the table create string for ${pMeadowTableSchema.TableName} ...`);
48
45
 
49
46
  let tmpPrimaryKey = false;
50
47
  let tmpCreateTableStatement = `-- [ ${pMeadowTableSchema.TableName} ]`;
51
- tmpCreateTableStatement += `\nCREATE TABLE [dbo].[${pMeadowTableSchema.TableName}]\n (`;
48
+ tmpCreateTableStatement += `\nCREATE TABLE IF NOT EXISTS ${pMeadowTableSchema.TableName}\n (`;
52
49
  for (let j = 0; j < pMeadowTableSchema.Columns.length; j++)
53
50
  {
54
51
  let tmpColumn = pMeadowTableSchema.Columns[j];
@@ -60,54 +57,40 @@ class MeadowConnectionSQLite extends libFableServiceProviderBase
60
57
  }
61
58
 
62
59
  tmpCreateTableStatement += `\n`;
63
- // Dump out each column......
64
60
  switch (tmpColumn.DataType)
65
61
  {
66
62
  case 'ID':
67
- // if (this.options.AllowIdentityInsert)
68
- // {
69
- // tmpCreateTableStatement += ` [${tmpColumn.Column}] INT NOT NULL PRIMARY KEY`;
70
- // }
71
- // else
72
- // {
73
- // There is debate on whether IDENTITY(1,1) is better or not.
74
- tmpCreateTableStatement += ` [${tmpColumn.Column}] INT NOT NULL IDENTITY PRIMARY KEY`;
75
- //}
63
+ tmpCreateTableStatement += ` ${tmpColumn.Column} INTEGER PRIMARY KEY AUTOINCREMENT`;
76
64
  tmpPrimaryKey = tmpColumn.Column;
77
65
  break;
78
66
  case 'GUID':
79
- tmpCreateTableStatement += ` [${tmpColumn.Column}] VARCHAR(254) DEFAULT '00000000-0000-0000-0000-000000000000'`;
67
+ tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT DEFAULT '00000000-0000-0000-0000-000000000000'`;
80
68
  break;
81
69
  case 'ForeignKey':
82
- tmpCreateTableStatement += ` [${tmpColumn.Column}] INT UNSIGNED NOT NULL DEFAULT 0`;
83
- tmpPrimaryKey = tmpColumn.Column;
70
+ tmpCreateTableStatement += ` ${tmpColumn.Column} INTEGER NOT NULL DEFAULT 0`;
84
71
  break;
85
72
  case 'Numeric':
86
- tmpCreateTableStatement += ` [${tmpColumn.Column}] INT NOT NULL DEFAULT 0`;
73
+ tmpCreateTableStatement += ` ${tmpColumn.Column} INTEGER NOT NULL DEFAULT 0`;
87
74
  break;
88
75
  case 'Decimal':
89
- tmpCreateTableStatement += ` [${tmpColumn.Column}] DECIMAL(${tmpColumn.Size})`;
76
+ tmpCreateTableStatement += ` ${tmpColumn.Column} REAL`;
90
77
  break;
91
78
  case 'String':
92
- tmpCreateTableStatement += ` [${tmpColumn.Column}] VARCHAR(${tmpColumn.Size}) DEFAULT ''`;
79
+ tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT NOT NULL DEFAULT ''`;
93
80
  break;
94
81
  case 'Text':
95
- tmpCreateTableStatement += ` [${tmpColumn.Column}] TEXT`;
82
+ tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT`;
96
83
  break;
97
84
  case 'DateTime':
98
- tmpCreateTableStatement += ` [${tmpColumn.Column}] DATETIME`;
85
+ tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT`;
99
86
  break;
100
87
  case 'Boolean':
101
- tmpCreateTableStatement += ` [${tmpColumn.Column}] TINYINT DEFAULT 0`;
88
+ tmpCreateTableStatement += ` ${tmpColumn.Column} INTEGER NOT NULL DEFAULT 0`;
102
89
  break;
103
90
  default:
104
91
  break;
105
92
  }
106
93
  }
107
- if (tmpPrimaryKey)
108
- {
109
- // tmpCreateTableStatement += `,\n\n PRIMARY KEY (${tmpPrimaryKey$})`;
110
- }
111
94
  tmpCreateTableStatement += `\n );`;
112
95
 
113
96
  this.log.info(`Generated Create Table Statement: ${tmpCreateTableStatement}`);
@@ -136,34 +119,17 @@ class MeadowConnectionSQLite extends libFableServiceProviderBase
136
119
  createTable(pMeadowTableSchema, fCallback)
137
120
  {
138
121
  let tmpCreateTableStatement = this.generateCreateTableStatement(pMeadowTableSchema);
139
- this._ConnectionPool.query(tmpCreateTableStatement)
140
- .then((pResult) =>
141
- {
142
- this.log.info(`Meadow-SQLite CREATE TABLE ${pMeadowTableSchema.TableName} Success`);
143
- this.log.warn(`Meadow-SQLite Create Table Statement: ${tmpCreateTableStatement}`)
144
- return fCallback();
145
- })
146
- .catch((pError) =>
147
- {
148
- if (pError.hasOwnProperty('originalError')
149
- // TODO: This check may be extraneous; not familiar enough with the sqlite node driver yet
150
- && (pError.originalError.hasOwnProperty('info'))
151
- // TODO: Validate that there isn't a better way to find this (pError.code isn't explicit enough)
152
- && (pError.originalError.info.message.indexOf("There is already an object named") == 0)
153
- && (pError.originalError.info.message.indexOf('in the database.') > 0))
154
- {
155
- // The table already existed; log a warning but keep on keeping on.
156
- //this.log.warn(`Meadow-SQLite CREATE TABLE ${pMeadowTableSchema.TableName} executed but table already existed.`);
157
- //this.log.warn(`Meadow-SQLite Create Table Statement: ${tmpCreateTableStatement}`)
158
- return fCallback();
159
- }
160
- else
161
- {
162
- this.log.error(`Meadow-SQLite CREATE TABLE ${pMeadowTableSchema.TableName} failed!`, pError);
163
- //this.log.warn(`Meadow-SQLite Create Table Statement: ${tmpCreateTableStatement}`)
164
- return fCallback(pError);
165
- }
166
- });
122
+ try
123
+ {
124
+ this._database.exec(tmpCreateTableStatement);
125
+ this.log.info(`Meadow-SQLite CREATE TABLE ${pMeadowTableSchema.TableName} Success`);
126
+ return fCallback();
127
+ }
128
+ catch (pError)
129
+ {
130
+ this.log.error(`Meadow-SQLite CREATE TABLE ${pMeadowTableSchema.TableName} failed!`, pError);
131
+ return fCallback(pError);
132
+ }
167
133
  }
168
134
 
169
135
  connect()
@@ -222,13 +188,15 @@ class MeadowConnectionSQLite extends libFableServiceProviderBase
222
188
 
223
189
  get preparedStatement()
224
190
  {
225
- if (this.connected && this._ConnectionPool)
191
+ if (this.connected && this._database)
226
192
  {
227
- return new libSQLite.PreparedStatement(this._ConnectionPool);
193
+ // In better-sqlite3, prepared statements are created via db.prepare(sql)
194
+ // This getter is maintained for API consistency with other providers.
195
+ return this._database;
228
196
  }
229
197
  else
230
198
  {
231
- throw new Error('The Meadow Microsoft SQL provider could not create a prepared statement; disconnected or no valid connection pool.');
199
+ throw new Error('The Meadow SQLite provider is not connected; cannot create a prepared statement.');
232
200
  }
233
201
  }
234
202
 
@@ -34,6 +34,40 @@ const _FableConfig = (
34
34
  }
35
35
  });
36
36
 
37
+ // A sample Stricture-compiled Meadow table schema
38
+ const _BookTableSchema =
39
+ {
40
+ TableName: 'Book',
41
+ Domain: 'Default',
42
+ Columns:
43
+ [
44
+ { Column: 'IDBook', DataType: 'ID' },
45
+ { Column: 'GUIDBook', DataType: 'GUID' },
46
+ { Column: 'Title', DataType: 'String', Size: '256' },
47
+ { Column: 'Description', DataType: 'Text' },
48
+ { Column: 'Price', DataType: 'Decimal', Size: '10,2' },
49
+ { Column: 'PageCount', DataType: 'Numeric' },
50
+ { Column: 'PublishDate', DataType: 'DateTime' },
51
+ { Column: 'InPrint', DataType: 'Boolean' },
52
+ { Column: 'IDAuthor', DataType: 'ForeignKey' }
53
+ ],
54
+ Description: 'A table of books'
55
+ };
56
+
57
+ const _AuthorTableSchema =
58
+ {
59
+ TableName: 'Author',
60
+ Domain: 'Default',
61
+ Columns:
62
+ [
63
+ { Column: 'IDAuthor', DataType: 'ID' },
64
+ { Column: 'GUIDAuthor', DataType: 'GUID' },
65
+ { Column: 'Name', DataType: 'String', Size: '128' },
66
+ { Column: 'Bio', DataType: 'Text' }
67
+ ],
68
+ Description: 'A table of authors'
69
+ };
70
+
37
71
  suite
38
72
  (
39
73
  'Connection',
@@ -46,10 +80,10 @@ suite
46
80
  {
47
81
  libFS.unlinkSync('./dist/FableTest.db');
48
82
  }
49
-
50
- let _Fable = new libFable(_FableConfig);
51
- _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
52
- _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
83
+ if (!libFS.existsSync('./dist'))
84
+ {
85
+ libFS.mkdirSync('./dist', { recursive: true });
86
+ }
53
87
  return fDone();
54
88
  });
55
89
 
@@ -72,18 +106,324 @@ suite
72
106
  _Fable.MeadowSQLiteProvider.connectAsync(
73
107
  (pError) =>
74
108
  {
75
- if (pError)
76
- {
77
- return fDone(pError);
78
- }
79
- let tmpRows = _Fable.MeadowSQLiteProvider.db.prepare(`SELECT * FROM FableTest`).get();
80
- Expect(tmpRows).to.be.an('array');
109
+ Expect(pError).to.not.exist;
110
+ Expect(_Fable.MeadowSQLiteProvider.connected).to.equal(true);
111
+ Expect(_Fable.MeadowSQLiteProvider.db).to.be.an('object');
81
112
  return fDone();
82
113
  }
83
114
  );
84
115
  }
85
116
  );
117
+
118
+ test
119
+ (
120
+ 'attempt to connect twice should not error',
121
+ (fDone) =>
122
+ {
123
+ let _Fable = new libFable(_FableConfig);
124
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
125
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
126
+
127
+ _Fable.MeadowSQLiteProvider.connectAsync(
128
+ (pError) =>
129
+ {
130
+ Expect(pError).to.not.exist;
131
+ // Connect again -- should log an error but callback with the existing database
132
+ _Fable.MeadowSQLiteProvider.connectAsync(
133
+ (pSecondError, pDatabase) =>
134
+ {
135
+ // Should not be a hard error
136
+ Expect(pSecondError).to.not.exist;
137
+ Expect(pDatabase).to.be.an('object');
138
+ return fDone();
139
+ });
140
+ });
141
+ }
142
+ );
143
+
144
+ test
145
+ (
146
+ 'connect without a callback should not throw',
147
+ (fDone) =>
148
+ {
149
+ let _Fable = new libFable(_FableConfig);
150
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
151
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
152
+
153
+ // Call connect() (sync wrapper) which calls connectAsync without a callback
154
+ _Fable.MeadowSQLiteProvider.connect();
155
+ // Give it a tick to complete
156
+ setTimeout(
157
+ () =>
158
+ {
159
+ Expect(_Fable.MeadowSQLiteProvider.connected).to.equal(true);
160
+ return fDone();
161
+ }, 100);
162
+ }
163
+ );
164
+
165
+ test
166
+ (
167
+ 'fail to connect with no SQLiteFilePath',
168
+ (fDone) =>
169
+ {
170
+ let _Fable = new libFable(
171
+ {
172
+ Product: 'MeadowSQLiteTestNoPath',
173
+ ProductVersion: '1.0.0'
174
+ });
175
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
176
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
177
+
178
+ _Fable.MeadowSQLiteProvider.connectAsync(
179
+ (pError) =>
180
+ {
181
+ Expect(pError).to.be.an.instanceof(Error);
182
+ return fDone();
183
+ });
184
+ }
185
+ );
186
+ }
187
+ );
188
+
189
+ suite
190
+ (
191
+ 'DDL Generation',
192
+ ()=>
193
+ {
194
+ test
195
+ (
196
+ 'generate a DROP TABLE statement',
197
+ (fDone) =>
198
+ {
199
+ let _Fable = new libFable(_FableConfig);
200
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
201
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
202
+
203
+ let tmpDropStatement = _Fable.MeadowSQLiteProvider.generateDropTableStatement('Book');
204
+ Expect(tmpDropStatement).to.equal('DROP TABLE IF EXISTS Book;');
205
+ return fDone();
206
+ }
207
+ );
208
+
209
+ test
210
+ (
211
+ 'generate a CREATE TABLE statement from a Meadow table schema',
212
+ (fDone) =>
213
+ {
214
+ let _Fable = new libFable(_FableConfig);
215
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
216
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
217
+
218
+ _Fable.MeadowSQLiteProvider.connectAsync(
219
+ (pError) =>
220
+ {
221
+ Expect(pError).to.not.exist;
222
+ let tmpCreateStatement = _Fable.MeadowSQLiteProvider.generateCreateTableStatement(_BookTableSchema);
223
+
224
+ // Should use SQLite syntax, not MSSQL
225
+ Expect(tmpCreateStatement).to.contain('CREATE TABLE IF NOT EXISTS Book');
226
+ Expect(tmpCreateStatement).to.contain('IDBook INTEGER PRIMARY KEY AUTOINCREMENT');
227
+ Expect(tmpCreateStatement).to.contain('GUIDBook TEXT DEFAULT');
228
+ Expect(tmpCreateStatement).to.contain('Title TEXT NOT NULL DEFAULT');
229
+ Expect(tmpCreateStatement).to.contain('Description TEXT');
230
+ Expect(tmpCreateStatement).to.contain('Price REAL');
231
+ Expect(tmpCreateStatement).to.contain('PageCount INTEGER NOT NULL DEFAULT 0');
232
+ Expect(tmpCreateStatement).to.contain('PublishDate TEXT');
233
+ Expect(tmpCreateStatement).to.contain('InPrint INTEGER NOT NULL DEFAULT 0');
234
+ Expect(tmpCreateStatement).to.contain('IDAuthor INTEGER NOT NULL DEFAULT 0');
235
+
236
+ // Should NOT contain any MSSQL syntax
237
+ Expect(tmpCreateStatement).to.not.contain('[dbo]');
238
+ Expect(tmpCreateStatement).to.not.contain('IDENTITY');
239
+ Expect(tmpCreateStatement).to.not.contain('UNSIGNED');
240
+ Expect(tmpCreateStatement).to.not.contain('TINYINT');
241
+ Expect(tmpCreateStatement).to.not.contain('VARCHAR');
242
+
243
+ return fDone();
244
+ });
245
+ }
246
+ );
247
+ }
248
+ );
249
+
250
+ suite
251
+ (
252
+ 'Create Tables',
253
+ ()=>
254
+ {
255
+ test
256
+ (
257
+ 'create a single table',
258
+ (fDone) =>
259
+ {
260
+ let _Fable = new libFable(_FableConfig);
261
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
262
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
263
+
264
+ _Fable.MeadowSQLiteProvider.connectAsync(
265
+ (pError) =>
266
+ {
267
+ Expect(pError).to.not.exist;
268
+
269
+ _Fable.MeadowSQLiteProvider.createTable(_BookTableSchema,
270
+ (pCreateError) =>
271
+ {
272
+ Expect(pCreateError).to.not.exist;
273
+
274
+ // Verify the table was actually created by inserting and reading back
275
+ let tmpDB = _Fable.MeadowSQLiteProvider.db;
276
+ tmpDB.prepare('INSERT INTO Book (Title, Price, PageCount, InPrint) VALUES (?, ?, ?, ?)').run('Test Book', 19.99, 200, 1);
277
+
278
+ let tmpRow = tmpDB.prepare('SELECT * FROM Book WHERE IDBook = 1').get();
279
+ Expect(tmpRow).to.be.an('object');
280
+ Expect(tmpRow.Title).to.equal('Test Book');
281
+ Expect(tmpRow.Price).to.equal(19.99);
282
+ Expect(tmpRow.PageCount).to.equal(200);
283
+ Expect(tmpRow.InPrint).to.equal(1);
284
+ Expect(tmpRow.IDAuthor).to.equal(0);
285
+ Expect(tmpRow.GUIDBook).to.equal('00000000-0000-0000-0000-000000000000');
286
+ return fDone();
287
+ });
288
+ });
289
+ }
290
+ );
291
+
292
+ test
293
+ (
294
+ 'create a table that already exists (idempotent)',
295
+ (fDone) =>
296
+ {
297
+ let _Fable = new libFable(_FableConfig);
298
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
299
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
300
+
301
+ _Fable.MeadowSQLiteProvider.connectAsync(
302
+ (pError) =>
303
+ {
304
+ Expect(pError).to.not.exist;
305
+
306
+ // Create the table
307
+ _Fable.MeadowSQLiteProvider.createTable(_BookTableSchema,
308
+ (pCreateError) =>
309
+ {
310
+ Expect(pCreateError).to.not.exist;
311
+
312
+ // Create it again -- should not error because of IF NOT EXISTS
313
+ _Fable.MeadowSQLiteProvider.createTable(_BookTableSchema,
314
+ (pSecondCreateError) =>
315
+ {
316
+ Expect(pSecondCreateError).to.not.exist;
317
+ return fDone();
318
+ });
319
+ });
320
+ });
321
+ }
322
+ );
323
+
324
+ test
325
+ (
326
+ 'create multiple tables from a schema',
327
+ (fDone) =>
328
+ {
329
+ let _Fable = new libFable(_FableConfig);
330
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
331
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
332
+
333
+ _Fable.MeadowSQLiteProvider.connectAsync(
334
+ (pError) =>
335
+ {
336
+ Expect(pError).to.not.exist;
337
+
338
+ let tmpSchema =
339
+ {
340
+ Tables:
341
+ [
342
+ _BookTableSchema,
343
+ _AuthorTableSchema
344
+ ]
345
+ };
346
+
347
+ _Fable.MeadowSQLiteProvider.createTables(tmpSchema,
348
+ (pCreateError) =>
349
+ {
350
+ Expect(pCreateError).to.not.exist;
351
+
352
+ // Verify both tables exist
353
+ let tmpDB = _Fable.MeadowSQLiteProvider.db;
354
+
355
+ tmpDB.prepare('INSERT INTO Author (Name) VALUES (?)').run('Test Author');
356
+ let tmpAuthor = tmpDB.prepare('SELECT * FROM Author WHERE IDAuthor = 1').get();
357
+ Expect(tmpAuthor).to.be.an('object');
358
+ Expect(tmpAuthor.Name).to.equal('Test Author');
359
+
360
+ tmpDB.prepare('INSERT INTO Book (Title, IDAuthor) VALUES (?, ?)').run('Author Book', 1);
361
+ let tmpBook = tmpDB.prepare('SELECT * FROM Book WHERE IDBook = 1').get();
362
+ Expect(tmpBook).to.be.an('object');
363
+ Expect(tmpBook.Title).to.equal('Author Book');
364
+ Expect(tmpBook.IDAuthor).to.equal(1);
365
+
366
+ return fDone();
367
+ });
368
+ });
369
+ }
370
+ );
371
+ }
372
+ );
373
+
374
+ suite
375
+ (
376
+ 'Accessors',
377
+ ()=>
378
+ {
379
+ test
380
+ (
381
+ 'access the SQLite library via the SQLite getter',
382
+ (fDone) =>
383
+ {
384
+ let _Fable = new libFable(_FableConfig);
385
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
386
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
387
+
388
+ Expect(_Fable.MeadowSQLiteProvider.SQLite).to.be.a('function');
389
+ return fDone();
390
+ }
391
+ );
392
+
393
+ test
394
+ (
395
+ 'access the preparedStatement getter when connected',
396
+ (fDone) =>
397
+ {
398
+ let _Fable = new libFable(_FableConfig);
399
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
400
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
401
+
402
+ _Fable.MeadowSQLiteProvider.connectAsync(
403
+ (pError) =>
404
+ {
405
+ Expect(pError).to.not.exist;
406
+ let tmpPrepared = _Fable.MeadowSQLiteProvider.preparedStatement;
407
+ Expect(tmpPrepared).to.be.an('object');
408
+ return fDone();
409
+ });
410
+ }
411
+ );
412
+
413
+ test
414
+ (
415
+ 'preparedStatement getter throws when disconnected',
416
+ (fDone) =>
417
+ {
418
+ let _Fable = new libFable(_FableConfig);
419
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
420
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
421
+
422
+ Expect(() => _Fable.MeadowSQLiteProvider.preparedStatement).to.throw('The Meadow SQLite provider is not connected');
423
+ return fDone();
424
+ }
425
+ );
86
426
  }
87
427
  );
88
428
  }
89
- );
429
+ );