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';
var DBTypeEnum = {
export const DBTypeEnum = {
ALASQL_SQLITE: 1,
ALASQL_INDEXEDDB: 2 // TODO: implement with migration
ALASQL_INDEXEDDB: 2,
ALASQL_NATIVE: 3
};
export class DB {
@ -22,44 +23,65 @@ export class DB {
// Perform a chain of asynchronous queries, each one waiting for the last before
// starting. Not supported on all database types.
queries_async(s) {
if(this.state.db_type === DBTypeEnum.ALASQL_SQLITE ||
this.state.db_type === DBTypeEnum.ALASQL_INDEXEDDB) {
var self = this;
var p = Promise.resolve(null);
for(let i = 0; i < s.length; i++) {
p = p.then(() => { return self.state.db_object.promise(s[i]); });
}
return p;
if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE ||
this.state.db_type === DBTypeEnum.ALASQL_INDEXEDDB ||
this.state.db_type === DBTypeEnum.ALASQL_NATIVE) {
var self = this;
var p = Promise.resolve(null);
for (let i = 0; i < s.length; i++) {
p = p.then(() => { return self.state.db_object.promise(s[i]); });
}
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.
// Note that synchronous operation is not supported for all database types.
queries_sync(s) {
if(this.state.db_type === DBTypeEnum.ALASQL_SQLITE) {
for (var i = 0; i < (s.length-1); i++) {
if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE ||
this.state.db_type === DBTypeEnum.ALASQL_NATIVE) {
for (var i = 0; i < (s.length - 1); 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);
}
migrate_async(db_type, db_name) {
var self = this;
if(this.state.db_type === DBTypeEnum.ALASQL_SQLITE && db_type === DBTypeEnum.ALASQL_INDEXEDDB) {
return new Promise(function(resolve, reject) {
if (this.state.db_type === DBTypeEnum.ALASQL_SQLITE && db_type === DBTypeEnum.ALASQL_INDEXEDDB) {
return new Promise(function (resolve, reject) {
var tables = self.queries_sync(["SHOW TABLES;"]);
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("ATTACH INDEXEDDB DATABASE " + 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 + ";");
" 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("USE " + db_name + ";");
@ -75,28 +97,56 @@ export class DB {
}
// TODO random database names
function fetch_db_from_sqlite(filename) {
return new Promise(function(resolve, reject) {
function fetch_db_from_sqlite(filename, db_name) {
return new Promise(function (resolve, reject) {
var initSqlJs = require('sql.js');
initSqlJs({ locateFile: filename => `/sql.js/dist/${filename}` })
.then( SQL => {
var asql = require('alasql');
// Attach to the SQLite database file,
// Create an IndexedDB to migrate it to, named "db",
// Get tables
var query = "";
query += "ATTACH SQLITE DATABASE db(\"" + filename + "\"); USE db;";
asql.promise(query)
.then(SQL => {
var asql = require('alasql');
asql.promise("ATTACH SQLITE DATABASE " + db_name + "(\"" + filename + "\");")
.then(res => { asql.promise("USE " + db_name + ";"); })
.then(res => {
var imported_db = new DB(asql, db_name, DBTypeEnum.ALASQL_SQLITE);
resolve(imported_db);
});
});
});
}
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 => {
var imported_db = new DB(asql, "db", DBTypeEnum.ALASQL_SQLITE);
resolve(imported_db);
var created_db = new DB(asql, name, DBTypeEnum.ALASQL_INDEXEDDB);
resolve(created_db);
});
});
});
}
// TODO: generalize to FetchDB
export class FetchSQLiteDB extends React.Component {
function create_native_db(name) {
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 = {
loading: true,
error: false,
@ -105,14 +155,35 @@ export class FetchSQLiteDB extends React.Component {
};
componentDidMount() {
fetch_db_from_sqlite(this.props.sqlite_file)
.then(db => {
if(this.props.migrate_to_indexeddb) {
db.migrate_async(DBTypeEnum.ALASQL_INDEXEDDB, "indexed_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 }));
if (this.props.db_source_type === DBTypeEnum.ALASQL_SQLITE && this.props.db_source === DBSourceEnum.ATTACHFILE) {
fetch_db_from_sqlite(this.props.db_file, 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 }) };
})
.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() {
@ -128,7 +199,7 @@ export class DBQueryBar extends React.Component {
render() {
return (
<form>
<input type="text" onChange={ this.onChangeHandler }/>
<input type="text" onChange={this.onChangeHandler} />
</form>
);
}
@ -141,24 +212,24 @@ export class DBQueryConsole extends React.Component {
query: "",
}
onQueryChangeHandler = query => { this.setState( { query: query } ); }
onQueryChangeHandler = query => { this.setState({ query: query }); }
onQuerySubmitHandler = () => {
this.setState( { processing: true, result: false } );
this.setState({ processing: true, result: false });
this.props.database.queries_async([this.state.query])
.then(result => {
this.setState( { processing: false, result: JSON.stringify(result) } );
});
.then(result => {
this.setState({ processing: false, result: JSON.stringify(result) });
});
}
render() {
return (
<>
<div>
<DBQueryBar onChange={this.onQueryChangeHandler} />
<button onClick={this.onQuerySubmitHandler}>Submit</button>
<p>Result:</p>
{ this.state.result && <p>{this.state.result}</p> }
</div>
<div>
<DBQueryBar onChange={this.onQueryChangeHandler} />
<button onClick={this.onQuerySubmitHandler}>Submit</button>
<p>Result:</p>
{this.state.result && <p>{this.state.result}</p>}
</div>
</>
);
}

@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
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 './index.css';
@ -33,7 +33,7 @@ export class PhotoFromDB extends React.Component {
}
componentDidMount() {
this.props.database.queries_async(["SELECT * FROM images;"])
this.props.database.queries_async(["SELECT * FROM Images;"])
.then(res => {
var photo = new Photo(res[0]);
this.setState({ done: true, photo: photo });
@ -50,7 +50,8 @@ export class PhotoFromDB extends React.Component {
}
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 && <DBLoading sqlite_file={sqlite_file}/> }
@ -60,15 +61,29 @@ const TestDBFetch = ({sqlite_file}) => (
{ 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(
<>
<h1>Test file fetching:</h1>
<TestFetch url={process.env.PUBLIC_URL + "/stuff"} />
<h1>IndexedDB playground:</h1>
<TestDBPlayground db_name="playground_db"/>
<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')
);

Loading…
Cancel
Save