diff --git a/README.md b/README.md
index 6299b49..c879dac 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,79 @@
# SQL Converter
-SQL Converter is a browser-side tool for converting MariaDB/MySQL `.sql` dump files into SQLite-compatible SQL.
+SQL Converter converts MariaDB/MySQL `.sql` dump files into SQLite-compatible SQL in the browser.
Live app: https://dominosaurs.github.io/sql-converter/
-## What It Does
+## ✨ Why This Exists
-- Converts MariaDB/MySQL dump syntax into SQLite-oriented SQL.
-- Streams large input files directly from the browser.
-- Writes the converted output to a local file without uploading database data.
-- Handles common dump features such as data type conversion, quoted identifiers, auto-increment primary keys, indexes, foreign keys, JSON checks, dump directives, and MySQL string escapes.
+MariaDB and MySQL dumps often contain syntax that SQLite cannot import directly: backtick identifiers, `AUTO_INCREMENT`, engine options, charset/collation options, MariaDB dump directives, and MySQL-style escaped strings.
-## Browser Requirement
+SQL Converter rewrites the common parts of those dumps so the output can be imported into SQLite with fewer manual edits.
-Large-file conversion uses the File System Access API so the app can stream output directly to disk.
+## 🔒 Privacy
-Use a browser that supports `showSaveFilePicker`, such as:
+Conversion runs locally in your browser.
+
+- The input dump is read from your disk.
+- The converted file is written back to your disk.
+- The app does not upload your database dump to a server.
+
+## 🌐 Browser Requirement
+
+Large-file conversion uses the File System Access API so output can be streamed directly to disk.
+
+Recommended browsers:
- Chrome
- Edge
-Firefox and Safari may not support the required direct-to-disk streaming flow.
+Firefox and Safari may not support the required `showSaveFilePicker` API for large-file direct-to-disk conversion.
+
+## 🚀 Usage
+
+1. Open https://dominosaurs.github.io/sql-converter/
+2. Select a MariaDB/MySQL `.sql` dump file.
+3. Choose where to save the converted SQLite SQL file.
+4. Wait for conversion to finish.
+5. Import the generated `.sqlite.sql` file with your SQLite client.
+
+## 🔁 Example
+
+MariaDB/MySQL dump input:
+
+```sql
+CREATE TABLE `users` (
+ `id` bigint unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+INSERT INTO `users` VALUES
+(1,'O\'Connor');
+```
-## Supported Conversion Scope
+SQLite output:
-The converter is designed for common `mysqldump` / MariaDB dump files.
+```sql
+CREATE TABLE "users" (
+ "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ "name" TEXT NOT NULL
+);
+
+INSERT INTO "users" VALUES
+(1,'O''Connor');
+```
+
+## ✅ Supported Conversion Scope
+
+The converter is designed for common `mysqldump`, MariaDB dump, and phpMyAdmin-style SQL exports.
Currently handled:
- `CREATE TABLE`
- `DROP TABLE`
- `INSERT`
+- Backtick identifiers to SQLite double-quoted identifiers
- MariaDB/MySQL integer, decimal, text, blob, date/time, enum/set, and JSON-like column types
- `AUTO_INCREMENT` to SQLite `INTEGER PRIMARY KEY AUTOINCREMENT`
- Table-level primary keys
@@ -42,87 +85,56 @@ Currently handled:
- MySQL escaped string values in inserts
- MySQL hex literals like `0xDEADBEEF`
-Known limits:
+Generated string values use standard SQLite single-quoted strings:
-- This is not a full SQL parser.
-- Stored procedures, triggers, views, generated columns, and complex vendor-specific SQL may need more handling.
-- Memory use is bounded by chunks and completed statements, but a single extremely large SQL statement can still be expensive.
+```sql
+'O''Connor'
+```
-## Usage
+Multi-row inserts are preserved for performance.
-1. Open https://dominosaurs.github.io/sql-converter/
-2. Select a MariaDB/MySQL `.sql` dump file.
-3. Choose the output file location when prompted.
-4. Wait for conversion to finish.
-5. Import the generated `.sqlite.sql` file with your SQLite client.
+## ⚠️ Known Limits
-## Local Development
+This is a practical dump converter, not a full SQL parser or validator.
-This project uses Bun.
+Known limits:
-```bash
-bun install
-bun run dev
-```
+- Stored procedures are not supported.
+- Triggers and views may need manual review.
+- Generated columns may need manual review.
+- Vendor-specific functions may need manual review.
+- A single extremely large SQL statement can still be expensive, even though file reading/writing is streamed.
+- SQLite client/importer behavior varies. Some GUI tools may have script execution bugs even when the generated SQL is valid SQLite.
-Run checks:
+## 📥 Import Notes
-```bash
-bun run lint
-bun test
-bun run build
-```
+If your SQLite GUI client fails to import a generated file, test the failing statement with another importer before assuming the SQL is invalid.
-Preview production build:
+Recommended SQLite CLI import:
```bash
-bun run build
-bun run preview
+sqlite3 output.sqlite < converted.sqlite.sql
```
-## Scripts
+Or inside SQLite:
-- `bun run dev` starts the Vite dev server.
-- `bun run build` type-checks and builds the app.
-- `bun run lint` runs Knip, Biome, and TypeScript checks.
-- `bun run format` applies Biome formatting/fixes.
-- `bun test` runs converter tests.
-- `bun run preview` previews the production build.
+```sql
+.read converted.sqlite.sql
+```
-## Deployment
+## 🛠️ Local Development
-The app is configured for GitHub Pages at:
+This project uses Bun.
-```text
-https://dominosaurs.github.io/sql-converter/
+```bash
+bun install
+bun run dev
```
-Vite uses:
+Run checks:
-```ts
-base: '/sql-converter/'
+```bash
+bun run lint
+bun test
+bun run build
```
-
-GitHub Actions workflows:
-
-- `.github/workflows/ci.yml` runs lint, tests, and build.
-- `.github/workflows/deploy-pages.yml` builds and deploys `dist` to GitHub Pages.
-
-In GitHub repository settings, configure Pages source as **GitHub Actions**.
-
-## Reporting Issues
-
-If a dump fails to import after conversion, open an issue:
-
-https://github.com/dominosaurs/sql-converter/issues
-
-Include:
-
-- The SQLite error message.
-- The source SQL statement that caused it.
-- The converted output statement.
-- Whether the dump came from MariaDB, MySQL, phpMyAdmin, or another tool.
-
-## License
-
-MIT
diff --git a/package.json b/package.json
index 6770de1..3c7513e 100644
--- a/package.json
+++ b/package.json
@@ -43,5 +43,5 @@
"lint": "knip && biome check --write . && tsc --noEmit"
},
"type": "module",
- "version": "0.0.2"
+ "version": "0.0.3"
}
diff --git a/src/app.tsx b/src/app.tsx
index 4e7ff46..1855564 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -36,22 +36,35 @@ type ConversionState =
| 'error'
| 'cancelled'
+const DEFAULT_STATUS =
+ 'Choose a MariaDB .sql dump, then convert it to a SQLite import file.'
+const UNSUPPORTED_BROWSER_STATUS =
+ 'This browser cannot stream directly to disk. Use Chrome or Edge before selecting a large SQL file.'
+
export default function App() {
const inputFileRef = useRef(null)
const abortControllerRef = useRef(null)
+ const isBrowserSupported = supportsSaveFilePicker()
const [warnings, setWarnings] = useState([])
const [progress, setProgress] = useState(null)
const [selectedFileName, setSelectedFileName] = useState('')
const [selectedFileSize, setSelectedFileSize] = useState(0)
const [outputFileName, setOutputFileName] = useState('')
- const [status, setStatus] = useState(
- 'Choose a MariaDB .sql dump, then convert it to a SQLite import file.',
+ const [status, setStatus] = useState(() =>
+ isBrowserSupported ? DEFAULT_STATUS : UNSUPPORTED_BROWSER_STATUS,
+ )
+ const [conversionState, setConversionState] = useState(
+ () => (isBrowserSupported ? 'idle' : 'error'),
)
- const [conversionState, setConversionState] =
- useState('idle')
const [isConverting, setIsConverting] = useState(false)
const handleConvertClick = () => {
+ if (!isBrowserSupported) {
+ setStatus(UNSUPPORTED_BROWSER_STATUS)
+ setConversionState('error')
+ return
+ }
+
const file = inputFileRef.current?.files?.[0]
if (!file) {
@@ -63,6 +76,12 @@ export default function App() {
}
const handleFileChange = (file: File | undefined) => {
+ if (!isBrowserSupported) {
+ setStatus(UNSUPPORTED_BROWSER_STATUS)
+ setConversionState('error')
+ return
+ }
+
setSelectedFileName(file?.name ?? '')
setSelectedFileSize(file?.size ?? 0)
@@ -78,9 +97,7 @@ export default function App() {
.showSaveFilePicker
if (!saveFilePicker) {
- setStatus(
- 'This browser cannot stream directly to disk. Use Chrome or Edge for large 1GB files.',
- )
+ setStatus(UNSUPPORTED_BROWSER_STATUS)
setConversionState('error')
return
}
@@ -168,9 +185,9 @@ export default function App() {
setSelectedFileSize(0)
setOutputFileName('')
setStatus(
- 'Choose a MariaDB .sql dump, then convert it to a SQLite import file.',
+ isBrowserSupported ? DEFAULT_STATUS : UNSUPPORTED_BROWSER_STATUS,
)
- setConversionState('idle')
+ setConversionState(isBrowserSupported ? 'idle' : 'error')
}
const isFinished =
@@ -212,10 +229,12 @@ export default function App() {