Skip to content

Commit 6260374

Browse files
committed
support: sqlite3 and mssql
1 parent 016883e commit 6260374

File tree

5 files changed

+242
-84
lines changed

5 files changed

+242
-84
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ dev: sqldef.wasm
66
@echo "Starting HTTP server at http://localhost:6543"
77
@python3 -m http.server 6543
88
.PHONEY: devk
9+
10+
format:
11+
go fmt .
12+
.PHONEY: format

index.html

Lines changed: 152 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!DOCTYPE html>
22
<html>
3-
<title>sqldef</title>
3+
<title>sqldef.github.io</title>
44
<link href="https://fonts.googleapis.com/css?family=Raleway&display=swap" rel="stylesheet">
55
<head>
66
<meta charset="utf-8">
@@ -20,12 +20,17 @@
2020
background: #eee;
2121
color: #000;
2222
padding: 20px;
23+
}
24+
textarea {
2325
width: 100%;
26+
box-sizing: border-box;
2427
}
2528
pre {
2629
display: none;
30+
width: 100%;
31+
box-sizing: border-box;
2732
}
28-
#error {
33+
.error {
2934
padding: 1em;
3035
background: IndianRed;
3136
color: DarkRed;
@@ -39,15 +44,40 @@
3944
}
4045
h4.schema-header {
4146
margin-bottom: 0.7em;
47+
margin-top: 1em;
48+
}
49+
.schema-row {
50+
display: flex;
51+
gap: 20px;
52+
margin-bottom: 20px;
53+
}
54+
.schema-pane {
55+
flex: 1;
56+
}
57+
.diff-section {
58+
margin-bottom: 20px;
59+
}
60+
.diff-label {
61+
font-weight: bold;
62+
margin-bottom: 0.5em;
63+
}
64+
select {
65+
font-size: 18px;
66+
padding: 8px;
67+
margin-bottom: 20px;
68+
}
69+
footer {
70+
margin-top: 100px;
71+
text-align: center;
4272
}
4373
</style>
4474
</head>
4575
<body>
4676
<h1>sqldef</h1>
47-
<img src="https://github.com/k0kubun/sqldef/raw/master/demo.gif" alt="screen capture" />
48-
49-
<h2>What is it?</h2>
50-
<p>sqldef is a <a href="https://github.com/k0kubun/sqldef">CLI tool</a>, <a href="https://github.com/sqldef/sqldef.github.io">webasm library</a>, and <a href="https://github.com/sqldef/node-sqldef">nodejs tool/library</a> for diffing SQL schema. You can use it to manage migration of PostgreSQL and MySQL databases, using regular SQL DDL.</p>
77+
<p>sqldef is a <a href="https://github.com/sqldef/sqldef">CLI tool</a>, <a
78+
href="https://github.com/sqldef/sqldef.github.io">Wasm library</a>, and <a
79+
href="https://github.com/sqldef/node-sqldef">nodejs tool/library</a> for diffing two SQL schemas. You can use it to
80+
manage migration of PostgreSQL, MySQL, SQLite3, and SQL Server databases, using regular SQL DDLs.</p>
5181

5282
<h2>Demo</h2>
5383
<p>You can generate DDLs to update the DB schema:</p>
@@ -56,50 +86,141 @@ <h2>Demo</h2>
5686
<select id="dbType">
5787
<option value="mysql">MySQL</option>
5888
<option value="postgres">PostgreSQL</option>
89+
<option value="sqlite3">SQLite3</option>
90+
<option value="mssql">SQL Server</option>
5991
</select>
60-
<button id="buttonDiff">DIFF</button>
6192
</div>
6293

63-
<pre id="output"></pre>
64-
<pre id="error"></pre>
65-
66-
<h4 class="schema-header">Current schema</h4>
67-
<textarea id="inputA" rows="10">
68-
CREATE TABLE user (
94+
<div class="schema-row">
95+
<div class="schema-pane">
96+
<h4 class="schema-header">Current schema</h4>
97+
<textarea id="inputA" rows="10">CREATE TABLE users (
6998
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
7099
name VARCHAR(128) DEFAULT 'konsumer'
71-
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;
72-
</textarea>
73-
74-
<h4 class="schema-header">New schema</h4>
75-
<textarea id="inputB" rows="10">
76-
CREATE TABLE user (
100+
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;</textarea>
101+
</div>
102+
<div class="schema-pane">
103+
<h4 class="schema-header">Desired schema</h4>
104+
<textarea id="inputB" rows="10">CREATE TABLE users (
77105
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
78106
name VARCHAR(128) DEFAULT 'konsumer',
79107
created_at DATETIME NOT NULL
80-
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;
81-
</textarea>
108+
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;</textarea>
109+
</div>
110+
</div>
111+
112+
<div class="diff-section">
113+
<div class="diff-label">Up (current → desired)</div>
114+
<pre id="outputUp"></pre>
115+
<pre id="errorUp" class="error"></pre>
116+
</div>
117+
118+
<div class="diff-section">
119+
<div class="diff-label">Down (desired → current)</div>
120+
<pre id="outputDown"></pre>
121+
<pre id="errorDown" class="error"></pre>
122+
</div>
82123

83124
<script>
84-
const button = document.getElementById('buttonDiff')
85125
const dbType = document.getElementById('dbType')
86126
const inputA = document.getElementById('inputA')
87127
const inputB = document.getElementById('inputB')
88-
const output = document.getElementById('output')
89-
const error = document.getElementById('error')
128+
const outputUp = document.getElementById('outputUp')
129+
const errorUp = document.getElementById('errorUp')
130+
const outputDown = document.getElementById('outputDown')
131+
const errorDown = document.getElementById('errorDown')
90132

91-
button.addEventListener('click', async () => {
92-
output.style.display = 'none'
93-
error.style.display = 'none'
133+
const schemaExamples = {
134+
mysql: {
135+
current: `CREATE TABLE users (
136+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
137+
name VARCHAR(128) DEFAULT 'konsumer'
138+
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;`,
139+
desired: `CREATE TABLE users (
140+
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
141+
name VARCHAR(128) DEFAULT 'konsumer',
142+
created_at DATETIME NOT NULL
143+
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;`
144+
},
145+
postgres: {
146+
current: `CREATE TABLE users (
147+
id BIGSERIAL PRIMARY KEY,
148+
name VARCHAR(128) DEFAULT 'konsumer'
149+
);`,
150+
desired: `CREATE TABLE users (
151+
id BIGSERIAL PRIMARY KEY,
152+
name VARCHAR(128) DEFAULT 'konsumer',
153+
created_at TIMESTAMP NOT NULL
154+
);`
155+
},
156+
sqlite3: {
157+
current: `CREATE TABLE users (
158+
id INTEGER PRIMARY KEY AUTOINCREMENT,
159+
name TEXT DEFAULT 'konsumer'
160+
);`,
161+
desired: `CREATE TABLE users (
162+
id INTEGER PRIMARY KEY AUTOINCREMENT,
163+
name TEXT DEFAULT 'konsumer',
164+
created_at TEXT NOT NULL
165+
);`
166+
},
167+
mssql: {
168+
current: `CREATE TABLE users (
169+
id BIGINT IDENTITY(1,1) PRIMARY KEY,
170+
name NVARCHAR(128) DEFAULT 'konsumer'
171+
);`,
172+
desired: `CREATE TABLE users (
173+
id BIGINT IDENTITY(1,1) PRIMARY KEY,
174+
name NVARCHAR(128) DEFAULT 'konsumer',
175+
created_at DATETIME NOT NULL
176+
);`
177+
}
178+
}
179+
180+
async function runDiff() {
181+
// Run up diff (current -> desired)
182+
outputUp.style.display = 'none'
183+
errorUp.style.display = 'none'
184+
try {
185+
const result = await window.sqldef(dbType.value, inputB.value, inputA.value)
186+
outputUp.innerHTML = result
187+
outputUp.style.display = 'block'
188+
} catch (e) {
189+
errorUp.style.display = 'block'
190+
errorUp.innerHTML = e.message
191+
}
192+
193+
// Run down diff (desired -> current)
194+
outputDown.style.display = 'none'
195+
errorDown.style.display = 'none'
94196
try {
95-
output.innerHTML = await window.sqldef(dbType.value, inputB.value, inputA.value)
96-
output.style.display = 'block'
197+
const result = await window.sqldef(dbType.value, inputA.value, inputB.value)
198+
outputDown.innerHTML = result
199+
outputDown.style.display = 'block'
97200
} catch (e) {
98-
error.style.display = 'block'
99-
error.innerHTML = e.message
201+
errorDown.style.display = 'block'
202+
errorDown.innerHTML = e.message
100203
}
204+
}
205+
206+
dbType.addEventListener('change', () => {
207+
const examples = schemaExamples[dbType.value]
208+
if (examples) {
209+
inputA.value = examples.current
210+
inputB.value = examples.desired
211+
}
212+
runDiff()
101213
})
214+
215+
inputA.addEventListener('input', runDiff)
216+
inputB.addEventListener('input', runDiff)
217+
218+
// Run diff on initial load
219+
runDiff()
102220
</script>
103221

222+
<footer>
223+
<p><a href="https://github.com/sqldef">https://github.com/sqldef</a></p>
224+
</footer>
104225
</body>
105226
</html>

sqldef-wasm.go

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,68 @@
1-
// +build js
2-
// This is a light wasm wraper around just the DDL conversion stuff
1+
//go:build js
2+
3+
// This is a light wasm wrapper around just the DDL conversion stuff
34
package main
45

56
import (
6-
"strings"
7-
"syscall/js"
8-
"github.com/sqldef/sqldef/v3/database"
9-
"github.com/sqldef/sqldef/v3/parser"
10-
"github.com/sqldef/sqldef/v3/schema"
7+
"fmt"
8+
"strings"
9+
"syscall/js"
10+
11+
"github.com/sqldef/sqldef/v3/database"
12+
"github.com/sqldef/sqldef/v3/parser"
13+
"github.com/sqldef/sqldef/v3/schema"
1114
)
1215

13-
func diff(this js.Value, args []js.Value) interface {} {
14-
mode := args[0].String()
15-
desiredDDLs := args[1].String()
16-
currentDDLs := args[2].String()
17-
callback := args[3]
18-
19-
generatorMode := schema.GeneratorModeMysql
20-
parserMode := parser.ParserModeMysql
21-
if (mode == "postgres"){
22-
generatorMode = schema.GeneratorModePostgres
23-
parserMode = parser.ParserModePostgres
24-
}
25-
26-
sqlParser := database.NewParser(parserMode)
27-
config := database.GeneratorConfig{}
28-
defaultSchema := ""
29-
30-
ddls, err := schema.GenerateIdempotentDDLs(generatorMode, sqlParser, desiredDDLs, currentDDLs, config, defaultSchema)
31-
out := strings.Join(ddls, ";\n")
32-
33-
if err != nil {
34-
callback.Invoke(err.Error(), out)
35-
return false
36-
} else {
37-
callback.Invoke(js.Null(), out)
38-
return true
39-
}
16+
// diff function (mode: string, desiredDDLs: string, currentDDLs: string, callback: (err: string | null, result: string | null): void)
17+
func sqldefDiff(this js.Value, args []js.Value) any {
18+
mode := args[0].String()
19+
desiredDDLs := args[1].String()
20+
currentDDLs := args[2].String()
21+
callback := args[3]
22+
23+
generatorMode := schema.GeneratorModeMysql
24+
parserMode := parser.ParserModeMysql
25+
switch mode {
26+
case "postgres":
27+
generatorMode = schema.GeneratorModePostgres
28+
parserMode = parser.ParserModePostgres
29+
case "mysql":
30+
generatorMode = schema.GeneratorModeMysql
31+
parserMode = parser.ParserModeMysql
32+
case "sqlite3":
33+
generatorMode = schema.GeneratorModeSQLite3
34+
parserMode = parser.ParserModeSQLite3
35+
case "mssql":
36+
generatorMode = schema.GeneratorModeMssql
37+
parserMode = parser.ParserModeMssql
38+
default:
39+
callback.Invoke(fmt.Errorf("unsupported database type: %s", mode), nil)
40+
return false
41+
}
42+
43+
sqlParser := database.NewParser(parserMode)
44+
config := database.GeneratorConfig{}
45+
defaultSchema := ""
46+
47+
ddls, err := schema.GenerateIdempotentDDLs(generatorMode, sqlParser, desiredDDLs, currentDDLs, config, defaultSchema)
48+
out := strings.Join(ddls, ";\n")
49+
50+
if err != nil {
51+
callback.Invoke(err.Error(), out)
52+
return false
53+
} else {
54+
callback.Invoke(nil, out)
55+
return true
56+
}
4057
}
4158

4259
func main() {
43-
c := make(chan bool)
44-
// I wish this wasn't global!
45-
js.Global().Set("_SQLDEF", js.FuncOf(diff))
46-
<-c
60+
c := make(chan bool)
61+
62+
exports := map[string]any{
63+
"diff": js.FuncOf(sqldefDiff),
64+
}
65+
js.Global().Set("_SQLDEF", js.ValueOf(exports))
66+
67+
<-c
4768
}

sqldef.wasm

1.76 KB
Binary file not shown.

0 commit comments

Comments
 (0)