Option to output additional read only client #694
-
It would be great if we could set an option to additionally generate an interface that only has read only functions on it. This would be extremely handy for situations where you have read replica endpoints on your database and create a separate connection to them and you only ever want to read instead of write. Your application would then have two different clients. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hey, thanks for the suggestion. This is a really good idea. I think we can get away with generating a read-only interface instead of a different struct. Here's a simple example, given the following two input files: -- query.sql
CREATE TABLE foo (bar text);
-- name: ListFoo :many
SELECT * FROM foo;
-- name: CreateFoo :exec
INSERT INTO foo (bar) VALUES ($1); {
"version": "1",
"packages": [
{
"path": "go",
"name": "db",
"schema": "query.sql",
"queries": "query.sql",
"engine": "postgresql",
"emit_interface": true
}
]
} Today, this generates the following four Go files: // Code generated by sqlc. DO NOT EDIT.
// models.go
package db
import (
"database/sql"
)
type Foo struct {
Bar sql.NullString
} // Code generated by sqlc. DO NOT EDIT.
// querier.go
package db
import (
"context"
"database/sql"
)
type Querier interface {
CreateFoo(ctx context.Context, bar sql.NullString) error
ListFoo(ctx context.Context) ([]sql.NullString, error)
}
var _ Querier = (*Queries)(nil) // Code generated by sqlc. DO NOT EDIT.
// source: query.sql
package db
import (
"context"
"database/sql"
)
const createFoo = `-- name: CreateFoo :exec
INSERT INTO foo (bar) VALUES ($1)
`
func (q *Queries) CreateFoo(ctx context.Context, bar sql.NullString) error {
_, err := q.db.ExecContext(ctx, createFoo, bar)
return err
}
const listFoo = `-- name: ListFoo :many
SELECT bar FROM foo
`
func (q *Queries) ListFoo(ctx context.Context) ([]sql.NullString, error) {
rows, err := q.db.QueryContext(ctx, listFoo)
if err != nil {
return nil, err
}
defer rows.Close()
var items []sql.NullString
for rows.Next() {
var bar sql.NullString
if err := rows.Scan(&bar); err != nil {
return nil, err
}
items = append(items, bar)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
} // Code generated by sqlc. DO NOT EDIT.
// db.go
package db
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
} Let's say we add an "emit_readonly_interface" configuration option. I'm envisioning that type ReadOnlyQuerier interface {
ListFoo(ctx context.Context) ([]sql.NullString, error)
}
var _ ReadOnlyQuerier = (*Queries)(nil) I'm not sure what if we'd need a constructor, as you can just cast an existing Queries instance to a ReadOnlyQuerier. var reader ReadOnlyQuerier
reader = New(db) Thoughts? |
Beta Was this translation helpful? Give feedback.
Hey, thanks for the suggestion. This is a really good idea.
I think we can get away with generating a read-only interface instead of a different struct. Here's a simple example, given the following two input files:
Today, this generates the following four Go files: