Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Option to output additional read only client #694

Discussion options

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.

You must be logged in to vote

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 (
	

Replies: 1 comment 1 reply

Comment options

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 querier.go would have the following interface added to it:

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?

You must be logged in to vote
1 reply
Comment options

This is exactly what I was hoping to be able to do. And I agree with the point of not having a different constructor. Since it's an interface, we just make sure that anywhere we know we only need a read-only client, we just pass in the ReadOnlyQuerier interface.

Answer selected by abatilo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Converted from issue

This discussion was converted from issue #694 on August 29, 2020 16:20.

AltStyle によって変換されたページ (->オリジナル) /