forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			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: | ||
|  | 
 | ||
|  |  | ||
|  | 
 | ||
|  | ```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) |