meadow-connection-sqlite 1.0.10 → 1.0.11
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 +8 -8
- package/docs/api.md +0 -2
- package/docs/examples-pipeline.md +33 -1
- package/package.json +1 -1
- package/source/Meadow-Connection-SQLite.js +28 -60
- package/test/SQLite_tests.js +351 -11
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
|
|
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/package.json
CHANGED
|
@@ -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
|
+
);
|