Skip to content

Commit 383b620

Browse files
committed
Added support for the database engine plugin system for extending sqlc with new databases (in addition to PostgreSQL, Dolphin, sqlite)
1 parent 67e865b commit 383b620

File tree

32 files changed

+6201
-12
lines changed

32 files changed

+6201
-12
lines changed

docs/howto/engine-plugins.md

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# Database Engine Plugins
2+
3+
sqlc supports adding custom database backends through engine plugins. This allows you to use sqlc with databases that aren't natively supported (like MyDB, CockroachDB, or other SQL-compatible databases).
4+
5+
## Overview
6+
7+
Engine plugins are external programs that implement the sqlc engine interface:
8+
- **Process plugins** (Go): Communicate via **Protocol Buffers** over stdin/stdout
9+
- **WASM plugins** (any language): Communicate via **JSON** over stdin/stdout
10+
11+
## Compatibility Guarantee
12+
13+
For Go process plugins, compatibility is guaranteed at **compile time**:
14+
15+
```go
16+
import "github.com/sqlc-dev/sqlc/pkg/engine"
17+
```
18+
19+
When you import this package:
20+
- If your plugin compiles successfully → it's compatible with this version of sqlc
21+
- If types change incompatibly → your plugin won't compile until you update it
22+
23+
The Protocol Buffer schema ensures binary compatibility. No version negotiation needed.
24+
25+
## Configuration
26+
27+
### sqlc.yaml
28+
29+
```yaml
30+
version: "2"
31+
32+
# Define engine plugins
33+
engines:
34+
- name: mydb
35+
process:
36+
cmd: sqlc-engine-mydb
37+
env:
38+
- MYDB_CONNECTION_STRING
39+
40+
sql:
41+
- engine: mydb # Use the MyDB engine
42+
schema: "schema.sql"
43+
queries: "queries.sql"
44+
gen:
45+
go:
46+
package: db
47+
out: db
48+
```
49+
50+
### Configuration Options
51+
52+
| Field | Description |
53+
|-------|-------------|
54+
| `name` | Unique name for the engine (used in `sql[].engine`) |
55+
| `process.cmd` | Command to run (must be in PATH or absolute path) |
56+
| `wasm.url` | URL to download WASM module (`file://` or `https://`) |
57+
| `wasm.sha256` | SHA256 checksum of the WASM module |
58+
| `env` | Environment variables to pass to the plugin |
59+
60+
## Creating a Go Engine Plugin
61+
62+
### 1. Import the SDK
63+
64+
```go
65+
import "github.com/sqlc-dev/sqlc/pkg/engine"
66+
```
67+
68+
### 2. Implement the Handler
69+
70+
```go
71+
package main
72+
73+
import (
74+
"github.com/sqlc-dev/sqlc/pkg/engine"
75+
)
76+
77+
func main() {
78+
engine.Run(engine.Handler{
79+
PluginName: "mydb",
80+
PluginVersion: "1.0.0",
81+
Parse: handleParse,
82+
GetCatalog: handleGetCatalog,
83+
IsReservedKeyword: handleIsReservedKeyword,
84+
GetCommentSyntax: handleGetCommentSyntax,
85+
GetDialect: handleGetDialect,
86+
})
87+
}
88+
```
89+
90+
### 3. Implement Methods
91+
92+
#### Parse
93+
94+
Parses SQL text into statements with AST.
95+
96+
```go
97+
func handleParse(req *engine.ParseRequest) (*engine.ParseResponse, error) {
98+
sql := req.GetSql()
99+
// Parse SQL using your database's parser
100+
101+
return &engine.ParseResponse{
102+
Statements: []*engine.Statement{
103+
{
104+
RawSql: sql,
105+
StmtLocation: 0,
106+
StmtLen: int32(len(sql)),
107+
AstJson: astJSON, // AST encoded as JSON bytes
108+
},
109+
},
110+
}, nil
111+
}
112+
```
113+
114+
#### GetCatalog
115+
116+
Returns the initial catalog with built-in types and functions.
117+
118+
```go
119+
func handleGetCatalog(req *engine.GetCatalogRequest) (*engine.GetCatalogResponse, error) {
120+
return &engine.GetCatalogResponse{
121+
Catalog: &engine.Catalog{
122+
DefaultSchema: "public",
123+
Name: "mydb",
124+
Schemas: []*engine.Schema{
125+
{
126+
Name: "public",
127+
Functions: []*engine.Function{
128+
{Name: "now", ReturnType: &engine.DataType{Name: "timestamp"}},
129+
},
130+
},
131+
},
132+
},
133+
}, nil
134+
}
135+
```
136+
137+
#### IsReservedKeyword
138+
139+
Checks if a string is a reserved keyword.
140+
141+
```go
142+
func handleIsReservedKeyword(req *engine.IsReservedKeywordRequest) (*engine.IsReservedKeywordResponse, error) {
143+
reserved := map[string]bool{
144+
"select": true, "from": true, "where": true,
145+
}
146+
return &engine.IsReservedKeywordResponse{
147+
IsReserved: reserved[strings.ToLower(req.GetKeyword())],
148+
}, nil
149+
}
150+
```
151+
152+
#### GetCommentSyntax
153+
154+
Returns supported SQL comment syntax.
155+
156+
```go
157+
func handleGetCommentSyntax(req *engine.GetCommentSyntaxRequest) (*engine.GetCommentSyntaxResponse, error) {
158+
return &engine.GetCommentSyntaxResponse{
159+
Dash: true, // -- comment
160+
SlashStar: true, // /* comment */
161+
Hash: false, // # comment
162+
}, nil
163+
}
164+
```
165+
166+
#### GetDialect
167+
168+
Returns SQL dialect information for formatting.
169+
170+
```go
171+
func handleGetDialect(req *engine.GetDialectRequest) (*engine.GetDialectResponse, error) {
172+
return &engine.GetDialectResponse{
173+
QuoteChar: "`", // Identifier quoting character
174+
ParamStyle: "dollar", // $1, $2, ...
175+
ParamPrefix: "$", // Parameter prefix
176+
CastSyntax: "cast_function", // CAST(x AS type) or "double_colon" for ::
177+
}, nil
178+
}
179+
```
180+
181+
### 4. Build and Install
182+
183+
```bash
184+
go build -o sqlc-engine-mydb .
185+
mv sqlc-engine-mydb /usr/local/bin/
186+
```
187+
188+
## Protocol
189+
190+
### Process Plugins (Go)
191+
192+
Process plugins use **Protocol Buffers** for serialization:
193+
194+
```
195+
sqlc → stdin (protobuf) → plugin → stdout (protobuf) → sqlc
196+
```
197+
198+
The proto schema is published at `buf.build/sqlc/sqlc` in `engine/engine.proto`.
199+
200+
Methods are invoked as command-line arguments:
201+
```bash
202+
sqlc-engine-mydb parse # stdin: ParseRequest, stdout: ParseResponse
203+
sqlc-engine-mydb get_catalog # stdin: GetCatalogRequest, stdout: GetCatalogResponse
204+
```
205+
206+
### WASM Plugins
207+
208+
WASM plugins use **JSON** for broader language compatibility:
209+
210+
```
211+
sqlc → stdin (JSON) → wasm module → stdout (JSON) → sqlc
212+
```
213+
214+
## Full Example
215+
216+
See `examples/plugin-based-codegen/` for a complete engine plugin implementation.
217+
218+
## Architecture
219+
220+
```
221+
┌─────────────────────────────────────────────────────────────────┐
222+
│ sqlc generate │
223+
│ │
224+
│ 1. Read sqlc.yaml │
225+
│ 2. Find engine: mydb → look up in engines[] │
226+
│ 3. Run: sqlc-engine-mydb parse < schema.sql │
227+
│ 4. Get AST via protobuf on stdout │
228+
│ 5. Generate Go code │
229+
└─────────────────────────────────────────────────────────────────┘
230+
231+
Process Plugin Communication (Protobuf):
232+
233+
sqlc sqlc-engine-mydb
234+
──── ────────────────
235+
│ │
236+
│──── spawn process ─────────────► │
237+
│ args: ["parse"] │
238+
│ │
239+
│──── protobuf on stdin ─────────► │
240+
│ ParseRequest{sql: "..."} │
241+
│ │
242+
│◄─── protobuf on stdout ───────── │
243+
│ ParseResponse{statements} │
244+
│ │
245+
```
246+
247+
## See Also
248+
249+
- [Codegen Plugins](plugins.md) - For custom code generators
250+
- [Configuration Reference](../reference/config.md)
251+
- Proto schema: `protos/engine/engine.proto`

0 commit comments

Comments
 (0)