217 lines
6.8 KiB
Markdown
217 lines
6.8 KiB
Markdown
|
---
|
||
|
title: Sheets with WebSQL
|
||
|
sidebar_label: Web SQL Database
|
||
|
pagination_prev: demos/data/index
|
||
|
pagination_next: demos/cloud/index
|
||
|
sidebar_custom_props:
|
||
|
summary: Reading and writing data in an in-browser SQL database
|
||
|
---
|
||
|
|
||
|
import CodeBlock from '@theme/CodeBlock';
|
||
|
|
||
|
WebSQL (formally "Web SQL Database") is a popular SQL-based in-browser database
|
||
|
available in Chromium and related browsers including Google Chrome. In practice,
|
||
|
it is powered by SQLite. Many SQLite-compatible queries work as-is in WebSQL.
|
||
|
|
||
|
The public demo <https://sheetjs.com/sql> generates a database from workbook.
|
||
|
|
||
|
:::caution pass
|
||
|
|
||
|
WebSQL is only supported in Chromium-based browsers including Chrome.
|
||
|
|
||
|
Safari historically supported WebSQL but Safari 13 dropped support.
|
||
|
|
||
|
Legacy browsers including Internet Explorer and Firefox never added support.
|
||
|
|
||
|
:::
|
||
|
|
||
|
:::info pass
|
||
|
|
||
|
WebSQL is not commonly available on server-side platforms. Typically scripts
|
||
|
will directly query SQLite databases using connector modules.
|
||
|
|
||
|
[The "SQLite" demo](/docs/demos/data/sqlite) covers NodeJS and other platforms.
|
||
|
|
||
|
:::
|
||
|
|
||
|
## Overview
|
||
|
|
||
|
Environments that support WebSQL expose the `openDatabase` global method. It
|
||
|
takes 4 arguments:
|
||
|
- internal database name
|
||
|
- version string (`1.0`)
|
||
|
- public display name
|
||
|
- database size (measured in bytes)
|
||
|
|
||
|
The following command attempts to connect to the database named `sheetql`. If
|
||
|
the database does not exist, it will create a new database with a hint to
|
||
|
allocate 2MB of space.
|
||
|
|
||
|
```js
|
||
|
const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);
|
||
|
```
|
||
|
|
||
|
### Transactions and Queries
|
||
|
|
||
|
Queries are performed within transactions.
|
||
|
|
||
|
`Database#transaction` passes a transaction object to the callback argument:
|
||
|
|
||
|
```js
|
||
|
db.transaction(function(tx) {
|
||
|
/* tx is a transaction object */
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Within a transaction, queries are performed with `Transaction#executeSql`. The
|
||
|
method takes 4 arguments:
|
||
|
- SQL statement stored in a string
|
||
|
- Array of parameterized query arguments
|
||
|
- Success callback
|
||
|
- Error callback
|
||
|
|
||
|
If the query succeeds, the success callback will be invoked with two arguments:
|
||
|
- Transaction object
|
||
|
- Result of the query
|
||
|
|
||
|
If the query fails, the error callback will be invoked with two arguments:
|
||
|
- Transaction object
|
||
|
- Error information
|
||
|
|
||
|
The Web SQL Database API is callback-based. The following snippet runs one query
|
||
|
and wraps the execution in a promise that resolves to the query result or
|
||
|
rejects with the error:
|
||
|
|
||
|
```js
|
||
|
function execute_simple_query(db, query) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
db.transaction(tx =>
|
||
|
tx.executeSQL(query, [],
|
||
|
(tx, data) => resolve(data),
|
||
|
(tx, err) => reject(err)
|
||
|
)
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Importing Data
|
||
|
|
||
|
Importing data from spreadsheets is straightforward using the `generate_sql`
|
||
|
helper function from ["Generating Tables"](/docs/demos/data/sql#generating-tables)
|
||
|
|
||
|
```js
|
||
|
const stmts = generate_sql(ws, wsname);
|
||
|
|
||
|
// NOTE: tx.executeSql and db.transaction use callbacks. This wraps in Promises
|
||
|
for(var stmt of stmts) await new Promise((res, rej) => {
|
||
|
db.transaction(tx =>
|
||
|
tx.executeSql(stmt, [],
|
||
|
(tx, data) => res(data), // if the query is successful, return the data
|
||
|
(tx, err) => rej(err) // if the query fails, reject with the error
|
||
|
));
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Typically worksheet objects are extracted from workbook objects[^1] generated
|
||
|
from the SheetJS `read` or `readFile` methods[^2].
|
||
|
|
||
|
### Exporting Data
|
||
|
|
||
|
The result of a SQL SELECT statement is a `SQLResultSet`. The `rows` property
|
||
|
is a `SQLResultSetRowList`. It is an "array-like" structure that has `length`
|
||
|
and properties like `0`, `1`, etc. However, this is not a real Array object!
|
||
|
|
||
|
A real Array can be created using `Array.from`. The SheetJS `json_to_sheet`
|
||
|
method[^3] can generate a worksheet object[^4] from the real array:
|
||
|
|
||
|
```js
|
||
|
db.readTransaction(tx =>
|
||
|
tx.executeSQL("SELECT * FROM DatabaseTable", [], (tx, data) => {
|
||
|
// data.rows is "array-like", so `Array.from` can make it a real array
|
||
|
const aoo = Array.from(data.rows);
|
||
|
const ws = XLSX.utils.json_to_sheet(aoo);
|
||
|
// ... perform an export here OR wrap in a Promise
|
||
|
})
|
||
|
);
|
||
|
```
|
||
|
|
||
|
Using `book_new` and `book_append_sheet`[^5], a workbook object can be created.
|
||
|
This workbook is typically exported to the filesystem with `writeFile`[^6].
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
## Live Demo
|
||
|
|
||
|
:::note
|
||
|
|
||
|
This browser demo was tested in the following environments:
|
||
|
|
||
|
| Browser | Date |
|
||
|
|:------------|:-----------|
|
||
|
| Chrome 117 | 2023-10-13 |
|
||
|
|
||
|
Some lesser-used browsers do not support WebSQL:
|
||
|
|
||
|
| Browser | Date | Support |
|
||
|
|:------------|:-----------|:------------------------------------|
|
||
|
| Safari 17.0 | 2023-10-13 | Error `Web SQL is deprecated` |
|
||
|
| Firefox 118 | 2023-10-13 | Error `openDatabase is not defined` |
|
||
|
|
||
|
:::
|
||
|
|
||
|
### Export Demo
|
||
|
|
||
|
The following demo generates a database with 5 fixed SQL statements. Queries
|
||
|
can be changed in the Live Editor. The WebSQL database can be inspected in the
|
||
|
"WebSQL" section of the "Application" Tab of Developer Tools:
|
||
|
|
||
|
![WebSQL view in Developer Tools](pathname:///files/websql.png)
|
||
|
|
||
|
```jsx live
|
||
|
function SheetQL() {
|
||
|
const [out, setOut] = React.useState("");
|
||
|
const queries = [
|
||
|
'DROP TABLE IF EXISTS Presidents',
|
||
|
'CREATE TABLE Presidents (Name TEXT, Idx REAL)',
|
||
|
'INSERT INTO Presidents (Name, Idx) VALUES ("Barack Obama", 44)',
|
||
|
'INSERT INTO Presidents (Name, Idx) VALUES ("Donald Trump", 45)',
|
||
|
'INSERT INTO Presidents (Name, Idx) VALUES ("Joseph Biden", 46)'
|
||
|
];
|
||
|
const xport = React.useCallback(async() => {
|
||
|
/* prep database */
|
||
|
const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);
|
||
|
|
||
|
for(var q of queries) await new Promise((res, rej) => {
|
||
|
db.transaction((tx) => {
|
||
|
tx.executeSql(q, [], (tx, data) => res(data), (tx, err) => rej(err));
|
||
|
});
|
||
|
});
|
||
|
|
||
|
/* pull data and generate rows */
|
||
|
db.readTransaction(tx => {
|
||
|
tx.executeSql("SELECT * FROM Presidents", [], (tx, data) => {
|
||
|
const aoo = Array.from(data.rows);
|
||
|
setOut("QUERY RESULT:\n" + aoo.map(r => JSON.stringify(r)).join("\n") + "\n")
|
||
|
const ws = XLSX.utils.json_to_sheet(aoo);
|
||
|
const wb = XLSX.utils.book_new();
|
||
|
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
|
||
|
XLSX.writeFile(wb, "SheetQL.xlsx");
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
return ( <pre>{out}<button onClick={xport}><b>Fetch!</b></button></pre> );
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Server-Side SQLite
|
||
|
|
||
|
**[The exposition has been moved to a separate page.](/docs/demos/data/sqlite)**
|
||
|
|
||
|
[^1]: See ["Workbook Object"](/docs/csf/book)
|
||
|
[^2]: See [`read` and `readFile` in "Reading Files"](/docs/api/parse-options)
|
||
|
[^3]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
|
||
|
[^4]: See ["Sheet Objects"](/docs/csf/sheet)
|
||
|
[^5]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||
|
[^6]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
|