Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extensibility / Plugin Support #288

Closed
mightyguava opened this issue Jan 22, 2020 · 3 comments · Fixed by #1578
Closed

Extensibility / Plugin Support #288

mightyguava opened this issue Jan 22, 2020 · 3 comments · Fixed by #1578
Labels
future In the year 3000...

Comments

@mightyguava
Copy link
Contributor

mightyguava commented Jan 22, 2020

Add support for calling into plugins during code generation. One specific use case I have is to support automatic encryption/decryption of columns. To illustrate, here's very naive proposal:

CREATE TABLE authors (
          id   BIGSERIAL PRIMARY KEY,
          name text      NOT NULL, -- :encrypted
          bio  text -- :encrypted
);

Adding the :encrypted annotation and having the encryption plugin enabled would cause sqlc to augment the generated code to look like the below:

// db.go
interface Cipher {
  Encrypt(s string) (string, error)
  Decrypt(s string) (string, error)
}

func New(db DBTX) *Queries {
	return &Queries{db: db, enc: &NoopCipher{}}
}

func NewWithEncryptor(db DBTX, ciph Cipher) *Queries {
	return &Queries{db: db, ciph: ciph}
}

type Queries struct {
	db DBTX
	ciph Cipher
}

// query.sql.go
const createAuthor = `-- name: CreateAuthor :one
INSERT INTO authors (
          name, bio
) VALUES (
  $1, $2
)
RETURNING id, name, bio
`

type CreateAuthorParams struct {
	Name string
	Bio  sql.NullString
}

func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (Author, error) {
	var err error
	var i Author

	arg.Name, err = q.ciph.Encrypt(arg.Bio)
	if err != nil {
		return i, err
	}
	arg.Bio, err = q.ciph.Encrypt(arg.Bio)
	if err != nil {
		return i, err
	}
	row := q.db.QueryRowContext(ctx, createAuthor, arg.Name, arg.Bio)
	err := row.Scan(&i.ID, &i.Name, &i.Bio)
	if err != nil {
		return i, err
	}
	i.Name, err = q.ciph.Decrypt(i.Name)
	if err != nil {
		return i, err
	}
	i.Bio, err = q.ciph.Decrypt(i.Bio)
	if err != nil {
		return i, err
	}

	return i, err
}

The Cipher is be an interface dictated by the plugin that the user would have to provide.

This proposal leaves a lot to answer and is just an example of what we can do with plugins. sqlc having an understanding of the schema and being able to parse annotations (comments) makes it a great place to hook into this automated code for translating. I don't think this is achievable simply with a custom type, since the Cipher needs to be initialized with secret key material to perform the encryption/decryption.

I don't know how exactly you would do a plugin with sqlc. Maybe make it easy to create a custom build of the sqlc CLI, and adding support to the library for code generation hooks. Or, maybe invoking external binaries that communicate over grpc (a pattern in the Go community nowadays), but that could result in a complex protocol and you'd have to describe the AST and in-memory models in protobuf.

I think this could be a really powerful feature though. Curious what your thoughts are.

@kyleconroy kyleconroy added the future In the year 3000... label Jan 24, 2020
@kyleconroy
Copy link
Collaborator

I think this could be a really powerful feature though. Curious what your thoughts are.

sqlc isn't at a place where plugins or extensions make sense. Long term, I'd like to have first-class support for extending sqlc to do cool things like encryption. Right now the internals are under heavy development and we haven't even decided on the Go interface between code generation, query parsing, and type checking.

@kyleconroy
Copy link
Collaborator

👀 #1414 👀

@kyleconroy
Copy link
Collaborator

#1684

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
future In the year 3000...
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants