diff --git a/package-lock.json b/package-lock.json
index 767a399..b4f1c78 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@libsqlstudio/studio",
- "version": "0.6.4",
+ "version": "0.7.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@libsqlstudio/studio",
- "version": "0.6.4",
+ "version": "0.7.0",
"dependencies": {
"@aws-sdk/client-s3": "^3.540.0",
"@blocknote/core": "^0.12.1",
@@ -75,7 +75,6 @@
"react-resizable-panels": "^1.0.9",
"sonner": "^1.4.41",
"sql-formatter": "^15.3.2",
- "sql-query-identifier": "^2.6.0",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.22.4"
@@ -21931,14 +21930,6 @@
"sql-formatter": "bin/sql-formatter-cli.cjs"
}
},
- "node_modules/sql-query-identifier": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/sql-query-identifier/-/sql-query-identifier-2.7.0.tgz",
- "integrity": "sha512-MTDRfn0kUv+9ELnt9wt/FATi03Wq1j3YvhE5Up8ToE5Afk5zzBgVHPuIu1bVhqNqfF0aeIPl3vEzZ60H1yv0ag==",
- "engines": {
- "node": ">= 10.13"
- }
- },
"node_modules/sql.js": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.10.3.tgz",
diff --git a/package.json b/package.json
index fa5b8e6..bffbbac 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@libsqlstudio/studio",
- "version": "0.6.4",
+ "version": "0.7.0",
"private": false,
"scripts": {
"dev": "next dev -p 3008",
@@ -94,7 +94,6 @@
"react-resizable-panels": "^1.0.9",
"sonner": "^1.4.41",
"sql-formatter": "^15.3.2",
- "sql-query-identifier": "^2.6.0",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.22.4"
diff --git a/src/app/(public)/databases/mysql/page.tsx b/src/app/(public)/databases/mysql/page.tsx
new file mode 100644
index 0000000..ca900db
--- /dev/null
+++ b/src/app/(public)/databases/mysql/page.tsx
@@ -0,0 +1,141 @@
+import { MySQLIcon } from "@/components/icons/outerbase-icon";
+import WebsiteLayout from "@/components/website-layout";
+import { Metadata } from "next";
+
+const siteDescription =
+ "LibSQL Studio is a fully-featured, lightweight GUI client for managing MySQL databases";
+
+export const metadata: Metadata = {
+ title: "MySQL - LibSQL Studio",
+ keywords: ["mysql", "studio", "browser", "editor", "gui", "online", "client"],
+ description: siteDescription,
+ openGraph: {
+ siteName: "LibSQL Studio",
+ description: siteDescription,
+ },
+};
+
+function HeroSection() {
+ return (
+
+
+
+
+
+
+
+ MySQL Support
+
+
+ LibSQL Studio is a lightweight, fully-featured GUI client for MySQL
+ databases. It enables you to manage and view your database, or
+ expose your database interface externally and much more.
+
+
+
+
+ );
+}
+
+export default function DatabaseMySqlPage() {
+ return (
+
+
+
+
+
+ Connecting
+
+
+
+ You can connect to your MySQL database using our command line
+ interface.
+
+
+
+
+ > npx{" "}
+ @outerbase/studio
+
+ mysql://root:123@localhost:3306/chinook
+
+
+
+
{" "}
+
{" Serving! "}
+
{" - Local: http://localhost:4000 "}
+
{" - Network: http://xxx.xxx.xxx.xxx:4000 "}
+
{" "}
+
+
+
+
+ You can also configure the port and secure it with authentication.
+
+
+
+
+
+ > npx{" "}
+ @outerbase/studio \
+
+
--port=5000 \
+
--user=admin --pass=123 \
+
+
+ mysql://root:123@localhost:3306/chinook
+
+
+
+
+
+
+ Configuration File
+
+
+
+ Tired of typing long connection strings repeatedly? Simply save the
+ configuration to a file and use it whenever needed. Create{" "}
+ outerbase.json
+
+
+
+ {`{
+ "driver": "mysql",
+ "connection": {
+ "database": "chinook",
+ "host": "localhost",
+ "port": 3306,
+ "user": "root",
+ "password": "123456"
+ }
+}
+`}
+
+
+
+ Next, run our command line tool to start the service. By default, it
+ will search for the{" "}
+ outerbase.json{" "}
+ configuration file. Alternatively, you can specify a custom
+ configuration using the{" "}
+ --config flag.
+
+
+
+
+ > npx{" "}
+ @outerbase/studio
+
+
+
+
+ );
+}
diff --git a/src/app/(public)/page.tsx b/src/app/(public)/page.tsx
index 786fa2f..89d06e9 100644
--- a/src/app/(public)/page.tsx
+++ b/src/app/(public)/page.tsx
@@ -125,12 +125,9 @@ function SupportDriver() {
-
+
MySQL
-
- Coming Soon 15th Sep 2024
-
diff --git a/src/components/gui/sql-editor/index.tsx b/src/components/gui/sql-editor/index.tsx
index febd12e..bcd2340 100644
--- a/src/components/gui/sql-editor/index.tsx
+++ b/src/components/gui/sql-editor/index.tsx
@@ -1,13 +1,15 @@
import CodeMirror, {
EditorView,
+ Extension,
ReactCodeMirrorRef,
} from "@uiw/react-codemirror";
+import { LanguageSupport } from "@codemirror/language";
import {
acceptCompletion,
completionStatus,
startCompletion,
} from "@codemirror/autocomplete";
-import { sql, SQLNamespace } from "@codemirror/lang-sql";
+import { sql, SQLNamespace, MySQL as MySQLDialect } from "@codemirror/lang-sql";
import { forwardRef, KeyboardEventHandler, useMemo } from "react";
import { defaultKeymap, insertTab } from "@codemirror/commands";
@@ -19,9 +21,12 @@ import { sqliteDialect } from "@/drivers/sqlite/sqlite-dialect";
import { functionTooltip } from "./function-tooltips";
import sqliteFunctionList from "@/drivers/sqlite/function-tooltip.json";
import { toast } from "sonner";
+import SqlStatementHighlightPlugin from "./statement-highlight";
+import { SupportedDialect } from "@/drivers/base-driver";
interface SqlEditorProps {
value: string;
+ dialect: SupportedDialect;
readOnly?: boolean;
onChange?: (value: string) => void;
schema?: SQLNamespace;
@@ -38,6 +43,7 @@ interface SqlEditorProps {
const SqlEditor = forwardRef(
function SqlEditor(
{
+ dialect,
value,
onChange,
schema,
@@ -119,6 +125,51 @@ const SqlEditor = forwardRef(
]);
}, [fontSize, onFontSizeChanged]);
+ const extensions = useMemo(() => {
+ let sqlDialect: LanguageSupport | undefined = undefined;
+ let tooltipExtension: Extension | undefined = undefined;
+
+ if (dialect === "sqlite") {
+ sqlDialect = sql({
+ dialect: sqliteDialect,
+ schema,
+ });
+ tooltipExtension = functionTooltip(sqliteFunctionList);
+ } else {
+ sqlDialect = sql({
+ dialect: MySQLDialect,
+ schema,
+ });
+ }
+
+ return [
+ EditorView.baseTheme({
+ "& .cm-line": {
+ borderLeft: "3px solid transparent",
+ paddingLeft: "10px",
+ },
+ }),
+ keyExtensions,
+ sqlDialect,
+ tooltipExtension,
+ tableNameHighlightPlugin,
+ SqlStatementHighlightPlugin,
+ EditorView.updateListener.of((state) => {
+ const pos = state.state.selection.main.head;
+ const line = state.state.doc.lineAt(pos);
+ const lineNumber = line.number;
+ const columnNumber = pos - line.from;
+ if (onCursorChange) onCursorChange(pos, lineNumber, columnNumber);
+ }),
+ ].filter(Boolean) as Extension[];
+ }, [
+ dialect,
+ onCursorChange,
+ keyExtensions,
+ schema,
+ tableNameHighlightPlugin,
+ ]);
+
return (
(
fontSize: 20,
height: "100%",
}}
- extensions={[
- keyExtensions,
- sql({
- dialect: sqliteDialect,
- schema,
- }),
- functionTooltip(sqliteFunctionList),
- tableNameHighlightPlugin,
- EditorView.updateListener.of((state) => {
- const pos = state.state.selection.main.head;
- const line = state.state.doc.lineAt(pos);
- const lineNumber = line.number;
- const columnNumber = pos - line.from;
- if (onCursorChange) onCursorChange(pos, lineNumber, columnNumber);
- }),
- ]}
+ extensions={extensions}
/>
);
}
diff --git a/src/components/gui/sql-editor/statement-highlight.test.ts b/src/components/gui/sql-editor/statement-highlight.test.ts
new file mode 100644
index 0000000..324202a
--- /dev/null
+++ b/src/components/gui/sql-editor/statement-highlight.test.ts
@@ -0,0 +1,155 @@
+import { MySQL, SQLite } from "@codemirror/lang-sql";
+import { EditorState } from "@codemirror/state";
+import { splitSqlQuery } from "./statement-highlight";
+
+function sqlite(code: string) {
+ const state = EditorState.create({ doc: code, extensions: [SQLite] });
+ return splitSqlQuery(state).map((p) => p.text);
+}
+
+function mysql(code: string) {
+ const state = EditorState.create({ doc: code, extensions: [MySQL] });
+ return splitSqlQuery(state).map((p) => p.text);
+}
+
+describe("split sql statements", () => {
+ test("should parse a query with different statements in a single line", () => {
+ expect(
+ sqlite(
+ `INSERT INTO Persons (PersonID, Name) VALUES (1, 'Jack');SELECT * FROM Persons`
+ )
+ ).toEqual([
+ `INSERT INTO Persons (PersonID, Name) VALUES (1, 'Jack');`,
+ `SELECT * FROM Persons`,
+ ]);
+ });
+
+ test("should identify a query with different statements in multiple lines", () => {
+ expect(
+ sqlite(`
+ INSERT INTO Persons (PersonID, Name) VALUES (1, 'Jack');
+ SELECT * FROM Persons';
+ `)
+ ).toEqual([
+ `INSERT INTO Persons (PersonID, Name) VALUES (1, 'Jack');`,
+ `SELECT * FROM Persons';\n `,
+ ]);
+ });
+
+ test("sholud be able to split statement with BEGIN and END", () => {
+ expect(
+ sqlite(`CREATE TABLE customer(
+ cust_id INTEGER PRIMARY KEY,
+ cust_name TEXT,
+ cust_addr TEXT
+);
+
+-- some comment here that should be ignore
+
+
+CREATE VIEW customer_address AS
+ SELECT cust_id, cust_addr FROM customer;
+CREATE TRIGGER cust_addr_chng
+INSTEAD OF UPDATE OF cust_addr ON customer_address
+BEGIN
+ UPDATE customer SET cust_addr=NEW.cust_addr
+ WHERE cust_id=NEW.cust_id;
+END ;`)
+ ).toEqual([
+ `CREATE TABLE customer(\n cust_id INTEGER PRIMARY KEY,\n cust_name TEXT,\n cust_addr TEXT\n);`,
+ `CREATE VIEW customer_address AS\n SELECT cust_id, cust_addr FROM customer;`,
+ `CREATE TRIGGER cust_addr_chng\nINSTEAD OF UPDATE OF cust_addr ON customer_address\nBEGIN\n UPDATE customer SET cust_addr=NEW.cust_addr\n WHERE cust_id=NEW.cust_id;\nEND ;`,
+ ]);
+ });
+
+ test("should be able to split statement with BEGIN and END and CONDITION inside", () => {
+ expect(
+ mysql(`CREATE TRIGGER upd_check BEFORE UPDATE ON account
+FOR EACH ROW
+BEGIN
+ IF NEW.amount < 0 THEN
+ SET NEW.amount = 0;
+ ELSEIF NEW.amount > 100 THEN
+ SET NEW.amount = 100;
+ END IF;
+END; SELECT * FROM hello`)
+ ).toEqual([
+ `CREATE TRIGGER upd_check BEFORE UPDATE ON account\nFOR EACH ROW\nBEGIN\n IF NEW.amount < 0 THEN\n SET NEW.amount = 0;\n ELSEIF NEW.amount > 100 THEN\n SET NEW.amount = 100;\n END IF;\nEND;`,
+ "SELECT * FROM hello",
+ ]);
+ });
+
+ test("should be able to split statement with BEGIN with no end", () => {
+ expect(
+ mysql(`SELECT * FROM outerbase; CREATE TRIGGER upd_check BEFORE UPDATE ON account
+FOR EACH ROW
+BEGIN
+ IF NEW.amount < 0 THEN
+ SET NEW.amount = 0;
+ ELSEIF NEW.amount > 100 THEN
+ SET NEW.amount = 100;`)
+ ).toEqual([
+ "SELECT * FROM outerbase;",
+ `CREATE TRIGGER upd_check BEFORE UPDATE ON account
+FOR EACH ROW
+BEGIN
+ IF NEW.amount < 0 THEN
+ SET NEW.amount = 0;
+ ELSEIF NEW.amount > 100 THEN
+ SET NEW.amount = 100;`,
+ ]);
+ });
+
+ test("should be able to split TRIGGER without begin", () => {
+ expect(
+ mysql(`create trigger hire_log after insert on employees
+for each row insert into hiring values (new.id, current_time());
+
+insert into employees (first_name, last_name) values ("Tim", "Sehn");`)
+ ).toEqual([
+ `create trigger hire_log after insert on employees \nfor each row insert into hiring values (new.id, current_time());`,
+ `insert into employees (first_name, last_name) values ("Tim", "Sehn");`,
+ ]);
+ });
+
+ test("should be able to split nested BEGIN", () => {
+ expect(
+ mysql(
+ `CREATE PROCEDURE procCreateCarTable
+IS
+BEGIN
+ BEGIN
+ EXECUTE IMMEDIATE 'DROP TABLE CARS';
+ EXCEPTION WHEN OTHERS THEN NULL;
+ EXECUTE IMMEDIATE 'CREATE TABLE CARS (ID VARCHAR2(1), NAME VARCHAR2(10), TITLE
+ VARCHAR2(10))';
+ END;
+ BEGIN
+ EXECUTE IMMEDIATE 'DROP TABLE TRUCKS';
+ EXCEPTION WHEN OTHERS THEN NULL;
+ EXECUTE IMMEDIATE 'CREATE TABLE TRUCKS (ID VARCHAR2(1), NAME VARCHAR2(10), TITLE
+ VARCHAR2(10))';
+ END;
+END; SELECT * FROM outeerbase;`
+ )
+ ).toEqual([
+ `CREATE PROCEDURE procCreateCarTable
+IS
+BEGIN
+ BEGIN
+ EXECUTE IMMEDIATE 'DROP TABLE CARS';
+ EXCEPTION WHEN OTHERS THEN NULL;
+ EXECUTE IMMEDIATE 'CREATE TABLE CARS (ID VARCHAR2(1), NAME VARCHAR2(10), TITLE
+ VARCHAR2(10))';
+ END;
+ BEGIN
+ EXECUTE IMMEDIATE 'DROP TABLE TRUCKS';
+ EXCEPTION WHEN OTHERS THEN NULL;
+ EXECUTE IMMEDIATE 'CREATE TABLE TRUCKS (ID VARCHAR2(1), NAME VARCHAR2(10), TITLE
+ VARCHAR2(10))';
+ END;
+END;`,
+ "SELECT * FROM outeerbase;",
+ ]);
+ });
+});
diff --git a/src/components/gui/sql-editor/statement-highlight.ts b/src/components/gui/sql-editor/statement-highlight.ts
new file mode 100644
index 0000000..49cfb85
--- /dev/null
+++ b/src/components/gui/sql-editor/statement-highlight.ts
@@ -0,0 +1,172 @@
+import {
+ Decoration,
+ EditorState,
+ EditorView,
+ StateField,
+ Range,
+} from "@uiw/react-codemirror";
+import { syntaxTree } from "@codemirror/language";
+import { SyntaxNode } from "@lezer/common";
+
+const statementLineHighlight = Decoration.line({
+ class: "cm-highlight-statement",
+});
+
+export interface StatementSegment {
+ from: number;
+ to: number;
+ text: string;
+}
+
+function toNodeString(state: EditorState, node: SyntaxNode) {
+ return state.doc.sliceString(node.from, node.to);
+}
+
+function isRequireEndStatement(state: EditorState, node: SyntaxNode): number {
+ const ptr = node.firstChild;
+ if (!ptr) return 0;
+
+ // Majority of the query will fall in SELECT, INSERT, UPDATE, DELETE
+ const firstKeyword = toNodeString(state, ptr).toLowerCase();
+ if (firstKeyword === "select") return 0;
+ if (firstKeyword === "insert") return 0;
+ if (firstKeyword === "update") return 0;
+ if (firstKeyword === "delete") return 0;
+
+ const keywords = node.getChildren("Keyword");
+ if (keywords.length === 0) return 0;
+
+ return keywords.filter(
+ (k) => toNodeString(state, k).toLowerCase() === "begin"
+ ).length;
+}
+
+function isEndStatement(state: EditorState, node: SyntaxNode) {
+ let ptr = node.firstChild;
+ if (!ptr) return false;
+ if (toNodeString(state, ptr).toLowerCase() !== "end") return false;
+
+ ptr = ptr.nextSibling;
+ if (!ptr) return false;
+ if (toNodeString(state, ptr) !== ";") return false;
+
+ return true;
+}
+
+export function splitSqlQuery(
+ state: EditorState,
+ generateText: boolean = true
+): StatementSegment[] {
+ const topNode = syntaxTree(state).topNode;
+
+ // Get all the statements
+ let needEndStatementCounter = 0;
+ const statements = topNode.getChildren("Statement");
+
+ if (statements.length === 0) return [];
+
+ const statementGroups: SyntaxNode[][] = [];
+ let accumulateNodes: SyntaxNode[] = [];
+ let i = 0;
+
+ for (; i < statements.length; i++) {
+ const statement = statements[i];
+ needEndStatementCounter += isRequireEndStatement(state, statement);
+
+ if (needEndStatementCounter) {
+ accumulateNodes.push(statement);
+ } else {
+ statementGroups.push([statement]);
+ }
+
+ if (needEndStatementCounter && isEndStatement(state, statement)) {
+ needEndStatementCounter--;
+ if (needEndStatementCounter === 0) {
+ statementGroups.push(accumulateNodes);
+ accumulateNodes = [];
+ }
+ }
+ }
+
+ if (accumulateNodes.length > 0) {
+ statementGroups.push(accumulateNodes);
+ }
+
+ return statementGroups.map((r) => ({
+ from: r[0].from,
+ to: r[r.length - 1].to,
+ text: generateText
+ ? state.doc.sliceString(r[0].from, r[r.length - 1].to)
+ : "",
+ }));
+}
+
+export function resolveToNearestStatement(
+ state: EditorState
+): { from: number; to: number } | null {
+ // Breakdown and grouping the statement
+ const cursor = state.selection.main.from;
+ const statements = splitSqlQuery(state, false);
+
+ if (statements.length === 0) return null;
+
+ // Check if our current cursor is within any statement
+ let i = 0;
+ for (; i < statements.length; i++) {
+ const statement = statements[i];
+ if (cursor < statement.from) break;
+ if (cursor > statement.to) continue;
+ if (cursor >= statement.from && cursor <= statement.to) return statement;
+ }
+
+ if (i === 0) return statements[0];
+ if (i === statements.length) return statements[i - 1];
+
+ const cursorLine = state.doc.lineAt(cursor).number;
+ const topLine = state.doc.lineAt(statements[i - 1].to).number;
+ const bottomLine = state.doc.lineAt(statements[i].from).number;
+
+ if (cursorLine - topLine >= bottomLine - cursorLine) {
+ return statements[i];
+ } else {
+ return statements[i - 1];
+ }
+}
+function getDecorationFromState(state: EditorState) {
+ const statement = resolveToNearestStatement(state);
+
+ if (!statement) return Decoration.none;
+
+ // Get the line of the node
+ const fromLineNumber = state.doc.lineAt(statement.from).number;
+ const toLineNumber = state.doc.lineAt(statement.to).number;
+
+ const d: Range[] = [];
+ for (let i = fromLineNumber; i <= toLineNumber; i++) {
+ d.push(statementLineHighlight.range(state.doc.line(i).from));
+ }
+
+ return Decoration.set(d);
+}
+
+const SqlStatementStateField = StateField.define({
+ create(state) {
+ return getDecorationFromState(state);
+ },
+
+ update(_, tr) {
+ return getDecorationFromState(tr.state);
+ },
+
+ provide: (f) => EditorView.decorations.from(f),
+});
+
+const SqlStatementTheme = EditorView.baseTheme({
+ ".cm-highlight-statement": {
+ borderLeft: "3px solid #ff9ff3 !important",
+ },
+});
+
+const SqlStatementHighlightPlugin = [SqlStatementStateField, SqlStatementTheme];
+
+export default SqlStatementHighlightPlugin;
diff --git a/src/components/gui/tabs/query-tab.tsx b/src/components/gui/tabs/query-tab.tsx
index 329fc25..ef1ae93 100644
--- a/src/components/gui/tabs/query-tab.tsx
+++ b/src/components/gui/tabs/query-tab.tsx
@@ -1,6 +1,5 @@
import { format } from "sql-formatter";
import { useCallback, useMemo, useRef, useState } from "react";
-import { identify } from "sql-query-identifier";
import {
LucideFastForward,
LucideGrid,
@@ -18,7 +17,6 @@ import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { KEY_BINDING } from "@/lib/key-matcher";
import { ReactCodeMirrorRef } from "@uiw/react-codemirror";
-import { selectStatementFromPosition } from "@/drivers/sqlite/sql-helper";
import QueryProgressLog from "../query-progress-log";
import { useDatabaseDriver } from "@/context/driver-provider";
import {
@@ -41,6 +39,10 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
+import {
+ resolveToNearestStatement,
+ splitSqlQuery,
+} from "../sql-editor/statement-highlight";
interface QueryWindowProps {
initialCode?: string;
@@ -89,23 +91,22 @@ export default function QueryWindow({
};
const onRunClicked = (all = false) => {
- const statements = identify(code, {
- dialect: "sqlite",
- strict: false,
- });
-
let finalStatements: string[] = [];
- const editor = editorRef.current;
+ const editorState = editorRef.current?.view?.state;
+
+ if (!editorState) return;
if (all) {
- finalStatements = statements.map((s) => s.text);
- } else if (editor?.view) {
- const position = editor.view.state.selection.main.head;
- const statement = selectStatementFromPosition(statements, position);
+ finalStatements = splitSqlQuery(editorState).map((q) => q.text);
+ } else {
+ const segment = resolveToNearestStatement(editorState);
+ if (!segment) return;
+
+ const statement = editorState.doc.sliceString(segment.from, segment.to);
if (statement) {
- finalStatements = [statement.text];
+ finalStatements = [statement];
}
}
@@ -227,6 +228,7 @@ export default function QueryWindow({
diff --git a/src/drivers/base-driver.ts b/src/drivers/base-driver.ts
index 0f8a392..ace202e 100644
--- a/src/drivers/base-driver.ts
+++ b/src/drivers/base-driver.ts
@@ -31,6 +31,7 @@ export function describeTableColumnType(type: TableColumnDataType) {
}
}
+export type SupportedDialect = "sqlite" | "mysql";
export type SqlOrder = "ASC" | "DESC";
export type DatabaseRow = Record;
@@ -202,6 +203,7 @@ export interface DriverFlags {
supportBigInt: boolean;
supportCreateUpdateTable: boolean;
mismatchDetection: boolean;
+ dialect: SupportedDialect;
}
export interface DatabaseTableColumnChange {
diff --git a/src/drivers/mysql/mysql-driver.ts b/src/drivers/mysql/mysql-driver.ts
index 011c7c2..fadad57 100644
--- a/src/drivers/mysql/mysql-driver.ts
+++ b/src/drivers/mysql/mysql-driver.ts
@@ -51,6 +51,7 @@ export default abstract class MySQLLikeDriver extends CommonSQLImplement {
supportBigInt: false,
mismatchDetection: false,
supportCreateUpdateTable: false,
+ dialect: "mysql",
};
}
diff --git a/src/drivers/sqlite-base-driver.ts b/src/drivers/sqlite-base-driver.ts
index e826cef..d83ce17 100644
--- a/src/drivers/sqlite-base-driver.ts
+++ b/src/drivers/sqlite-base-driver.ts
@@ -30,6 +30,7 @@ export abstract class SqliteLikeBaseDriver extends CommonSQLImplement {
optionalSchema: true,
mismatchDetection: false,
supportCreateUpdateTable: true,
+ dialect: "sqlite",
};
}
diff --git a/src/drivers/sqlite/sql-helper.test.ts b/src/drivers/sqlite/sql-helper.test.ts
index 1e82104..84dbc6e 100644
--- a/src/drivers/sqlite/sql-helper.test.ts
+++ b/src/drivers/sqlite/sql-helper.test.ts
@@ -5,10 +5,8 @@ import {
escapeSqlBinary,
escapeSqlString,
escapeSqlValue,
- selectStatementFromPosition,
unescapeIdentity,
} from "./sql-helper";
-import { identify } from "sql-query-identifier";
describe("Escape SQL", () => {
it("escape sql string", () => {
@@ -85,29 +83,3 @@ describe("Mapping sqlite column type to our table type", () => {
});
}
});
-
-function ss(sql: string) {
- const pos = sql.indexOf("|");
- const statements = identify(sql.replace("|", ""));
- return selectStatementFromPosition(statements, pos);
-}
-
-describe("Select current query", () => {
- it("select current query", () => {
- expect(ss("select * from |t1; update t1 set name='visal';")?.text).toBe(
- "select * from t1;"
- );
-
- expect(ss("select * from t1|; update t1 set name='visal';")?.text).toBe(
- "select * from t1;"
- );
-
- expect(ss("select * from t1;| update t1 set name='visal';")?.text).toBe(
- "select * from t1;"
- );
-
- expect(ss("select * from t1; update| t1 set name='visal';")?.text).toBe(
- "update t1 set name='visal';"
- );
- });
-});
diff --git a/src/drivers/sqlite/sql-helper.ts b/src/drivers/sqlite/sql-helper.ts
index 9d16884..19a7a18 100644
--- a/src/drivers/sqlite/sql-helper.ts
+++ b/src/drivers/sqlite/sql-helper.ts
@@ -1,6 +1,5 @@
import { DatabaseValue, TableColumnDataType } from "@/drivers/base-driver";
import { hex } from "@/lib/bit-operation";
-import type { IdentifyResult } from "sql-query-identifier/lib/defines";
export function escapeIdentity(str: string) {
return `"${str.replace(/"/g, `""`)}"`;
@@ -63,16 +62,6 @@ export function convertSqliteType(
return TableColumnDataType.TEXT;
}
-export function selectStatementFromPosition(
- statements: IdentifyResult[],
- pos: number
-): IdentifyResult | undefined {
- for (const statement of statements) {
- if (statement.end + 1 >= pos) return statement;
- }
- return undefined;
-}
-
export function escapeCsvValue(value: unknown): string {
if (value === null || value === undefined) {
return "";
diff --git a/src/drivers/sqlite/sql-parse-table.test.ts b/src/drivers/sqlite/sql-parse-table.test.ts
index 77d3700..149654d 100644
--- a/src/drivers/sqlite/sql-parse-table.test.ts
+++ b/src/drivers/sqlite/sql-parse-table.test.ts
@@ -88,6 +88,7 @@ it("parse column constraint", () => {
expect(pcc(`references "users" on delete cascade ("id")`)).toEqual({
foreignKey: {
+ foreignSchemaName: "main",
foreignTableName: "users",
foreignColumns: ["id"],
},
@@ -130,6 +131,7 @@ it("parse create table", () => {
type: "integer",
constraint: {
foreignKey: {
+ foreignSchemaName: "main",
foreignTableName: "product",
foreignColumns: ["id"],
},