Skip to content
Ahmad Faiz Kamaludin edited this page Oct 18, 2026 · 3 revisions

General

Is quill an ORM?

No. There are no models, relations, lifecycle hooks or migrations. You write queries with a fluent API and get rows back into your own structs.

Which databases are supported?

PostgreSQL, MySQL, MariaDB and SQLite. Tested on PostgreSQL 16, MySQL 8.4, MariaDB 11.4 and SQLite (pure-Go driver). SQL Server is not supported. See Dialect Differences.

Does it work without a database connection?

Yes. db.Use(dialect) only builds SQL. Use ToSQL() and run the result with any driver you like.

Is it production ready?

quill is pre-1.0 (v0.2.0). The API changed between 0.1 and 0.2 and may change again before 1.0. Pin the version and read the changelog before you upgrade.

Can I use it with *sql.Tx or *sql.Conn?

Yes. db.New accepts anything with QueryContext and ExecContext. A db.New(sqlTx, dialect) built on an existing *sql.Tx uses savepoints for nested Transaction calls.

Compile and usage problems

My code says db.Select undefined or conn.Select undefined

You have a local variable called db that hides the package. Rename the variable to conn.

I get db.ErrNotBound

You created the DB with db.Use, which has no connection. Use db.New(sqlDB, dialect) to run queries.

I get db.ErrNoWhere on an update or delete

The statement has no WHERE and no LIMIT. This guard prevents wiping a table by accident. Add a filter. If you really mean every row, call AllRows(). See the WHERE guard.

I get db.ErrNoTable

The select has no source. Call From, FromSub or FromRaw.

I get db.ErrInvalidArgs from db.From[T]

T has no TableName() method, or it returns "". quill never guesses a table name. See Typed API.

ErrNoValues when I update with SetRow

Every field was omitted. A field tagged omitempty is skipped while it holds its zero value. See the omitempty trap.

My false / 0 / "" is not written

Same cause: omitempty skips zero values. Use a pointer field, or Set / SetMap.

ErrUnsupported

The feature does not exist on the chosen dialect (for example Returning on MySQL, lateral joins on SQLite). Check the dialect table for the alternative.

ErrInvalidOperator

The operator is not in the allow-list. If it came from user input, that is the guard doing its job.

Where did the error from my chain go?

Builders collect errors and return the first one from the terminal call (ToSQL, Get, Exec, ...). Call q.Err() to look earlier.

Behaviour questions

Why does Count over a grouped query fail on PostgreSQL or MySQL?

Strict servers need the grouped column to be selected: conn.Select("user_id").From("posts").GroupBy("user_id").Count(ctx). See Reading Data.

Why does MySQL report 0 rows affected for an update?

MySQL counts changed rows. Updating a row to the value it already has reports 0. Upserts report 2 per updated row. See Dialect Differences.

Why is WhereIn with an empty slice not an error?

WhereIn with an empty slice compiles to 0 = 1 (matches nothing) and WhereNotIn to 1 = 1, so an empty filter list never produces invalid SQL.

Why does my HAVING with an alias fail on PostgreSQL?

PostgreSQL does not allow output aliases in HAVING. Use HavingRaw("sum(total) > ?", 100).

Why did Chunk skip or repeat rows?

Chunk pages by offset. If your callback changes rows that match the filter, offsets shift. Use ChunkByID.

Chunk returns ErrNoOrder

Chunk needs an ORDER BY. Add OrderBy("id") or use ChunkByID.

My program hangs inside an Each or Stream loop (SQLite)

On a single-connection pool such as in-memory SQLite, the open cursor holds the only connection. Do not run another query inside the loop. Collect ids first, or use a pool with more connections.

Is a builder safe to share between goroutines?

No. Build per request, or Clone() a shared base before each use. See Core Concepts.

My nullable column makes the scan fail

It should not. NULL into a plain field leaves the zero value. Use a pointer or sql.Null* if you need to tell NULL apart. See Reading Data.

How do I see the SQL that will run?

q.ToSQL() returns the SQL and arguments. q.ToRawSQL() interpolates values for debugging only: never execute or log it in production.

Can the cursor token be trusted?

It is safe from SQL injection (it is only bound as an argument) but it is not signed or encrypted and reveals the last key. See Security.

Still stuck?

  • Check the Cookbook for a similar task.
  • Run go doc github.com/akfaiz/quill/db <Name> for the doc comment.
  • Search or open an issue: https://github.com/akfaiz/quill/issues. When reporting, include the dialect, quill version, and the output of ToSQL(). Do not paste real customer data or credentials.

Clone this wiki locally