1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-06 18:18:53 +08:00

Updating how database object information is getting retrieved (#79)

* Updating how database object information is getting retrieved

I updated the SQLite query for gathering database objects to make use of the JSON1 extension so you can grab tables and views name, their associated columns with types and set it to the schema. This removes the need to work with DDL's. Hints for Tables and Views works since my approach is they are both database objects.
This commit is contained in:
twoxfh
2021-08-18 14:22:30 -04:00
committed by GitHub
parent f3e8448851
commit 53b2d8372f
5 changed files with 11 additions and 96 deletions

View File

@@ -1,5 +1,3 @@
import sqliteParser from 'sqlite-parser'
export default { export default {
* generateChunks (data, size) { * generateChunks (data, size) {
const matrix = Object.keys(data).map(col => data[col]) const matrix = Object.keys(data).map(col => data[col])
@@ -47,49 +45,5 @@ export default {
result = result.replace(/,\s$/, ');') result = result.replace(/,\s$/, ');')
return result return result
},
getAst (sql) {
// There is a bug is sqlite-parser
// It throws an error if tokenizer has an arguments:
// https://github.com/codeschool/sqlite-parser/issues/59
const fixedSql = sql
.replace(/(tokenize=[^,]+)"tokenchars=.+?"/, '$1')
.replace(/(tokenize=[^,]+)"remove_diacritics=.+?"/, '$1')
.replace(/(tokenize=[^,]+)"separators=.+?"/, '$1')
.replace(/tokenize=.+?(,|\))/, 'tokenize=unicode61$1')
return sqliteParser(fixedSql)
},
/*
* Return an array of columns with name and type. E.g.:
* [
* { name: 'id', type: 'INTEGER' },
* { name: 'title', type: 'NVARCHAR(30)' },
* ]
*/
getColumns (sql) {
const columns = []
const ast = this.getAst(sql)
const columnDefinition = ast.statement[0].format === 'table'
? ast.statement[0].definition
: ast.statement[0].result.args.expression // virtual table
columnDefinition.forEach(item => {
if (item.variant === 'column' && ['identifier', 'definition'].includes(item.type)) {
let type = item.datatype ? item.datatype.variant : 'N/A'
if (item.datatype && item.datatype.args) {
type = type + '(' + item.datatype.args.expression[0].value
if (item.datatype.args.expression.length === 2) {
type = type + ', ' + item.datatype.args.expression[1].value
}
type = type + ')'
}
columns.push({ name: item.name, type: type })
}
})
return columns
} }
} }

View File

@@ -1,4 +1,3 @@
import stms from './_statements'
import fu from '@/lib/utils/fileIo' import fu from '@/lib/utils/fileIo'
// We can import workers like so because of worker-loader: // We can import workers like so because of worker-loader:
// https://webpack.js.org/loaders/worker-loader/ // https://webpack.js.org/loaders/worker-loader/
@@ -81,24 +80,14 @@ class Database {
async refreshSchema () { async refreshSchema () {
const getSchemaSql = ` const getSchemaSql = `
SELECT name, sql WITH columns as (SELECT a.tbl_name, json_group_array(json_object('name', b.name,'type', IIF(b.type = '', 'N/A', b.type))) as column_json
FROM sqlite_master FROM sqlite_master a, pragma_table_info(a.name) b
WHERE type='table' AND name NOT LIKE 'sqlite_%'; WHERE a.type in ('table','view') AND a.name NOT LIKE 'sqlite_%' group by tbl_name
)
SELECT json_group_array(json_object('name',tbl_name, 'columns', json(column_json))) objects from columns;
` `
const result = await this.execute(getSchemaSql) const result = await this.execute(getSchemaSql)
// Parse DDL statements to get column names and types this.schema = JSON.parse(result.values.objects[0])
const parsedSchema = []
if (result && result.values && result.values.name) {
result.values.name.forEach((table, index) => {
parsedSchema.push({
name: table,
columns: stms.getColumns(result.values.sql[index])
})
})
}
// Refresh schema
this.schema = parsedSchema
} }
async execute (commands) { async execute (commands) {

View File

@@ -35,32 +35,4 @@ describe('_statements.js', () => {
'CREATE table "foo"("id" REAL, "name" TEXT, "isAdmin" INTEGER, "startDate" TEXT);' 'CREATE table "foo"("id" REAL, "name" TEXT, "isAdmin" INTEGER, "startDate" TEXT);'
) )
}) })
it('getColumns', () => {
const sql = `CREATE TABLE test (
col1,
col2 integer,
col3 decimal(5,2),
col4 varchar(30)
)`
expect(stmts.getColumns(sql)).to.eql([
{ name: 'col1', type: 'N/A' },
{ name: 'col2', type: 'integer' },
{ name: 'col3', type: 'decimal(5, 2)' },
{ name: 'col4', type: 'varchar(30)' }
])
})
it('getColumns with virtual table', async () => {
const sql = `
CREATE VIRTUAL TABLE test_virtual USING fts4(
col1, col2,
notindexed=col1, notindexed=col2,
tokenize=unicode61 "tokenchars=.+#")
`
expect(stmts.getColumns(sql)).to.eql([
{ name: 'col1', type: 'N/A' },
{ name: 'col2', type: 'N/A' }
])
})
}) })

View File

@@ -133,9 +133,9 @@ describe('database.js', () => {
expect(db.schema).to.have.lengthOf(1) expect(db.schema).to.have.lengthOf(1)
expect(db.schema[0].name).to.equal('foo') expect(db.schema[0].name).to.equal('foo')
expect(db.schema[0].columns).to.have.lengthOf(3) expect(db.schema[0].columns).to.have.lengthOf(3)
expect(db.schema[0].columns[0]).to.eql({ name: 'id', type: 'real' }) expect(db.schema[0].columns[0]).to.eql({ name: 'id', type: 'REAL' })
expect(db.schema[0].columns[1]).to.eql({ name: 'name', type: 'text' }) expect(db.schema[0].columns[1]).to.eql({ name: 'name', type: 'TEXT' })
expect(db.schema[0].columns[2]).to.eql({ name: 'faculty', type: 'text' }) expect(db.schema[0].columns[2]).to.eql({ name: 'faculty', type: 'TEXT' })
const result = await db.execute('SELECT * from foo') const result = await db.execute('SELECT * from foo')
expect(result).to.eql(data) expect(result).to.eql(data)

View File

@@ -166,8 +166,8 @@ describe('Schema.vue', () => {
await state.db.refreshSchema.returnValues[0] await state.db.refreshSchema.returnValues[0]
expect(wrapper.vm.$store.state.db.schema).to.eql([ expect(wrapper.vm.$store.state.db.schema).to.eql([
{ name: 'test', columns: [{ name: 'col1', type: 'real' }, { name: 'col2', type: 'text' }] }, { name: 'foo', columns: [{ name: 'id', type: 'N/A' }] },
{ name: 'foo', columns: [{ name: 'id', type: 'N/A' }] } { name: 'test', columns: [{ name: 'col1', type: 'REAL' }, { name: 'col2', type: 'TEXT' }] }
]) ])
const res = await wrapper.vm.$store.state.db.execute('select * from test') const res = await wrapper.vm.$store.state.db.execute('select * from test')