jeep-sqlite
jeep-sqlite
is a Stencil component to create SQLite database and query it in the browser. The entire database is stored in an IndexedDB
store named jeepSQLiteStore
in a table databases
. Multiple databases can be stored on this table.
jeep-sqlite
is based on sql.js
for SQLite queries and localforage
for database storage in IndexedDB.
This component might be used in PWA applications. It will also be used in the web implementation of the @capacitor-community-sqlite
.
This is the reason for having similar API than the @capacitor-community-sqlite
. Look at the @capacitor-community-sqlite
documentation and CHANGELOG.md
It will be used at that stage to test the integration with the @capacitor-community-sqlite
but can also be used in development of Stencil
or Ionic/Angular
applications.
Integration in other frameworks (Vue
, React
, Ionic/Vue
, Ionic/React
) will be looked at later but if some of you want to contribute feel free.
Stencil is also great for building entire apps. For that, use the stencil-app-starter instead.
Getting Started
Script tag
- Put a script tag similar to this
<script type="module" src="https://cdn.jsdelivr.net/npm/jeep-sqlite@latest/dist/jeep-sqlite/jeep-sqlite.esm.js"></script>
in the head of your index.html
- Then you can use the element anywhere in your template, JSX, html etc
Node Modules
- Run
npm install jeep-sqlite --save
- Put a script tag similar to this
<script src='node_modules/jeep-sqlite/dist/jeep-sqlite.esm.js'></script>
in the head of your index.html - Then you can use the element anywhere in your template, JSX, html etc
In a stencil-starter app
- Run
npm install jeep-sqlite --save
- Add an import to the npm packages
import jeep-sqlite;
- Then you can use the element anywhere in your template, JSX, html etc
Supported methods
Name | Web |
---|
echo | ✅ |
createConnection | ✅ |
closeConnection | ✅ |
open (non-encrypted DB) | ✅ |
close | ✅ |
execute | ✅ |
executeSet | ✅ |
run | ✅ |
query | ✅ |
deleteDatabase | ✅ |
isDBExists | ✅ |
isDBOpen | ✅ |
isStoreOpen | ✅ |
isTableExists | ✅ |
createSyncTable | ✅ |
getSyncDate | ✅ |
setSyncDate | ✅ |
isJsonValid | ✅ |
importFromJson | ✅ |
exportToJson | ✅ |
copyFromAssets | ✅ |
addUpgradeStatement | ✅ |
isDatabase | ✅ |
getDatabaseList | ✅ |
getTableList | ✅ |
checkConnectionsConsistency | ✅ |
saveToStore | ✅ |
The database is saved when you run the methods close
or closeConnection
, in the Browser Storage IndexedDB
as a localforage
store under the jeepSqliteStore
name and databases
table name.
The copyFromAssets
required to have a databases.json
file having the name of the databases in the assets/databases
The databases.json
file is for example
{
"databaseList" : [
"dbForCopy.db",
"myDBSQLite.db"
]
}
if dbForCopy.db
and myDBSQLite.db
are databases located in the assets/databases
folder.
Supported Events
Name | Web |
---|
jeepSqliteImportProgress | ✅ |
jeepSqliteExportProgress | ✅ |
Usage
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
<title>Stencil Component Starter</title>
<script type="module" src="/build/jeep-sqlite.esm.js"></script>
<script nomodule src="/build/jeep-sqlite.js"></script>
</head>
<body>
<jeep-sqlite></jeep-sqlite>
</body>
</html>
<script>
(async () => {
await customElements.whenDefined('jeep-sqlite');
const jeepSqlite = document.querySelector('jeep-sqlite');
let result = await jeepSqlite.echo("Hello World from Jeep");
if(await jeepSqlite.isStoreOpen()) {
try {
await jeepSqlite.createConnection({
database:"testNew",
version: 1
});
await jeepSqlite.open({database: "testNew"});
const isDB = await jeepSqlite.isDBOpen({database: "testNew"})
let sql = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY NOT NULL,email TEXT UNIQUE NOT NULL,name TEXT,company TEXT,size REAL,age INTEGER,last_modified INTEGER DEFAULT (strftime('%s', 'now')));";
sql += "CREATE INDEX IF NOT EXISTS users_index_name ON users (name);";
sql += "CREATE INDEX IF NOT EXISTS users_index_last_modified ON users (last_modified);";
sql += "CREATE TRIGGER IF NOT EXISTS users_trigger_last_modified AFTER UPDATE ON users FOR EACH ROW WHEN NEW.last_modified <= OLD.last_modified BEGIN UPDATE users SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id; END;";
sql += "PRAGMA user_version = 1;";
let ret = await jeepSqlite.execute({database: "testNew", statements: sql});
ret = await jeepSqlite.isTableExists({database: "testNew", table: "users"});
ret = await jeepSqlite.isTableExists({database: "testNew", table: "contact"});
ret = await jeepSqlite.createSyncTable({database: "testNew"});
let syncDate = "2021-08-01T08:42:25.000Z";
await jeepSqlite.setSyncDate({database: "testNew", syncdate: syncDate});
ret = await jeepSqlite.getSyncDate({database: "testNew"});
if(ret.syncDate !== 1627807345) {
throw new Error("Get the synchronization date failed");
}
const row = [["Whiteley","Whiteley.com",30,1.83],["Jones","Jones.com",44,1.75]];
let delUsers = `DELETE FROM users;`;
delUsers += `VACUUM;`;
ret = await jeepSqlite.execute({database: "testNew", statements: delUsers, transaction: false});
let twoUsers = `INSERT INTO users (name,email,age,size) VALUES ("${row[0][0]}","${row[0][1]}",${row[0][2]},${row[0][3]});`;
twoUsers += `INSERT INTO users (name,email,age,size) VALUES ("${row[1][0]}","${row[1][1]}",${row[1][2]},${row[1][3]});`;
ret = await jeepSqlite.execute({database: "testNew", statements: twoUsers});
if (ret.changes.changes !== 2) {
throw new Error("Execute 3 users failed");
}
await jeepSqlite.saveToStore({database: "testNew"});
ret = await jeepSqlite.query({database: "testNew",
statement: "SELECT * FROM users;"});
ret = await jeepSqlite.query({database: "testNew",
statement: "SELECT * FROM users where size > ?;",
values:[1.80]});
let sqlcmd = "INSERT INTO users (name,email,age,size,company) VALUES (?,?,?,?,?)";
let values = ["Simpson","Simpson@example.com",69,1.82,null];
ret = await jeepSqlite.run({database: "testNew",
statement: sqlcmd,
values: values});
if(ret.changes.lastId !== 3) {
throw new Error("Run 1 user failed");
}
sqlcmd = `INSERT INTO users (name,email,age,size,company) VALUES ` +
`("Brown","Brown@example.com",15,1.75,null)`;
ret = await jeepSqlite.run({database: "testNew",
statement: sqlcmd});
if(ret.changes.lastId !== 4) {
throw new Error("Run 2 user failed");
}
ret = await jeepSqlite.query({database: "testNew",
statement: "SELECT * FROM users;"});
if(ret.values.length != 4) {
throw new Error("Query 3 user failed");
}
await jeepSqlite.createConnection({
database:"testSet",
version: 1
});
ret = await jeepSqlite.isDBExists({database:"testSet"});
if (ret.result) {
await jeepSqlite.deleteDatabase({database:"testSet"});
}
const createSchemaContacts = `
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY NOT NULL,
email TEXT UNIQUE NOT NULL,
name TEXT,
FirstName TEXT,
company TEXT,
size REAL,
age INTEGER,
MobileNumber TEXT
);
CREATE INDEX IF NOT EXISTS contacts_index_name ON contacts (name);
CREATE INDEX IF NOT EXISTS contacts_index_email ON contacts (email);
PRAGMA user_version = 1;
`;
await jeepSqlite.open({database: "testSet"});
const isDBSet = await jeepSqlite.isDBOpen({database: "testSet"})
const setContacts = [
{ statement:"INSERT INTO contacts (name,FirstName,email,company,age,MobileNumber) VALUES (?,?,?,?,?,?);",
values:["Simpson","Tom","Simpson@example.com",null,69,"4405060708"]
},
{ statement:"INSERT INTO contacts (name,FirstName,email,company,age,MobileNumber) VALUES (?,?,?,?,?,?);",
values:[
["Jones","David","Jones@example.com",,42.1,"4404030201"],
["Whiteley","Dave","Whiteley@example.com",,45.3,"4405162732"],
["Brown","John","Brown@example.com",null,35,"4405243853"]
]
},
{ statement:"UPDATE contacts SET age = ? , MobileNumber = ? WHERE id = ?;",
values:[51.4,"4404030202",2]
}
];
ret = await jeepSqlite.execute({database: "testSet", statements: createSchemaContacts});
ret = await jeepSqlite.executeSet({database: "testSet", set: setContacts});
if (ret.changes.changes !== 5) {
return Promise.reject(new Error("ExecuteSet 5 contacts failed"));
}
const dataToImport = {
database : "db-from-json",
version : 1,
encrypted : false,
mode : "full",
tables :[
{
name: "users",
schema: [
{column:"id", value: "INTEGER PRIMARY KEY NOT NULL"},
{column:"email", value:"TEXT UNIQUE NOT NULL"},
{column:"name", value:"TEXT"},
{column:"age", value:"REAL"},
{column:"last_modified", value:"INTEGER"}
],
indexes: [
{name: "index_user_on_name",value: "name"},
{name: "index_user_on_last_modified",value: "last_modified DESC"},
{name: "index_user_on_email_name", value: "email ASC, name", mode: "UNIQUE"}
],
values: [
[1,"Whiteley.com","Whiteley",30.5,1582536810],
[2,"Jones.com","Jones",44.2,1582812800],
[3,"Simpson@example.com","Simpson",69,1583570630],
[4,"Brown@example.com","Brown",15,1590383895]
]
},
{
name: "messages",
schema: [
{column:"id", value: "INTEGER PRIMARY KEY NOT NULL"},
{column:"title", value:"TEXT NOT NULL"},
{column:"body", value:"TEXT NOT NULL"},
{column:"last_modified", value:"INTEGER"}
],
values: [
[1,"test post 1","content test post 1",1587310030],
[2,"test post 2","content test post 2",1590388125]
]
},
]
};
result = await jeepSqlite.isJsonValid({jsonstring: JSON.stringify(dataToImport)});
if(!result.result) {
return Promise.reject(new Error("IsJsonValid failed"));
}
result = await jeepSqlite.importFromJson({jsonstring: JSON.stringify(dataToImport)});
if(result.changes.changes === -1 ) return Promise.reject(new Error("ImportFromJson 'full' dataToImport failed"));
await jeepSqlite.createConnection({database:"db-from-json", version: 1});
await jeepSqlite.open({database: "db-from-json"});
const isDBJson = await jeepSqlite.isDBOpen({database: "db-from-json"})
if (!isDBJson.result) return Promise.reject(new Error("isDBOpen 'db-from-json' failed"));
ret = await jeepSqlite.createSyncTable({database: "db-from-json"});
if (result.changes.changes < 0) return Promise.reject(new Error("CreateSyncTable failed"));
ret = await jeepSqlite.getSyncDate({database: "db-from-json"});
if(ret.length === 0) return Promise.reject(new Error("GetSyncDate failed"));
ret = await jeepSqlite.query({database: "db-from-json",
statement: "SELECT * FROM users;"});
if(ret.values.length !== 4 ||
ret.values[0].name !== "Whiteley" ||
ret.values[1].name !== "Jones" ||
ret.values[2].name !== "Simpson" ||
ret.values[3].name !== "Brown" ) {
return Promise.reject(new Error("Query 1 db-from-json Users failed"));
}
await jeepSqlite.closeConnection({database:"db-from-json"});
const partialImport1 = {
database : "db-from-json",
version : 1,
encrypted : false,
mode : "partial",
tables :[
{
name: "users",
values: [
[5,"Addington.com","Addington",22.7,1590388335],
[6,"Bannister.com","Bannister",59,1590393015],
[2,"Jones@example.com","Jones",45,1590393325]
]
},
]
};
result = await jeepSqlite.isJsonValid({jsonstring: JSON.stringify(partialImport1)});
if(!result.result) {
return Promise.reject(new Error("IsJsonValid failed"));
}
result = await jeepSqlite.importFromJson({jsonstring: JSON.stringify(partialImport1)});
if(result.changes.changes === -1 ) return Promise.reject(new Error("ImportFromJson 'partial1' dataToImport failed"));
const partialImport2 = {
database : "db-from-json",
version : 1,
encrypted : false,
mode : "partial",
tables :[
{
name: "messages",
indexes: [
{name: "index_messages_on_title",value: "title"},
{name: "index_messages_on_last_modified",value: "last_modified DESC"}
],
values: [
[3,"test post 3","content test post 3",1590396146],
[4,"test post 4","content test post 4",1590396288]
]
}
]
};
result = await jeepSqlite.importFromJson({jsonstring: JSON.stringify(partialImport2)});
if(result.changes.changes === -1 ) return Promise.reject(new Error("ImportFromJson 'partial2' dataToImport failed"));
const partialImport3 = {
database : "db-from-json",
version : 1,
encrypted : false,
mode : "partial",
tables :[
{
name: "test113",
schema: [
{column:"id", value: "TEXT PRIMARY KEY NOT NULL"},
{column:"name", value:"TEXT UNIQUE NOT NULL"},
{column:"code", value:"TEXT"},
{column:"last_modified", value:"INTEGER"}
],
indexes: [
{name: "index_test113_on_title",value: "name"},
{name: "index_test113_on_last_modified",value: "last_modified DESC"}
],
values: [
["ef5c57d5-b885-49a9-9c4d-8b340e4abdbc","valve","BV50",1590396146],
["bced3262-5d42-470a-9585-d3fd12c45452","pipe","PIPE100",1590396288],
["ef5c57d5-b885-49a9-9c4d-8b340e4abdbc","valve","BV100",1590396300],
]
}
]
};
result = await jeepSqlite.importFromJson({jsonstring: JSON.stringify(partialImport3)});
if(result.changes.changes === -1 ) return Promise.reject(new Error("ImportFromJson 'partial3' dataToImport failed"));
await jeepSqlite.createConnection({database:"db-from-json", version: 1});
await jeepSqlite.open({database: "db-from-json"});
ret = await jeepSqlite.query({database: "db-from-json",
statement: "SELECT * FROM users;"});
if(ret.values.length !== 6 ||
ret.values[0].name !== "Whiteley" ||
ret.values[1].name !== "Jones" ||
ret.values[2].name !== "Simpson" ||
ret.values[3].name !== "Brown" ||
ret.values[4].name !== "Addington" ||
ret.values[5].name !== "Bannister" ) {
return Promise.reject(new Error("Query 2 db-from-json Users failed"));
}
ret = await jeepSqlite.query({database: "db-from-json",
statement: "SELECT * FROM messages;"});
if(ret.values.length !== 4 ||
ret.values[0].title !== "test post 1" ||
ret.values[1].title !== "test post 2" ||
ret.values[2].title !== "test post 3" ||
ret.values[3].title !== "test post 4" ) {
return Promise.reject(new Error("Query 3 db-from-json Messages failed"));
}
let jsonObj = await jeepSqlite.exportToJson({database: "db-from-json",jsonexportmode: 'full'});
result = await jeepSqlite.isJsonValid({jsonstring: JSON.stringify(jsonObj.export)});
if(!result.result) {
return Promise.reject(new Error("IsJsonValid 'full' export failed"));
}
syncDate = "2020-05-20T18:40:00.000Z";
await jeepSqlite.setSyncDate({database: "db-from-json", syncdate: syncDate});
jsonObj = await jeepSqlite.exportToJson({database: "db-from-json",jsonexportmode: 'partial'});
result = await jeepSqlite.isJsonValid({jsonstring: JSON.stringify(jsonObj.export)});
if(!result.result) {
return Promise.reject(new Error("IsJsonValid 'partial' export failed"));
}
if(jsonObj.export.tables.length != 3 || jsonObj.export.tables[0].name != 'users'
|| jsonObj.export.tables[1].name != 'messages'
|| jsonObj.export.tables[2].name != 'test113'
|| jsonObj.export.tables[0].values.length != 4
|| jsonObj.export.tables[1].values.length != 3
|| jsonObj.export.tables[2].values.length != 2) {
return Promise.reject(new Error("IsJsonValid 'partial' export failed: No 4 tables"));
}
await jeepSqlite.closeConnection({database:"testNew"});
await jeepSqlite.closeConnection({database:"testSet"});
await jeepSqlite.closeConnection({database:"db-from-json"});
await jeepSqlite.copyFromAssets();
await jeepSqlite.createConnection({database:"myDB",version: 1});
await jeepSqlite.open({database: "myDB"});
let statement = `SELECT name FROM sqlite_master WHERE type='table';`;
ret = await jeepSqlite.query({database: "myDB",
statement: statement});
if(ret.values.length !== 3 ||
ret.values[0].name !== "users" ||
ret.values[1].name !== "messages" ||
ret.values[2].name !== "sync_table"
) {
throw new Error("Query MyDB Tables failed");
}
ret = await jeepSqlite.query({database: "myDB",
statement: "SELECT * FROM users;"});
if(ret.values.length != 7 ||
ret.values[0].name !== "Whiteley" ||
ret.values[1].name !== "Jones" ||
ret.values[2].name !== "Simpson" ||
ret.values[3].name !== "Brown" ||
ret.values[4].name !== "Jackson" ||
ret.values[5].name !== "Kennedy" ||
ret.values[6].name !== "Bush"
) {
throw new Error("Query MyDB Users failed");
}
await jeepSqlite.closeConnection({database:"myDB"});
await jeepSqlite.createConnection({database:"dbForCopy",version: 1});
await jeepSqlite.open({database: "dbForCopy"});
ret = await jeepSqlite.query({database: "dbForCopy",
statement: "SELECT * FROM areas;"});
if(ret.values.length != 3 ||
ret.values[0].name !== "Access road" ||
ret.values[1].name !== "Accessway" ||
ret.values[2].name !== "Air handling system" ) {
throw new Error("Query dbForCopy Areas failed");
}
await jeepSqlite.closeConnection({database:"dbForCopy"});
await jeepSqlite.createConnection({
database:"test-updversion",
version: 1
});
ret = await jeepSqlite.isDBExists({database:"test-updversion"});
if (ret.result) {
await jeepSqlite.deleteDatabase({database:"test-updversion"});
}
await jeepSqlite.open({database: "test-updversion"});
const createSchemaVersion1 = `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY NOT NULL,
email TEXT UNIQUE NOT NULL,
name TEXT,
company TEXT,
size REAL,
age INTEGER,
last_modified INTEGER DEFAULT (strftime('%s', 'now'))
);
CREATE INDEX IF NOT EXISTS users_index_name ON users (name);
CREATE INDEX IF NOT EXISTS users_index_last_modified ON users (last_modified);
CREATE TRIGGER IF NOT EXISTS users_trigger_last_modified
AFTER UPDATE ON users
FOR EACH ROW WHEN NEW.last_modified <= OLD.last_modified
BEGIN
UPDATE users SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id;
END;
`;
ret = await jeepSqlite.execute({database: "test-updversion", statements: createSchemaVersion1});
if (ret.changes.changes < 0) {
throw new Error("Execute createSchemaVersion1 failed");
}
const rowU = [["Whiteley","Whiteley.com",30.5],["Jones","Jones.com",44]];
const twoUsersU = `
DELETE FROM users;
INSERT INTO users (name,email,age) VALUES ("${rowU[0][0]}","${rowU[0][1]}",${rowU[0][2]});
INSERT INTO users (name,email,age) VALUES ("${rowU[1][0]}","${rowU[1][1]}",${rowU[1][2]});
`;
ret = await jeepSqlite.execute({database: "test-updversion", statements: twoUsersU});
if (ret.changes.changes !== 2) {
throw new Error("Execute twoUsers failed");
}
ret = await jeepSqlite.query({database: "test-updversion",
statement: "SELECT * FROM users;"});
if(ret.values.length !== 2 ||
ret.values[0].name !== "Whiteley" ||
ret.values[1].name !== "Jones") {
throw new Error("Query 2 Users failed");
}
await jeepSqlite.closeConnection({database:"test-updversion"});
const createSchemaVersion2 = `
CREATE TABLE users (
id INTEGER PRIMARY KEY NOT NULL,
email TEXT UNIQUE NOT NULL,
name TEXT,
company TEXT,
country TEXT,
age INTEGER,
last_modified INTEGER DEFAULT (strftime('%s', 'now'))
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY NOT NULL,
userid INTEGER,
title TEXT NOT NULL,
body TEXT NOT NULL,
last_modified INTEGER DEFAULT (strftime('%s', 'now')),
FOREIGN KEY (userid) REFERENCES users(id) ON DELETE SET DEFAULT
);
CREATE INDEX users_index_name ON users (name);
CREATE INDEX users_index_last_modified ON users (last_modified);
CREATE INDEX messages_index_title ON messages (title);
CREATE INDEX messages_index_last_modified ON messages (last_modified);
CREATE TRIGGER users_trigger_last_modified
AFTER UPDATE ON users
FOR EACH ROW WHEN NEW.last_modified <= OLD.last_modified
BEGIN
UPDATE users SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id;
END;
CREATE TRIGGER messages_trigger_last_modified
AFTER UPDATE ON messages
FOR EACH ROW WHEN NEW.last_modified <= OLD.last_modified
BEGIN
UPDATE messages SET last_modified= (strftime('%s', 'now')) WHERE id=OLD.id;
END;
`;
setArrayVersion2 = [
{ statement:"INSERT INTO messages (userid,title,body) VALUES (?,?,?);",
values:[
[1,"test message 1","content test message 1"],
[2,"test message 2","content test message 2"],
[1,"test message 3","content test message 3"]
]
},
{ statement:"UPDATE users SET country = ? WHERE id = ?;",
values:["United Kingdom",1]
},
{ statement:"UPDATE users SET country = ? WHERE id = ?;",
values:["Australia",2]
},
];
await jeepSqlite.addUpgradeStatement({database: "test-updversion",
upgrade: [{fromVersion: 1, toVersion: 2, statement: createSchemaVersion2,
set: setArrayVersion2}]
});
await jeepSqlite.createConnection({
database:"test-updversion",
version: 2
});
await jeepSqlite.open({database: "test-updversion"});
ret = await jeepSqlite.query({database: "test-updversion",
statement: "SELECT country FROM users;"});
if(ret.values.length !== 2 ||
ret.values[0].country !== "United Kingdom" ||
ret.values[1].country !== "Australia") {
throw new Error("Query Version 2 Users failed");
}
const userMessages = `
SELECT users.name,messages.title,messages.body FROM users
INNER JOIN messages ON users.id = messages.userid
WHERE users.id = ?;
`;
ret = await jeepSqlite.query({database: "test-updversion",
statement: userMessages,
values : [1]});
if(ret.values.length !== 2 ||
ret.values[0].name !== "Whiteley" ||
ret.values[0].title !== "test message 1" ||
ret.values[1].name !== "Whiteley" ||
ret.values[1].title !== "test message 3") {
return Promise.reject(new Error("Query Messages User 1 Version 2 failed"));
}
ret = await jeepSqlite.query({database: "test-updversion",
statement: userMessages,
values : [2]});
if(ret.values.length !== 1 ||
ret.values[0].name !== "Jones" ||
ret.values[0].title !== "test message 2") {
return Promise.reject(new Error("Query Messages User 2 Version 2 failed"));
}
await jeepSqlite.closeConnection({database:"test-updversion"});
console.log("db success");
} catch (err) {
console.log(`Error ${err}`);
}
} else {
console.log("store creation failed")
}
})();
</script>