Problem
StatementBuilder.InsertRecord and InsertRecords reject records that produce zero columns from Map (e.g. a struct where every field is tagged ,omitempty or ,omitzero and every value is the type's zero). The rejection is a build-time error suggesting sq.Expr("INSERT INTO ... DEFAULT VALUES") as the workaround, but that bypasses the type-safe builder.
The "every column wants its DB default" use case is legitimate (think a row identified only by an INSERT ... RETURNING id flow with all-defaulted columns). The right fix is to emit INSERT INTO <table> DEFAULT VALUES natively.
Why not in #50
The omitzero PR rejects empty-column records with a clear error pointing at sq.Expr as the escape hatch. Supporting DEFAULT VALUES natively requires more invasive work:
- Squirrel's
sq.InsertBuilder doesn't support DEFAULT VALUES natively; calling .ToSql() on an Insert without Columns/Values errors with "insert statements must have at least one set of values or select clause".
- pgkit's
InsertBuilder embeds sq.InsertBuilder, so the chain methods (.Suffix(...), .ToSql(), etc.) inherit from squirrel.
- A proper fix needs either:
- An override path on pgkit's
InsertBuilder.ToSql that swaps to a raw sq.Expr(...) when cols is empty, plus matching override for .Suffix(...) so RETURNING clauses still compose, or
- A separate constructor (
InsertRecordDefaults?) that returns a different shape and skips the squirrel embed entirely.
Either path expands pgkit's API surface beyond what the omitzero PR justified.
Proposed work
- Decide between the override path (option 1 above) or a separate constructor (option 2). Lean override — keeps the InsertBuilder return type stable, callers don't have to branch.
- Implement, with
.Suffix(...) composition working for RETURNING.
- Drop the
len(cols) == 0 rejection in builder.go and replace with the native path.
- Add round-trip tests against PostgreSQL exercising an all-default INSERT + RETURNING flow.
Repro
type AllDefaults struct {
ID int64 `db:"id,omitempty"`
Tags []string `db:"tags,omitzero"`
}
sb.InsertRecord(&AllDefaults{}, "items") // errors: "Map returned no columns..."
Tests catching the current behavior: TestInsertRecord_EmptyColumnsRejected, TestInsertRecords_EmptyColumnsRejected (added in #50).
Problem
StatementBuilder.InsertRecordandInsertRecordsreject records that produce zero columns fromMap(e.g. a struct where every field is tagged,omitemptyor,omitzeroand every value is the type's zero). The rejection is a build-time error suggestingsq.Expr("INSERT INTO ... DEFAULT VALUES")as the workaround, but that bypasses the type-safe builder.The "every column wants its DB default" use case is legitimate (think a row identified only by an
INSERT ... RETURNING idflow with all-defaulted columns). The right fix is to emitINSERT INTO <table> DEFAULT VALUESnatively.Why not in #50
The omitzero PR rejects empty-column records with a clear error pointing at
sq.Expras the escape hatch. SupportingDEFAULT VALUESnatively requires more invasive work:sq.InsertBuilderdoesn't supportDEFAULT VALUESnatively; calling.ToSql()on an Insert without Columns/Values errors with"insert statements must have at least one set of values or select clause".InsertBuilderembedssq.InsertBuilder, so the chain methods (.Suffix(...),.ToSql(), etc.) inherit from squirrel.InsertBuilder.ToSqlthat swaps to a rawsq.Expr(...)when cols is empty, plus matching override for.Suffix(...)so RETURNING clauses still compose, orInsertRecordDefaults?) that returns a different shape and skips the squirrel embed entirely.Either path expands pgkit's API surface beyond what the omitzero PR justified.
Proposed work
.Suffix(...)composition working forRETURNING.len(cols) == 0rejection inbuilder.goand replace with the native path.Repro
Tests catching the current behavior:
TestInsertRecord_EmptyColumnsRejected,TestInsertRecords_EmptyColumnsRejected(added in #50).