feat(Firebird SQL): display table content and query results

This commit is contained in:
Fabio Di Stasio 2022-11-04 16:31:10 +01:00
parent 7ab84bde57
commit 95bb41e9db
11 changed files with 344 additions and 148 deletions

View File

@ -5,6 +5,7 @@
"MySQL", "MySQL",
"PostgreSQL", "PostgreSQL",
"SQLite", "SQLite",
"Firebird SQL",
"Windows", "Windows",
"translation", "translation",
"Linux", "Linux",

View File

@ -20,7 +20,7 @@ export const customizations: Customizations = {
variables: false, variables: false,
// Structure // Structure
schemas: false, schemas: false,
tables: false, tables: true,
views: false, views: false,
triggers: false, triggers: false,
triggerFunctions: false, triggerFunctions: false,
@ -29,7 +29,7 @@ export const customizations: Customizations = {
schedulers: false, schedulers: false,
// Settings // Settings
elementsWrapper: '', elementsWrapper: '',
stringsWrapper: '"', stringsWrapper: '\'',
tableAdd: false, tableAdd: false,
tableTruncateDisableFKCheck: false, tableTruncateDisableFKCheck: false,
viewAdd: false, viewAdd: false,

View File

@ -0,0 +1,120 @@
import { TypesGroup } from 'common/interfaces/antares';
export default [
{
group: 'integer',
types: [
{
name: 'SMALLINT',
length: true,
collation: false,
unsigned: true,
zerofill: true
},
{
name: 'INTEGER',
length: true,
collation: false,
unsigned: true,
zerofill: true
},
{
name: 'BIGINT',
length: true,
collation: false,
unsigned: true,
zerofill: true
}
]
},
{
group: 'float',
types: [
{
name: 'DECIMAL',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'NUMERIC',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'FLOAT',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'DOUBLE PRECISION',
length: true,
collation: false,
unsigned: false,
zerofill: false
}
]
},
{
group: 'string',
types: [
{
name: 'CHAR',
length: true,
collation: true,
unsigned: false,
zerofill: false
},
{
name: 'VARCHAR',
length: true,
collation: true,
unsigned: false,
zerofill: false
}
]
},
{
group: 'binary',
types: [
{
name: 'BLOB',
length: true,
collation: false,
unsigned: false,
zerofill: false
}
]
},
{
group: 'time',
types: [
{
name: 'DATE',
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'TIME',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'TIMESTAMP',
length: true,
collation: false,
unsigned: false,
zerofill: false
}
]
}
] as TypesGroup[];

View File

@ -0,0 +1,5 @@
export default [
'PRIMARY',
'INDEX',
'UNIQUE'
];

View File

@ -97,7 +97,7 @@ export default (connections: {[key: string]: antares.Client}) => {
ipcMain.handle('get-engines', async (event, uid) => { ipcMain.handle('get-engines', async (event, uid) => {
try { try {
const result = await connections[uid].getEngines(); const result: any = await connections[uid].getEngines();
return { status: 'success', response: result }; return { status: 'success', response: result };
} }

View File

@ -1,9 +1,9 @@
import * as path from 'path';
import * as antares from 'common/interfaces/antares'; import * as antares from 'common/interfaces/antares';
import * as firebird from 'node-firebird'; import * as firebird from 'node-firebird';
import { AntaresCore } from '../AntaresCore'; import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/sqlite'; import dataTypes from 'common/data-types/sqlite';
import { NUMBER, FLOAT, TIME, DATETIME } from 'common/fieldTypes'; import { NUMBER, FLOAT, TIME, DATETIME } from 'common/fieldTypes';
import { promisify } from 'util';
export class FirebirdSQLClient extends AntaresCore { export class FirebirdSQLClient extends AntaresCore {
private _schema?: string; private _schema?: string;
@ -11,6 +11,25 @@ export class FirebirdSQLClient extends AntaresCore {
protected _connection?: firebird.Database; protected _connection?: firebird.Database;
_params: firebird.Options; _params: firebird.Options;
private types: {[key: number]: string} ={
452: 'CHAR', // Array of char
448: 'VARCHAR',
500: 'SMALLINT',
496: 'INTEGER',
482: 'FLOAT',
480: 'DOUBLE',
530: 'DOUBLE PRECISION',
510: 'TIMESTAMP',
520: 'BLOB',
540: 'ARRAY',
550: 'QUAD',
560: 'TIME',
570: 'DATE',
580: 'BIGINT',
32764: 'BOOLEAN', // >= 3.0
32766: 'NULL' // >= 2.5
}
constructor (args: antares.ClientParams) { constructor (args: antares.ClientParams) {
super(args); super(args);
@ -46,141 +65,151 @@ export class FirebirdSQLClient extends AntaresCore {
} }
async getStructure (schemas: Set<string>) { async getStructure (schemas: Set<string>) {
/* eslint-disable camelcase */
interface ShowTableResult { interface ShowTableResult {
Db?: string; FORMAT: number;
type: string; NAME: string;
name: string; TYPE: string;
tbl_name: string; DESCRIPTION: string | null;
rootpage:4;
sql: string;
} }
type ShowTriggersResult = ShowTableResult // type ShowTriggersResult = ShowTableResult
/* eslint-enable camelcase */
const { rows: databases } = await this.raw<antares.QueryResult<{ name: string}>>('SELECT * FROM pragma_database_list'); const { rows: databases } = await this.raw<antares.QueryResult<{ NAME: string}>>('SELECT rdb$get_context(\'SYSTEM\', \'DB_NAME\') as name FROM rdb$database');
const filteredDatabases = databases; const filteredDatabases = databases.map(db => {
return { name: path.basename(db.NAME) };
});
const tablesArr: ShowTableResult[] = []; const tablesArr: ShowTableResult[] = [];
const triggersArr: ShowTriggersResult[] = []; // const triggersArr: ShowTriggersResult[] = [];
let schemaSize = 0; let schemaSize = 0;
for (const db of filteredDatabases) { for (const db of filteredDatabases) {
if (!schemas.has(db.name)) continue; // if (!schemas.has(db.name)) continue;
let { rows: tables } = await this.raw<antares.QueryResult<ShowTableResult>>(` const { rows: tables } = await this.raw<antares.QueryResult<ShowTableResult>>(`
SELECT * SELECT
FROM "${db.name}".sqlite_master rdb$relation_name AS name,
WHERE type IN ('table', 'view') rdb$format AS format,
AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' rdb$description AS description,
ORDER BY name 'table' AS type
FROM RDB$RELATIONS a
WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0
AND RDB$RELATION_TYPE = 0
`); `);
if (tables.length) {
tables = tables.map(table => {
table.Db = db.name;
return table;
});
tablesArr.push(...tables);
}
let { rows: triggers } = await this.raw<antares.QueryResult<ShowTriggersResult>>(`SELECT * FROM "${db.name}".sqlite_master WHERE type='trigger'`); tablesArr.push(...tables);
if (triggers.length) {
triggers = triggers.map(trigger => {
trigger.Db = db.name;
return trigger;
});
triggersArr.push(...triggers);
}
} }
return filteredDatabases.map(db => { return filteredDatabases.map(db => {
if (schemas.has(db.name)) { // TABLES
// TABLES const remappedTables = tablesArr.map(table => {
const remappedTables = tablesArr.filter(table => table.Db === db.name).map(table => { const tableSize = 0;
const tableSize = 0; schemaSize += tableSize;
schemaSize += tableSize;
return {
name: table.name,
type: table.type,
rows: false,
size: false
};
});
// TRIGGERS
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.name).map(trigger => {
return {
name: trigger.name,
table: trigger.tbl_name
};
});
return { return {
name: db.name, name: table.NAME.trim(),
size: schemaSize, type: table.TYPE.trim(),
tables: remappedTables, rows: false,
functions: [], size: false
procedures: [],
triggers: remappedTriggers,
schedulers: []
}; };
} });
else {
return { // TRIGGERS
name: db.name, // const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.name).map(trigger => {
size: 0, // return {
tables: [], // name: trigger.name,
functions: [], // table: trigger.tbl_name
procedures: [], // };
triggers: [], // });
schedulers: []
}; return {
} name: db.name,
size: schemaSize,
tables: remappedTables,
functions: [],
procedures: [],
triggers: [],
schedulers: []
};
}); });
} }
async getTableColumns ({ schema, table }: { schema: string; table: string }) { async getTableColumns ({ schema, table }: { schema: string; table: string }) {
interface TableColumnsResult { interface TableColumnsResult {
cid: number; POSITION: number;
name: string; DESCRIPTION?: string;
type: string; /* eslint-disable camelcase */
notnull: 0 | 1; FIELD_NAME: string;
// eslint-disable-next-line camelcase FIELD_TYPE: string;
dflt_value: string; NOT_NULL: 0 | 1;
pk: 0 | 1; DEFAULT_VALUE: string;
FIELD_LENGTH: number;
FIELD_PRECISION: number;
FIELD_SCALE: number;
/* eslint-enable camelcase */
SUBTYPE: string;
COLLATION: string;
CHARSET: string;
} }
const { rows: fields } = await this.raw<antares.QueryResult<TableColumnsResult>>(`SELECT * FROM "${schema}".pragma_table_info('${table}')`);
const { rows: fields } = await this.raw<antares.QueryResult<TableColumnsResult>>(`
SELECT
r.RDB$FIELD_NAME AS field_name,
r.RDB$DESCRIPTION AS description,
r.RDB$DEFAULT_VALUE AS default_value,
r.RDB$NULL_FLAG AS not_null,
f.RDB$FIELD_LENGTH AS field_length,
f.RDB$FIELD_PRECISION AS field_precision,
f.RDB$FIELD_SCALE AS field_scale,
CASE f.RDB$FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
WHEN 27 THEN 'DOUBLE'
WHEN 10 THEN 'FLOAT'
WHEN 16 THEN 'INT64'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
END AS field_type,
f.RDB$FIELD_SUB_TYPE AS subtype,
-- coll.RDB$COLLATION_NAME AS collation,
cset.RDB$CHARACTER_SET_NAME AS charset
FROM RDB$RELATION_FIELDS r
LEFT JOIN RDB$FIELDS f ON r.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME
-- LEFT JOIN RDB$COLLATIONS coll ON f.RDB$COLLATION_ID = coll.RDB$COLLATION_ID
LEFT JOIN RDB$CHARACTER_SETS cset ON f.RDB$CHARACTER_SET_ID = cset.RDB$CHARACTER_SET_ID
WHERE r.RDB$RELATION_NAME='${table}'
ORDER BY r.RDB$FIELD_POSITION;
`);
return fields.map(field => { return fields.map(field => {
const [type, length]: [string, number?] = field.type.includes('(')
? field.type.replace(')', '').split('(').map((el: string | number) => {
if (!isNaN(Number(el))) el = Number(el);
return el;
}) as [string, number?]
: [field.type, null];
return { return {
name: field.name, name: field.FIELD_NAME.trim(),
key: null, key: null,
type: type.trim(), type: field.FIELD_TYPE.trim(),
schema: schema, schema: schema,
table: table, table: table,
numPrecision: [...NUMBER, ...FLOAT].includes(type) ? length : null, numPrecision: field.FIELD_PRECISION,
datePrecision: null, datePrecision: null,
charLength: ![...NUMBER, ...FLOAT].includes(type) ? length : null, charLength: field.FIELD_LENGTH,
nullable: !field.notnull, nullable: !field.NOT_NULL,
unsigned: null, unsigned: null,
zerofill: null, zerofill: null,
order: typeof field.cid === 'string' ? +field.cid + 1 : field.cid + 1, order: field.POSITION,
default: field.dflt_value, default: field.DEFAULT_VALUE,
charset: null, charset: field.CHARSET,
collation: null, collation: null,
autoIncrement: false, autoIncrement: false,
onUpdate: null, onUpdate: null,
comment: '' comment: field.DESCRIPTION?.trim()
}; };
}); });
} }
@ -393,17 +422,17 @@ export class FirebirdSQLClient extends AntaresCore {
} }
async duplicateTable (params: { schema: string; table: string }) { // TODO: retrive table informations and create a copy async duplicateTable (params: { schema: string; table: string }) { // TODO: retrive table informations and create a copy
const sql = `CREATE TABLE "${params.schema}"."${params.table}_copy" AS SELECT * FROM "${params.schema}"."${params.table}"`; const sql = `CREATE TABLE '${params.table}_copy' AS SELECT * FROM '${params.table}'`;
return await this.raw(sql); return await this.raw(sql);
} }
async truncateTable (params: { schema: string; table: string }) { async truncateTable (params: { schema: string; table: string }) {
const sql = `DELETE FROM "${params.schema}"."${params.table}"`; const sql = `DELETE FROM '${params.table}'`;
return await this.raw(sql); return await this.raw(sql);
} }
async dropTable (params: { schema: string; table: string }) { async dropTable (params: { schema: string; table: string }) {
const sql = `DROP TABLE "${params.schema}"."${params.table}"`; const sql = `DROP TABLE '${params.table}'`;
return await this.raw(sql); return await this.raw(sql);
} }
@ -420,7 +449,7 @@ export class FirebirdSQLClient extends AntaresCore {
} }
async dropView (params: { schema: string; view: string }) { async dropView (params: { schema: string; view: string }) {
const sql = `DROP VIEW "${params.schema}"."${params.view}"`; const sql = `DROP VIEW '${params.view}'`;
return await this.raw(sql); return await this.raw(sql);
} }
@ -435,7 +464,7 @@ export class FirebirdSQLClient extends AntaresCore {
} }
async createView (params: antares.CreateViewParams) { async createView (params: antares.CreateViewParams) {
const sql = `CREATE VIEW "${params.schema}"."${params.name}" AS ${params.sql}`; const sql = `CREATE VIEW '${params.name}' AS ${params.sql}`;
return await this.raw(sql); return await this.raw(sql);
} }
@ -489,15 +518,19 @@ export class FirebirdSQLClient extends AntaresCore {
} }
async getVersion () { async getVersion () {
const os = require('os'); const sql = `
const sql = 'SELECT sqlite_version() AS version'; SELECT
rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version,
rdb$get_context('SYSTEM', 'NETWORK_PROTOCOL') as protocol,
RDB$GET_CONTEXT('SYSTEM', 'CLIENT_ADDRESS') AS address
FROM rdb$database`;
const { rows } = await this.raw(sql); const { rows } = await this.raw(sql);
return { return {
number: rows[0].version, number: rows[0].version,
name: 'SQLite', name: 'Firebird SQL',
arch: process.arch, arch: rows[0].protocol,
os: `${os.type()} ${os.release()}` os: rows[0].address
}; };
} }
@ -509,37 +542,40 @@ export class FirebirdSQLClient extends AntaresCore {
return null; return null;
} }
async commitTab (tabUid: string) { // async commitTab (tabUid: string) {
const connection = this._connectionsToCommit.get(tabUid); // const connection = this._connectionsToCommit.get(tabUid);
if (connection) { // if (connection) {
connection.prepare('COMMIT').run(); // connection.prepare('COMMIT').run();
return this.destroyConnectionToCommit(tabUid); // return this.destroyConnectionToCommit(tabUid);
} // }
} // }
async rollbackTab (tabUid: string) { // async rollbackTab (tabUid: string) {
const connection = this._connectionsToCommit.get(tabUid); // const connection = this._connectionsToCommit.get(tabUid);
if (connection) { // if (connection) {
connection.prepare('ROLLBACK').run(); // connection.prepare('ROLLBACK').run();
return this.destroyConnectionToCommit(tabUid); // return this.destroyConnectionToCommit(tabUid);
} // }
} // }
destroyConnectionToCommit (tabUid: string) { // destroyConnectionToCommit (tabUid: string) {
const connection = this._connectionsToCommit.get(tabUid); // const connection = this._connectionsToCommit.get(tabUid);
if (connection) { // if (connection) {
connection.close(); // connection.close();
this._connectionsToCommit.delete(tabUid); // this._connectionsToCommit.delete(tabUid);
} // }
} // }
getSQL () { getSQL () {
// LIMIT
const limitRaw = this._query.limit ? ` first ${this._query.limit}` : '';
// SELECT // SELECT
const selectArray = this._query.select.reduce(this._reducer, []); const selectArray = this._query.select.reduce(this._reducer, []);
let selectRaw = ''; let selectRaw = '';
if (selectArray.length) if (selectArray.length)
selectRaw = selectArray.length ? `SELECT ${selectArray.join(', ')} ` : 'SELECT * '; selectRaw = selectArray.length ? `SELECT${limitRaw||''} ${selectArray.join(', ')} ` : `SELECT${limitRaw||''} * `;
// FROM // FROM
let fromRaw = ''; let fromRaw = '';
@ -549,7 +585,7 @@ export class FirebirdSQLClient extends AntaresCore {
else if (Object.keys(this._query.insert).length) else if (Object.keys(this._query.insert).length)
fromRaw = 'INTO'; fromRaw = 'INTO';
fromRaw += this._query.from ? ` ${this._query.schema ? `"${this._query.schema}".` : ''}"${this._query.from}" ` : ''; fromRaw += this._query.from ? ` ${this._query.from} ` : '';
// WHERE // WHERE
const whereArray = this._query.where const whereArray = this._query.where
@ -579,16 +615,24 @@ export class FirebirdSQLClient extends AntaresCore {
const orderByArray = this._query.orderBy.reduce(this._reducer, []); const orderByArray = this._query.orderBy.reduce(this._reducer, []);
const orderByRaw = orderByArray.length ? `ORDER BY ${orderByArray.join(', ')} ` : ''; const orderByRaw = orderByArray.length ? `ORDER BY ${orderByArray.join(', ')} ` : '';
// LIMIT
const limitRaw = this._query.limit ? `LIMIT ${this._query.limit} ` : '';
// OFFSET // OFFSET
const offsetRaw = this._query.offset ? `OFFSET ${this._query.offset} ` : ''; const offsetRaw = this._query.offset ? `OFFSET ${this._query.offset} ` : '';
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`; return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${offsetRaw}${insertRaw}`;
} }
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) { async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
interface FieldData {
type: number;
nullable: boolean;
subType: number;
scale: number;
length: number;
field: string;
relation: string;
alias: string;
}
this._logger({ cUid: this._cUid, sql }); this._logger({ cUid: this._cUid, sql });
args = { args = {
@ -604,7 +648,6 @@ export class FirebirdSQLClient extends AntaresCore {
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const resultsArr = []; const resultsArr = [];
const paramsArr = [];
const queries = args.split const queries = args.split
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm) ? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm)
.filter(Boolean) .filter(Boolean)
@ -641,16 +684,23 @@ export class FirebirdSQLClient extends AntaresCore {
const { rows, report, fields, keys, duration }: any = await new Promise((resolve, reject) => { const { rows, report, fields, keys, duration }: any = await new Promise((resolve, reject) => {
(async () => { (async () => {
let queryResult; let queryResult;
let remappedFields;
try { try {
queryResult = await new Promise<unknown[]>((resolve, reject) => { queryResult = await new Promise<unknown[]>((resolve, reject) => {
connection.query(query, [], (err, res) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any
(connection as any).query(query, [], (err: any, res: any, fields: FieldData[]) => { // <- fields is not natively typed or documented
if (err) reject(err); if (err) reject(err);
else { else {
const remappedResponse = []; const remappedResponse = [];
for (const row of res) { for (const row of res) {
for (const key in row) { for (const key in row) {
const fieldData = fields.find(({ alias }) => alias === key);
if (fieldData.type === 520 && fieldData.subType === 1)// TODO: handle BLOB subType 1
row[key] = row[key]?.toString();
if (Buffer.isBuffer(row[key])) if (Buffer.isBuffer(row[key]))
row[key] = row[key].toString('binary'); row[key] = row[key].toString('binary');
} }
@ -658,6 +708,21 @@ export class FirebirdSQLClient extends AntaresCore {
remappedResponse.push(row); remappedResponse.push(row);
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
remappedFields = fields.map((field: any) => {
return {
name: field.alias,
alias: field.alias,
orgName: field.field,
schema: args.schema,
table: field.relation,
tableAlias: field.relation,
orgTable: field.relation,
type: this.types[field.type],
length: field.length
};
});
resolve(remappedResponse); resolve(remappedResponse);
} }
}); });
@ -669,8 +734,6 @@ export class FirebirdSQLClient extends AntaresCore {
timeStop = new Date(); timeStop = new Date();
const remappedFields = [];
// if (args.details) { // if (args.details) {
// } // }

View File

@ -123,8 +123,8 @@ else {
if (isWindows) if (isWindows)
mainWindow.show(); mainWindow.show();
if (isDevelopment) // if (isDevelopment)
mainWindow.webContents.openDevTools(); // mainWindow.webContents.openDevTools();
process.on('uncaughtException', error => { process.on('uncaughtException', error => {
mainWindow.webContents.send('unhandled-exception', error); mainWindow.webContents.send('unhandled-exception', error);

View File

@ -240,7 +240,9 @@ watch(() => tablesInQuery.value.length, () => {
}); });
fields.value = localFields; fields.value = localFields;
setCustomCompleter(); setTimeout(() => {
setCustomCompleter();
}, 100);
}); });
watch(editorTheme, () => { watch(editorTheme, () => {

View File

@ -416,7 +416,7 @@ const clients = [
{ name: 'MariaDB', slug: 'maria' }, { name: 'MariaDB', slug: 'maria' },
{ name: 'PostgreSQL', slug: 'pg' }, { name: 'PostgreSQL', slug: 'pg' },
{ name: 'SQLite', slug: 'sqlite' }, { name: 'SQLite', slug: 'sqlite' },
{ name: 'FirebirdSQL', slug: 'firebird' } { name: 'Firebird SQL (experimental)', slug: 'firebird' }
]; ];
const connection = ref({ const connection = ref({

View File

@ -429,7 +429,7 @@ const clients = [
{ name: 'MariaDB', slug: 'maria' }, { name: 'MariaDB', slug: 'maria' },
{ name: 'PostgreSQL', slug: 'pg' }, { name: 'PostgreSQL', slug: 'pg' },
{ name: 'SQLite', slug: 'sqlite' }, { name: 'SQLite', slug: 'sqlite' },
{ name: 'FirebirdSQL', slug: 'firebird' } { name: 'Firebird SQL (experimental)', slug: 'firebird' }
]; ];
const firstInput: Ref<HTMLInputElement> = ref(null); const firstInput: Ref<HTMLInputElement> = ref(null);

View File

@ -200,6 +200,11 @@ export const useWorkspacesStore = defineStore('workspaces', {
indexTypes = require('common/index-types/sqlite').default; indexTypes = require('common/index-types/sqlite').default;
clientCustomizations = customizations.sqlite; clientCustomizations = customizations.sqlite;
break; break;
case 'firebird':
dataTypes = require('common/data-types/firebird').default;
indexTypes = require('common/index-types/firebird').default;
clientCustomizations = customizations.firebird;
break;
} }
const { status, response: version } = await Schema.getVersion(connection.uid); const { status, response: version } = await Schema.getVersion(connection.uid);