sqlfunc - Stronger typing for database/sql prepared statements
sqlfunc is a Go library that simplifies the use of database/sql by binding SQL queries directly to strongly-typed Go functions. By leveraging Go generics and reflection, it provides an idiomatic and type-safe API that reduces boilerplate and minimizes common errors like incorrect column scanning or argument count mismatches.
- Strongly-Typed Query Binding: Bind
INSERT,UPDATE,DELETE, andSELECTstatements directly to Go function variables. The function signature defines the SQL parameters and the expected result types. Exec,QueryRow, andQuery: Dedicated functions for different SQL operations, ensuring that the bound functions return the appropriate results (sql.Result, individual columns, or*sql.Rows).ForEachIteration: A high-level helper for iterating over*sql.Rowsthat automatically scans columns into the arguments of a provided callback function.- Flexible
ScanFunctions: Generate reusable functions to scan a single row into pointers or return values, improving readability and reuse. - Transaction Support: Generated functions can optionally accept a
*sql.Txas an argument, allowing them to participate seamlessly in transactions. AnyAPI: Provides a more flexible, reflection-heavy API for use cases where function signatures are determined at runtime.- Performance-Oriented Registry: Includes a registry system to cache reflective implementations, designed with future code generation in mind to achieve performance comparable to manual
database/sqlcode.
import (
"context"
"database/sql"
"github.com/dolmen-go/sqlfunc"
)
// Define a function variable with the desired signature
var getPOI func(ctx context.Context, name string) (lat float64, lon float64, err error)
// Bind the SQL query to the function variable
closeStmt, err := sqlfunc.QueryRow(ctx, db,
"SELECT lat, lon FROM poi WHERE name = ?",
&getPOI,
)
if err != nil {
// handle error
}
defer closeStmt()
// Execute the query using the strongly-typed function
lat, lon, err := getPOI(ctx, "Château de Versailles")rows, err := db.QueryContext(ctx, "SELECT name, lat, lon FROM poi")
if err != nil { /* ... */ }
// Scan rows directly into callback arguments
err = sqlfunc.ForEach(rows, func(name string, lat, lon float64) {
fmt.Printf("%s: %.4f, %.4f\n", name, lat, lon)
})- Package
sqlfunc sqlfunc.ForEachsqlfunc.Querysqlfunc.QueryRowsqlfunc.Execsqlfunc.Scansqlfunc.Any.ForEachsqlfunc.Any.Querysqlfunc.Any.QueryRowsqlfunc.Any.Execsqlfunc.Any.Scan
Production ready.
Check code coverage by the testsuite.
- There is a speed/memory penalty in using the
sqlfuncwrappers (checkgo test -bench B -benchmem github.com/dolmen-go/sqlfunc). It is recommended to do your own benchmarks. However there is work in progress to add a code generator to reduce cost of runtimereflect. Check theexperiment-genbranch.
See quality reports on SonarQube.
Copyright 2026 Olivier Mengué
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.