Releases: microbus-io/sequel
Removed FOR_UPDATE() virtual function
Add parens around virtual function expansions
Bug fix: Added parenthesis around expressions generated through virtual functions. NOW_UTC() did not work as a DEFAULT value in a CREATE TABLE statement in SQLite.
Fix nested virtual function expansion
Bug fixed: Nested virtual functions were not expanded correctly in certain situations.
SQLite in-memory test databases and FOR_UPDATE
Fix: SQLite in-memory test databases
- Changed the default SQLite data source from
:memory:tofile:?mode=memory&cache=sharedso that multiple connections share the same in-memory database. - Removed the special-case that skipped
setDatabaseInDataSourceNamefor SQLite; it now follows the same code path as other drivers. - Updated the comment to clarify that SQLite in-memory databases are automatically freed when all connections close.
New: FOR_UPDATE() virtual function
Added a FOR_UPDATE() virtual function for portable row-level locking in SELECT statements.
SELECT * FROM users WHERE id=? FOR_UPDATE()Expands per driver:
- MySQL / PostgreSQL —
FOR UPDATE - MSSQL —
WITH (UPDLOCK, ROWLOCK) - SQLite — empty string (transaction-level locking is sufficient)
SQLite Driver Support
SQLite Driver Support
Sequel now supports SQLite via the pure-Go modernc.org/sqlite driver. No CGo or external libraries required.
Opening a connection
db, err := sequel.Open("sqlite", "file:path/to/database.db")
db, err := sequel.Open("sqlite", ":memory:")
db, err := sequel.Open("", "file:mydata.db") // driver inferred automaticallyThe driver name "sqlite3" is accepted as an alias for "sqlite".
Testing
SQLite is now the default driver for OpenTesting when no driver or DSN is provided. Tests use in-memory databases for speed and zero cleanup:
db, err := sequel.OpenTesting("", "", t.Name()) // SQLite in-memoryVirtual function support
All built-in virtual functions expand to SQLite-compatible SQL:
| Function | SQLite expansion |
|---|---|
NOW_UTC() |
STRFTIME('%Y-%m-%d %H:%M:%f', 'now') |
DATE_ADD_MILLIS(base, ms) |
STRFTIME('%Y-%m-%d %H:%M:%f', base, '+' || (ms/1000.0) || ' seconds') |
DATE_DIFF_MILLIS(a, b) |
(JULIANDAY(a) - JULIANDAY(b)) * 86400000.0 |
LIMIT_OFFSET(l, o) |
LIMIT l OFFSET o |
REGEXP_TEXT_SEARCH(? IN cols) |
cols LIKE ('%' || ? || '%') |
Note: REGEXP_TEXT_SEARCH uses LIKE for substring matching since SQLite does not have built-in regex support. This is case-insensitive for ASCII but not a true regular expression match.
Other details
- Placeholders:
?(same as MySQL and SQL Server — no conversion needed) - InsertReturnID: Uses
LastInsertId()(same mechanism as MySQL) - Schema migration: Fully supported with SQLite-native datetime functions and
INSERT OR IGNOREfor concurrency safety - Driver-specific migration files: Use
-- DRIVER: sqlitecomments
Internal improvements
- Removed hardcoded
$1, $2PostgreSQL placeholders from migration queries — these now use?and rely on automatic placeholder conversion like all other query paths - Replaced driver-specific datetime expressions in migration logic with
NOW_UTC()andDATE_ADD_MILLIS()virtual functions, collapsing many four-way switch statements into shared code - Default
OpenTestingdriver changed from MySQL to SQLite for zero-configuration testing
Executor interface
The Executor interface are the shared methods in DB and Tx
Extend sql.Tx
This release extend sql.Tx to enable virtual functions and placeholder replacement as well as with InsertReturnID.
InsertReturnID
InsertReturnID executes an INSERT statement and returns the auto-generated ID for the named ID column
Cross-Driver Support and Virtual Functions
Cross-Driver Support
Automatic Placeholder Conversion
All query methods (Exec, Query, QueryRow, Prepare, and their Context variants) now automatically convert ? placeholders to the driver's native syntax. For PostgreSQL, ? becomes $1, $2, etc. Placeholders inside quoted strings are left untouched.
Virtual Functions
Virtual functions are driver-agnostic function calls in SQL that Sequel expands into driver-specific expressions before execution. They are matched case-insensitively and support nesting.
Built-in virtual functions:
NOW_UTC()- current UTC timestamp with millisecond precisionREGEXP_TEXT_SEARCH(expr IN col1, col2, ...)- case-insensitive regex search across columnsDATE_ADD_MILLIS(baseExpr, milliseconds)- add milliseconds to a timestampDATE_DIFF_MILLIS(a, b)- difference between two timestamps in millisecondsLIMIT_OFFSET(limit, offset)- cross-driver pagination
Custom virtual functions can be registered with the package-level RegisterVirtualFunc:
sequel.RegisterVirtualFunc("BOOL", func(driverName string, args string) (string, error) {
switch driverName {
case "mysql", "pgx":
return args, nil
case "mssql":
return "CAST(" + args + " AS BIT)", nil
default:
return "", errors.New("unsupported driver: %s", driverName)
}
})New Public API
sequel.RegisterVirtualFunc(name, handler)- register custom virtual functions globallydb.UnpackQuery(query)- expand virtual functions and conform placeholders (called automatically by query methods)
Deprecated
db.ConformArgPlaceholders()- placeholder conversion is now automaticdb.NowUTC()- use theNOW_UTC()virtual function insteaddb.RegexpTextSearch()- use theREGEXP_TEXT_SEARCH()virtual function instead
Move code generation from Sequel to Fabric
Code generation is now done by a coding agent using the instructions in the Fabric's .claude directory. The legacy deterministic code generator is removed. It's now as simple as:
HEY CLAUDE
Create a new microservice to persist books in a SQL database