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.
- package/CONTRIBUTING.md +50 -0
- package/README.md +16 -11
- package/docs/api.md +0 -2
- package/docs/examples-pipeline.md +33 -1
- package/docs/retold-catalog.json +20 -1
- package/docs/retold-keyword-index.json +1 -1
- package/package.json +5 -5
- package/source/Meadow-Connection-SQLite.js +28 -60
- package/test/SQLite_tests.js +351 -11
package/CONTRIBUTING.md
ADDED
|
@@ -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` | `
|
|
135
|
-
| `GUID` | `
|
|
136
|
-
| `ForeignKey` | `
|
|
137
|
-
| `Numeric` | `
|
|
138
|
-
| `Decimal` | `
|
|
139
|
-
| `String` | `
|
|
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` | `
|
|
142
|
-
| `Boolean` | `
|
|
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
|
|
175
|
+
MIT
|
|
171
176
|
|
|
172
|
-
##
|
|
177
|
+
## Contributing
|
|
173
178
|
|
|
174
|
-
|
|
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
|
-
|
|
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(`
|
package/docs/retold-catalog.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"Generated": "2026-02-
|
|
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
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meadow-connection-sqlite",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
46
|
-
"retold-harness": "^1.0
|
|
45
|
+
"quackage": "^1.0.51",
|
|
46
|
+
"retold-harness": "^1.1.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"better-sqlite3": "^12.
|
|
50
|
-
"fable-serviceproviderbase": "^3.0.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 += `
|
|
67
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT DEFAULT '00000000-0000-0000-0000-000000000000'`;
|
|
80
68
|
break;
|
|
81
69
|
case 'ForeignKey':
|
|
82
|
-
tmpCreateTableStatement += `
|
|
83
|
-
tmpPrimaryKey = tmpColumn.Column;
|
|
70
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} INTEGER NOT NULL DEFAULT 0`;
|
|
84
71
|
break;
|
|
85
72
|
case 'Numeric':
|
|
86
|
-
tmpCreateTableStatement += `
|
|
73
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} INTEGER NOT NULL DEFAULT 0`;
|
|
87
74
|
break;
|
|
88
75
|
case 'Decimal':
|
|
89
|
-
tmpCreateTableStatement += `
|
|
76
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} REAL`;
|
|
90
77
|
break;
|
|
91
78
|
case 'String':
|
|
92
|
-
tmpCreateTableStatement += `
|
|
79
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT NOT NULL DEFAULT ''`;
|
|
93
80
|
break;
|
|
94
81
|
case 'Text':
|
|
95
|
-
tmpCreateTableStatement += `
|
|
82
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT`;
|
|
96
83
|
break;
|
|
97
84
|
case 'DateTime':
|
|
98
|
-
tmpCreateTableStatement += `
|
|
85
|
+
tmpCreateTableStatement += ` ${tmpColumn.Column} TEXT`;
|
|
99
86
|
break;
|
|
100
87
|
case 'Boolean':
|
|
101
|
-
tmpCreateTableStatement += `
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
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.
|
|
191
|
+
if (this.connected && this._database)
|
|
226
192
|
{
|
|
227
|
-
|
|
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
|
|
199
|
+
throw new Error('The Meadow SQLite provider is not connected; cannot create a prepared statement.');
|
|
232
200
|
}
|
|
233
201
|
}
|
|
234
202
|
|
package/test/SQLite_tests.js
CHANGED
|
@@ -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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
+
);
|