Skip to content

Commit e6a730a

Browse files
committed
docs
1 parent fbd5b43 commit e6a730a

File tree

3 files changed

+89
-156
lines changed

3 files changed

+89
-156
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ Check out [an interactive example](https://play.sqlc.dev/) to see it in action,
2929

3030
Additional languages can be added via [plugins](https://docs.sqlc.dev/en/latest/reference/language-support.html#community-language-support).
3131

32+
## Supported database engines
33+
34+
- PostgreSQL
35+
- MySQL
36+
- SQLite
37+
38+
Additional database engines can be added via [engine plugins](https://docs.sqlc.dev/en/latest/howto/engine-plugins.html).
39+
3240
## Sponsors
3341

3442
Development is possible thanks to our sponsors. If you would like to support sqlc,

docs/howto/engine-plugins.md

Lines changed: 80 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1-
# Database Engine Plugins
1+
# External Database Engines (Engine Plugins)
22

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).
3+
Engine plugins let you use sqlc with databases that are not built-in. You can add support for other SQL-compatible systems (e.g. CockroachDB, TiDB, or custom engines) by implementing a small external program that parses SQL and returns parameters and result columns.
4+
5+
## Why use an engine plugin?
6+
7+
- Use sqlc with a database that doesn’t have native support.
8+
- Reuse an existing SQL parser or dialect in a separate binary.
9+
- Keep engine-specific logic outside the sqlc core.
10+
11+
Data returned by the engine plugin (SQL text, parameters, columns) is passed through to [codegen plugins](../guides/plugins.md) without an extra compiler/AST step. The plugin is the single place that defines how queries are interpreted for that engine.
412

513
## Overview
614

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
15+
An engine plugin is an external process that implements one RPC:
1016

11-
## Compatibility Guarantee
17+
- **Parse** — accepts the query text and either schema SQL or connection parameters, and returns processed SQL, parameter list, and result columns.
1218

13-
For Go process plugins, compatibility is guaranteed at **compile time**:
19+
Process plugins (e.g. written in Go) talk to sqlc over **stdin/stdout** using **Protocol Buffers**. The schema is defined in `engine/engine.proto`.
20+
21+
## Compatibility
22+
23+
For Go plugins, compatibility is enforced at **compile time** by importing the engine package:
1424

1525
```go
1626
import "github.com/sqlc-dev/sqlc/pkg/engine"
1727
```
1828

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
29+
- If the plugin builds, it matches this version of the engine API.
30+
- If the API changes in a breaking way, the plugin stops compiling until it’s updated.
2231

23-
The Protocol Buffer schema ensures binary compatibility. No version negotiation needed.
32+
No version handshake is required; the proto schema defines the contract.
2433

2534
## Configuration
2635

@@ -29,223 +38,138 @@ The Protocol Buffer schema ensures binary compatibility. No version negotiation
2938
```yaml
3039
version: "2"
3140

32-
# Define engine plugins
3341
engines:
3442
- name: mydb
3543
process:
3644
cmd: sqlc-engine-mydb
3745
env:
38-
- MYDB_CONNECTION_STRING
46+
- MYDB_DSN
3947

4048
sql:
41-
- engine: mydb # Use the MyDB engine
49+
- engine: mydb
4250
schema: "schema.sql"
4351
queries: "queries.sql"
44-
gen:
45-
go:
46-
package: db
52+
codegen:
53+
- plugin: go
4754
out: db
4855
```
4956
50-
### Configuration Options
57+
### Engine options
5158
5259
| Field | Description |
5360
|-------|-------------|
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 |
61+
| `name` | Engine name used in `sql[].engine` |
62+
| `process.cmd` | Command to run (PATH or absolute path) |
63+
| `env` | Environment variables passed to the plugin |
5964

60-
## Creating a Go Engine Plugin
65+
## Implementing an engine plugin (Go)
6166

62-
### 1. Import the SDK
63-
64-
```go
65-
import "github.com/sqlc-dev/sqlc/pkg/engine"
66-
```
67-
68-
### 2. Implement the Handler
67+
### 1. Dependencies and entrypoint
6968

7069
```go
7170
package main
7271
73-
import (
74-
"github.com/sqlc-dev/sqlc/pkg/engine"
75-
)
72+
import "github.com/sqlc-dev/sqlc/pkg/engine"
7673
7774
func main() {
7875
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,
76+
PluginName: "mydb",
77+
PluginVersion: "1.0.0",
78+
Parse: handleParse,
8679
})
8780
}
8881
```
8982

90-
### 3. Implement Methods
83+
The engine API exposes only **Parse**. There are no separate methods for catalog, keywords, comment syntax, or dialect.
9184

92-
#### Parse
85+
### 2. Parse
9386

94-
Parses SQL text into statements with AST.
87+
**Request**
9588

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.
89+
- `sql` — The query text to parse.
90+
- `schema_source` — One of:
91+
- `schema_sql`: schema as in a schema.sql file (used for schema-based parsing).
92+
- `connection_params`: DSN and options for database-only mode.
11793

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-
```
94+
**Response**
13695

137-
#### IsReservedKeyword
96+
- `sql` — Processed query text. Often the same as input; with a schema you may expand `*` into explicit columns.
97+
- `parameters` — List of parameters (position/name, type, nullable, array, etc.).
98+
- `columns` — List of result columns (name, type, nullable, table/schema if known).
13899

139-
Checks if a string is a reserved keyword.
100+
Example handler:
140101

141102
```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-
```
103+
func handleParse(req *engine.ParseRequest) (*engine.ParseResponse, error) {
104+
sql := req.GetSql()
151105
152-
#### GetCommentSyntax
106+
var schema *SchemaInfo
107+
if s := req.GetSchemaSql(); s != "" {
108+
schema = parseSchema(s)
109+
}
110+
// Or use req.GetConnectionParams() for database-only mode.
153111
154-
Returns supported SQL comment syntax.
112+
parameters := extractParameters(sql)
113+
columns := extractColumns(sql, schema)
114+
processedSQL := processSQL(sql, schema) // e.g. expand SELECT *
155115
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
116+
return &engine.ParseResponse{
117+
Sql: processedSQL,
118+
Parameters: parameters,
119+
Columns: columns,
162120
}, nil
163121
}
164122
```
165123

166-
#### GetDialect
124+
Parameter and column types use the `Parameter` and `Column` messages in `engine.proto` (name, position, data_type, nullable, is_array, array_dims; for columns, table_name and schema_name are optional).
167125

168-
Returns SQL dialect information for formatting.
126+
Support for sqlc placeholders (`sqlc.arg()`, `sqlc.narg()`, `sqlc.slice()`, `sqlc.embed()`) is up to the plugin: it can parse and map them into `parameters` (and schema usage) as needed.
169127

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
128+
### 3. Build and run
182129

183130
```bash
184131
go build -o sqlc-engine-mydb .
185-
mv sqlc-engine-mydb /usr/local/bin/
132+
# Ensure sqlc-engine-mydb is on PATH or use an absolute path in process.cmd
186133
```
187134

188135
## Protocol
189136

190-
### Process Plugins (Go)
191-
192-
Process plugins use **Protocol Buffers** for serialization:
137+
Process plugins use Protocol Buffers on stdin/stdout:
193138

194139
```
195-
sqlc stdin (protobuf) plugin stdout (protobuf) sqlc
140+
sqlc stdin (protobuf) plugin stdout (protobuf) sqlc
196141
```
197142

198-
The proto schema is published at `buf.build/sqlc/sqlc` in `engine/engine.proto`.
143+
Invocation:
199144

200-
Methods are invoked as command-line arguments:
201145
```bash
202-
sqlc-engine-mydb parse # stdin: ParseRequest, stdout: ParseResponse
203-
sqlc-engine-mydb get_catalog # stdin: GetCatalogRequest, stdout: GetCatalogResponse
146+
sqlc-engine-mydb parse # stdin: ParseRequest, stdout: ParseResponse
204147
```
205148

206-
### WASM Plugins
149+
The definition lives in `engine/engine.proto` (and generated Go in `pkg/engine`).
207150

208-
WASM plugins use **JSON** for broader language compatibility:
151+
## Example
209152

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.
153+
A minimal engine that parses SQLite-style SQL and expands `*` using a schema is in this repository under `examples/plugin-based-codegen/plugins/sqlc-engine-sqlite3/`. It pairs with the Rust codegen example in the same `plugin-based-codegen` sample.
217154

218155
## Architecture
219156

220157
```
221158
┌─────────────────────────────────────────────────────────────────┐
222159
│ 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 │
160+
│ 1. Read sqlc.yaml, find engine for this sql block │
161+
│ 2. Call plugin: parse (sql + schema_sql or connection_params) │
162+
│ 3. Use returned sql, parameters, columns in codegen │
229163
└─────────────────────────────────────────────────────────────────┘
230164

231-
Process Plugin Communication (Protobuf):
232-
233165
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-
│ │
166+
│──── spawn, args: ["parse"] ──────────────────────────────► │
167+
│──── stdin: ParseRequest{sql, schema_sql|connection_params} ► │
168+
│◄─── stdout: ParseResponse{sql, parameters, columns} ─────── │
245169
```
246170
247-
## See Also
171+
## See also
248172
249-
- [Codegen Plugins](plugins.md) - For custom code generators
250-
- [Configuration Reference](../reference/config.md)
173+
- [Codegen plugins](../guides/plugins.md) — Custom code generators that consume engine output.
174+
- [Configuration reference](../reference/config.md)
251175
- Proto schema: `protos/engine/engine.proto`

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ code ever again.
6666
howto/embedding.md
6767
howto/overrides.md
6868
howto/rename.md
69+
howto/engine-plugins.md
6970

7071
.. toctree::
7172
:maxdepth: 3

0 commit comments

Comments
 (0)