Remove bugged tables from DB. Got fast native DB working.

master
Sander Vocke 6 years ago
parent 7cbeaf1a83
commit c122755451
  1. BIN
      public/digikam4.db
  2. BIN
      public/test.sqlite
  3. 179
      src/database.js
  4. 25
      src/index.js

Binary file not shown.

Binary file not shown.

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
var DBTypeEnum = { export const DBTypeEnum = {
ALASQL_SQLITE: 1, ALASQL_SQLITE: 1,
ALASQL_INDEXEDDB: 2 // TODO: implement with migration ALASQL_INDEXEDDB: 2,
ALASQL_NATIVE: 3
}; };
export class DB { export class DB {
@ -22,44 +23,65 @@ export class DB {
// Perform a chain of asynchronous queries, each one waiting for the last before // Perform a chain of asynchronous queries, each one waiting for the last before
// starting. Not supported on all database types. // starting. Not supported on all database types.
queries_async(s) { queries_async(s) {
if(this.state.db_type === DBTypeEnum.ALASQL_SQLITE || if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE ||
this.state.db_type === DBTypeEnum.ALASQL_INDEXEDDB) { this.state.db_type === DBTypeEnum.ALASQL_INDEXEDDB ||
var self = this; this.state.db_type === DBTypeEnum.ALASQL_NATIVE) {
var p = Promise.resolve(null); var self = this;
for(let i = 0; i < s.length; i++) { var p = Promise.resolve(null);
p = p.then(() => { return self.state.db_object.promise(s[i]); }); for (let i = 0; i < s.length; i++) {
} p = p.then(() => { return self.state.db_object.promise(s[i]); });
return p;
} }
throw new Error("Unsupported database type for async operations: " + this.state.db_type);
return p;
}
throw new Error("Unsupported database type for async operations: " + this.state.db_type);
} }
// Perform a series of queries sequentially, then return the last result. // Perform a series of queries sequentially, then return the last result.
// Note that synchronous operation is not supported for all database types. // Note that synchronous operation is not supported for all database types.
queries_sync(s) { queries_sync(s) {
if(this.state.db_type === DBTypeEnum.ALASQL_SQLITE) { if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE ||
for (var i = 0; i < (s.length-1); i++) { this.state.db_type === DBTypeEnum.ALASQL_NATIVE) {
for (var i = 0; i < (s.length - 1); i++) {
this.state.db_object(s[i]); this.state.db_object(s[i]);
} }
return this.state.db_object(s[s.length-1]); return this.state.db_object(s[s.length - 1]);
} }
throw new Error("Unsupported database type for sync operations: " + this.state.db_type); throw new Error("Unsupported database type for sync operations: " + this.state.db_type);
} }
migrate_async(db_type, db_name) { migrate_async(db_type, db_name) {
var self = this; var self = this;
if(this.state.db_type === DBTypeEnum.ALASQL_SQLITE && db_type === DBTypeEnum.ALASQL_INDEXEDDB) { if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE && db_type === DBTypeEnum.ALASQL_INDEXEDDB) {
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
var tables = self.queries_sync(["SHOW TABLES;"]); var tables = self.queries_sync(["SHOW TABLES;"]);
var queries = []; var queries = [];
queries.push("DROP INDEXEDDB DATABASE " + db_name + ";"); queries.push("DROP INDEXEDDB DATABASE IF EXISTS " + db_name + ";");
queries.push("CREATE INDEXEDDB DATABASE IF NOT EXISTS " + db_name + ";"); queries.push("CREATE INDEXEDDB DATABASE IF NOT EXISTS " + db_name + ";");
queries.push("ATTACH INDEXEDDB DATABASE " + db_name + ";"); queries.push("ATTACH INDEXEDDB DATABASE " + db_name + ";");
tables.forEach(elem => { tables.forEach(elem => {
queries.push("CREATE TABLE " + db_name + "." + elem.tableid + ";"); queries.push("CREATE TABLE " + db_name + "." + elem.tableid + ";");
queries.push("SELECT * INTO " + db_name + "." + elem.tableid + queries.push("SELECT * INTO " + db_name + "." + elem.tableid +
" FROM " + self.state.db_name + "." + elem.tableid + ";"); " FROM " + self.state.db_name + "." + elem.tableid + ";");
});
queries.push("DETACH DATABASE " + self.state.db_name + ";");
queries.push("USE " + db_name + ";");
self.queries_async(queries).then(() => {
self.state.db_type = db_type;
self.state.db_name = db_name;
resolve();
});
});
} else if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE && db_type === DBTypeEnum.ALASQL_NATIVE) {
return new Promise(function (resolve, reject) {
var tables = self.queries_sync(["SHOW TABLES;"]);
var queries = [];
queries.push("DROP DATABASE IF EXISTS " + db_name + ";");
queries.push("CREATE DATABASE IF NOT EXISTS " + db_name + ";");
tables.forEach(elem => {
queries.push("CREATE TABLE " + db_name + "." + elem.tableid + ";");
queries.push("SELECT * INTO " + db_name + "." + elem.tableid +
" FROM " + self.state.db_name + "." + elem.tableid + ";");
}); });
queries.push("DETACH DATABASE " + self.state.db_name + ";"); queries.push("DETACH DATABASE " + self.state.db_name + ";");
queries.push("USE " + db_name + ";"); queries.push("USE " + db_name + ";");
@ -75,28 +97,56 @@ export class DB {
} }
// TODO random database names // TODO random database names
function fetch_db_from_sqlite(filename) { function fetch_db_from_sqlite(filename, db_name) {
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
var initSqlJs = require('sql.js'); var initSqlJs = require('sql.js');
initSqlJs({ locateFile: filename => `/sql.js/dist/${filename}` }) initSqlJs({ locateFile: filename => `/sql.js/dist/${filename}` })
.then( SQL => { .then(SQL => {
var asql = require('alasql'); var asql = require('alasql');
// Attach to the SQLite database file, asql.promise("ATTACH SQLITE DATABASE " + db_name + "(\"" + filename + "\");")
// Create an IndexedDB to migrate it to, named "db", .then(res => { asql.promise("USE " + db_name + ";"); })
// Get tables .then(res => {
var query = ""; var imported_db = new DB(asql, db_name, DBTypeEnum.ALASQL_SQLITE);
query += "ATTACH SQLITE DATABASE db(\"" + filename + "\"); USE db;"; resolve(imported_db);
asql.promise(query) });
});
});
}
function create_indexed_db(name) {
return new Promise(function (resolve, reject) {
var asql = require('alasql');
asql.promise("DROP INDEXEDDB DATABASE " + name + ";")
.then(res => { asql.promise("CREATE INDEXEDDB DATABASE IF NOT EXISTS " + name + ";"); })
.then(res => { asql.promise("ATTACH INDEXEDDB DATABASE " + name + ";"); })
//.then(res => { asql.promise("USE " + name + ";"); }) // TODO why does this break things?
.then(res => { .then(res => {
var imported_db = new DB(asql, "db", DBTypeEnum.ALASQL_SQLITE); var created_db = new DB(asql, name, DBTypeEnum.ALASQL_INDEXEDDB);
resolve(imported_db); resolve(created_db);
}); });
});
}); });
} }
// TODO: generalize to FetchDB function create_native_db(name) {
export class FetchSQLiteDB extends React.Component { return new Promise(function (resolve, reject) {
var asql = require('alasql');
asql.promise("DROP DATABASE " + name + ";")
.then(res => { asql.promise("CREATE DATABASE IF NOT EXISTS " + name + ";"); })
.then(res => { asql.promise("ATTACH DATABASE " + name + ";"); })
//.then(res => { asql.promise("USE " + name + ";"); }) // TODO why does this break things?
.then(res => {
var created_db = new DB(asql, name, DBTypeEnum.ALASQL_INDEXEDDB);
resolve(created_db);
});
});
}
export const DBSourceEnum = {
ATTACHFILE: 1,
CREATE: 2
}
export class ProvideDB extends React.Component {
state = { state = {
loading: true, loading: true,
error: false, error: false,
@ -105,14 +155,35 @@ export class FetchSQLiteDB extends React.Component {
}; };
componentDidMount() { componentDidMount() {
fetch_db_from_sqlite(this.props.sqlite_file) if (this.props.db_source_type === DBTypeEnum.ALASQL_SQLITE && this.props.db_source === DBSourceEnum.ATTACHFILE) {
.then(db => { fetch_db_from_sqlite(this.props.db_file, this.props.db_source_name)
if(this.props.migrate_to_indexeddb) { .then(db => {
db.migrate_async(DBTypeEnum.ALASQL_INDEXEDDB, "indexed_db") if (this.props.db_target_type !== this.props.db_source_type) {
.then(() => { this.setState({ loading: false, done: true, db: db }) }); db.migrate_async(this.props.db_target_type, this.props.db_target_name)
} else { this.setState({ loading: false, done: true, db: db }) }; .then(() => { this.setState({ loading: false, done: true, db: db }) });
}) } else { this.setState({ loading: false, done: true, db: db }) };
.catch(error => this.setState({ loading: false, done: false, error })); })
.catch(error => this.setState({ loading: false, done: false, error }));
} else if (this.props.db_source_type === DBTypeEnum.ALASQL_INDEXEDDB && this.props.db_source === DBSourceEnum.CREATE) {
create_indexed_db(this.props.db_source_name)
.then(db => {
if (this.props.db_target_type !== this.props.db_source_type) {
db.migrate_async(this.props.db_target_type, this.props.db_target_name)
.then(() => { this.setState({ loading: false, done: true, db: db }) });
} else { this.setState({ loading: false, done: true, db: db }) };
});
} else if (this.props.db_source_type === DBTypeEnum.ALASQL_NATIVE && this.props.db_source === DBSourceEnum.CREATE) {
create_native_db(this.props.db_source_name)
.then(db => {
if (this.props.db_target_type !== this.props.db_source_type) {
db.migrate_async(this.props.db_target_type, this.props.db_target_name)
.then(() => { this.setState({ loading: false, done: true, db: db }) });
} else { this.setState({ loading: false, done: true, db: db }) };
});
} else {
throw new Error("Unsupported ProvideDB configuration: from source " + this.props.db_source +
" with source type " + this.props.db_source_type + " to target type " + this.props.db_target_type + ".");
}
} }
render() { render() {
@ -128,7 +199,7 @@ export class DBQueryBar extends React.Component {
render() { render() {
return ( return (
<form> <form>
<input type="text" onChange={ this.onChangeHandler }/> <input type="text" onChange={this.onChangeHandler} />
</form> </form>
); );
} }
@ -141,24 +212,24 @@ export class DBQueryConsole extends React.Component {
query: "", query: "",
} }
onQueryChangeHandler = query => { this.setState( { query: query } ); } onQueryChangeHandler = query => { this.setState({ query: query }); }
onQuerySubmitHandler = () => { onQuerySubmitHandler = () => {
this.setState( { processing: true, result: false } ); this.setState({ processing: true, result: false });
this.props.database.queries_async([this.state.query]) this.props.database.queries_async([this.state.query])
.then(result => { .then(result => {
this.setState( { processing: false, result: JSON.stringify(result) } ); this.setState({ processing: false, result: JSON.stringify(result) });
}); });
} }
render() { render() {
return ( return (
<> <>
<div> <div>
<DBQueryBar onChange={this.onQueryChangeHandler} /> <DBQueryBar onChange={this.onQueryChangeHandler} />
<button onClick={this.onQuerySubmitHandler}>Submit</button> <button onClick={this.onQuerySubmitHandler}>Submit</button>
<p>Result:</p> <p>Result:</p>
{ this.state.result && <p>{this.state.result}</p> } {this.state.result && <p>{this.state.result}</p>}
</div> </div>
</> </>
); );
} }

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Fetch } from './fetch.js'; import { Fetch } from './fetch.js';
import { FetchSQLiteDB, DBQueryConsole } from './database.js'; import { ProvideDB, DBQueryConsole, DBTypeEnum, DBSourceEnum } from './database.js';
import { Photo, PhotoView } from './media.js'; import { Photo, PhotoView } from './media.js';
import './index.css'; import './index.css';
@ -33,7 +33,7 @@ export class PhotoFromDB extends React.Component {
} }
componentDidMount() { componentDidMount() {
this.props.database.queries_async(["SELECT * FROM images;"]) this.props.database.queries_async(["SELECT * FROM Images;"])
.then(res => { .then(res => {
var photo = new Photo(res[0]); var photo = new Photo(res[0]);
this.setState({ done: true, photo: photo }); this.setState({ done: true, photo: photo });
@ -50,7 +50,8 @@ export class PhotoFromDB extends React.Component {
} }
const TestDBFetch = ({sqlite_file}) => ( const TestDBFetch = ({sqlite_file}) => (
<FetchSQLiteDB sqlite_file={sqlite_file} migrate_to_indexeddb={true}> <ProvideDB db_file={sqlite_file} db_source_type={DBTypeEnum.ALASQL_SQLITE} db_target_type={DBTypeEnum.ALASQL_NATIVE}
db_source_name="sqlite_db" db_target_name="db" db_source={DBSourceEnum.ATTACHFILE}>
{({ loading, error, done, db }) => ( {({ loading, error, done, db }) => (
<> <>
{ loading && <DBLoading sqlite_file={sqlite_file}/> } { loading && <DBLoading sqlite_file={sqlite_file}/> }
@ -60,15 +61,29 @@ const TestDBFetch = ({sqlite_file}) => (
{ done && <PhotoFromDB database={db}/> } { done && <PhotoFromDB database={db}/> }
</> </>
)} )}
</FetchSQLiteDB> </ProvideDB>
)
const TestDBPlayground = ({db_name}) => (
<ProvideDB db_source_type={DBTypeEnum.ALASQL_INDEXEDDB} db_target_type={DBTypeEnum.ALASQL_INDEXEDDB}
db_source_name={db_name} db_target_name={db_name} db_source={DBSourceEnum.CREATE}>
{({ loading, error, done, db }) => (
<>
{ error && <DBError error={error} />}
{ done && <DBQueryConsole database={db}/> }
</>
)}
</ProvideDB>
) )
ReactDOM.render( ReactDOM.render(
<> <>
<h1>Test file fetching:</h1> <h1>Test file fetching:</h1>
<TestFetch url={process.env.PUBLIC_URL + "/stuff"} /> <TestFetch url={process.env.PUBLIC_URL + "/stuff"} />
<h1>IndexedDB playground:</h1>
<TestDBPlayground db_name="playground_db"/>
<h1>Test DB fetching:</h1> <h1>Test DB fetching:</h1>
<TestDBFetch sqlite_file={process.env.PUBLIC_URL + "/test.sqlite"} /> <TestDBFetch sqlite_file={process.env.PUBLIC_URL + "/digikam4.db"} />
</>, </>,
document.getElementById('root') document.getElementById('root')
); );

Loading…
Cancel
Save