SQLiteLib — SQLite Database Library

Comprehensive SQLite database support for Plan9Basic. Create, query, and manage databases with JSON integration, transaction support, and table introspection. Depends on JsonLib. 55+ functions.

CategoryCountDescription
Error Handling4sql_error, sql_errormsg$, sql_strerror$, sql_clearerror
Connection6sql_open# (2 overloads), sql_close, sql_isopen, sql_path$, sql_version$
SQL Execution4sql_exec (2 overloads), sql_query# (2 overloads)
Statement Mgmt5sql_prepare#, sql_step, sql_reset#, sql_finalize, sql_eof
Parameter Binding5sql_bindstr#, sql_bindnum#, sql_bindnull#, sql_bindjson#, sql_clearbind#
Column Info5sql_colcount, sql_colname$, sql_coltype, sql_coltypename$, sql_colindex
Results (by Index)4sql_getstr$, sql_getnum, sql_isnull, sql_isblob
Results (by Name)3sql_gets$, sql_getn, sql_isn
JSON Integration5sql_row#, sql_fetchall#, sql_fetchone#, sql_insertjson, sql_updatejson
Transactions4sql_begin, sql_commit, sql_rollback, sql_intrans
Utility8sql_lastid, sql_changes, sql_totalchanges, sql_tableexists, sql_tables#, sql_columns#, sql_backup, sql_vacuum
String Helpers2sql_escape$, sql_quote$

Memory Management

ObjectManaged ByNotes
Database connectionsGarbage collectorAutomatically freed on scope exit
Statements / cursorsParent connectionNot in GC — freed when connection closes
JSON resultsGarbage collectorSafe to use after cursor is finalized
ⓘ Note: When a connection is freed, all its statements are automatically freed. This parent-child model prevents double-free issues.

Error Handling

CodeConstantDescription
0SQL_ERR_NONENo error
1SQL_ERR_NOT_OPENDatabase not open
2SQL_ERR_INVALID_CONNInvalid connection
3SQL_ERR_INVALID_STMTInvalid statement
4SQL_ERR_EXEC_FAILEDSQL execution failed
5SQL_ERR_QUERY_FAILEDQuery execution failed
6SQL_ERR_PREPARE_FAILStatement preparation failed
7SQL_ERR_BIND_FAILEDParameter binding failed
8SQL_ERR_STEP_FAILEDStep execution failed
9SQL_ERR_COLUMN_INDEXColumn index out of bounds
10SQL_ERR_COLUMN_NAMEColumn not found
11SQL_ERR_TRANSACTIONTransaction error
12SQL_ERR_FILE_ERRORFile operation error
13SQL_ERR_JSON_INVALIDInvalid JSON data
14SQL_ERR_TABLE_ERRORTable operation error
15SQL_ERR_BACKUP_FAILEDDatabase backup failed
FunctionSignatureDescription
sql_error()sql_error@Last error code (0–15)
sql_errormsg$()sql_errormsg$@Last error message
sql_strerror$(code)sql_strerror$@nDescription for error code
sql_clearerror()sql_clearerror@Clear error state
CodeColumn Type
0NULL
1INTEGER
2FLOAT
3TEXT
4BLOB
CodeStep Result
1SQL_STEP_ROW — row available
0SQL_STEP_DONE — no more rows
-1SQL_STEP_ERROR — error

Connection Management

FunctionSignatureDescription
sql_open#(path$)sql_open#@$Open/create database file
sql_open#()sql_open#@Open in-memory database
sql_close(db#)sql_close@#Close connection
sql_isopen(db#)sql_isopen@#Database open? (1/0)
sql_path$(db#)sql_path$@#Database file path
sql_version$()sql_version$@SQLite version string
╯ plan9basic
let db# = sql_open#("mydata.db")
if PntToNum(db#) = 0 then
    println "Failed: " + sql_errormsg$()
endif

println "SQLite: " + sql_version$()

' In-memory database
let memdb# = sql_open#()

sql_close(db#)

SQL Execution

FunctionSignatureDescription
sql_exec(db#, sql$)sql_exec@#$Execute statement (INSERT, UPDATE, DELETE, CREATE, etc.)
sql_exec(db#, sql$, params#)sql_exec@#$#Execute with JSON parameter binding
sql_query#(db#, sql$)sql_query#@#$Execute SELECT, returns cursor
sql_query#(db#, sql$, params#)sql_query#@#$#SELECT with JSON parameters
╯ plan9basic
' Create table
sql_exec(db#, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")

' Insert with parameters
let params# = json_object#()
json_sets#(params#, "name", "John")
json_setn#(params#, "age", 30)
sql_exec(db#, "INSERT INTO users (name, age) VALUES (:name, :age)", params#)

' Query
let cursor# = sql_query#(db#, "SELECT * FROM users")
while sql_step(cursor#) = 1
    println sql_gets$(cursor#, "name")
end while
sql_finalize(cursor#)
⚠ Warning: Always use parameter binding (:name or ?) instead of string concatenation to prevent SQL injection.

Statement Management

FunctionSignatureDescription
sql_prepare#(db#, sql$)sql_prepare#@#$Prepare statement for repeated execution
sql_step(cursor#)sql_step@#Execute one step: 1=row, 0=done, -1=error
sql_reset#(stmt#)sql_reset#@#Reset for re-execution with new parameters
sql_finalize(cursor#)sql_finalize@#Finalize (free) statement
sql_eof(cursor#)sql_eof@#End of results? (1/0)

Parameter Binding

FunctionSignatureDescription
sql_bindstr#(stmt#, idx, val$)sql_bindstr#@#n$Bind string by index (0-based)
sql_bindnum#(stmt#, idx, val)sql_bindnum#@#nnBind number by index
sql_bindnull#(stmt#, idx)sql_bindnull#@#nBind NULL by index
sql_bindjson#(stmt#, params#)sql_bindjson#@##Bind all from JSON object (by :name)
sql_clearbind#(stmt#)sql_clearbind#@#Clear all bindings
╯ plan9basic
let stmt# = sql_prepare#(db#, "INSERT INTO users (name, age) VALUES (:name, :age)")

let params# = json_object#()
json_sets#(params#, "name", "Alice")
json_setn#(params#, "age", 25)
sql_bindjson#(stmt#, params#)
sql_step(stmt#)

' Reuse with new data
sql_reset#(stmt#)
sql_clearbind#(stmt#)
json_sets#(params#, "name", "Bob")
json_setn#(params#, "age", 35)
sql_bindjson#(stmt#, params#)
sql_step(stmt#)

sql_finalize(stmt#)

Column Information

FunctionSignatureDescription
sql_colcount(cursor#)sql_colcount@#Number of columns in result set
sql_colname$(cursor#, idx)sql_colname$@#nColumn name at index (0-based)
sql_coltype(cursor#, idx)sql_coltype@#nColumn type code at index
sql_coltypename$(code)sql_coltypename$@nType name for code (NULL/INTEGER/FLOAT/TEXT/BLOB)
sql_colindex(cursor#, name$)sql_colindex@#$Column index by name (-1 if not found)

Result Access

By Index (0-based)

FunctionSignatureDescription
sql_getstr$(cursor#, idx)sql_getstr$@#nString value at column index
sql_getnum(cursor#, idx)sql_getnum@#nNumeric value at column index
sql_isnull(cursor#, idx)sql_isnull@#nColumn is NULL? (1/0)
sql_isblob(cursor#, idx)sql_isblob@#nColumn is BLOB? (1/0)

By Name

FunctionSignatureDescription
sql_gets$(cursor#, name$)sql_gets$@#$String value by column name
sql_getn(cursor#, name$)sql_getn@#$Numeric value by column name
sql_isn(cursor#, name$)sql_isn@#$Column is NULL? (1/0)
╯ plan9basic
let cursor# = sql_query#(db#, "SELECT * FROM users")
while sql_step(cursor#) = 1
    println sql_gets$(cursor#, "name") + " - age " + str$(sql_getn(cursor#, "age"))
    if sql_isn(cursor#, "email") = 1 then
        println "  (no email)"
    endif
end while
sql_finalize(cursor#)

JSON Integration

FunctionSignatureDescription
sql_row#(cursor#)sql_row#@#Current row as JSON object
sql_fetchall#(cursor#)sql_fetchall#@#All remaining rows as JSON array
sql_fetchone#(cursor#)sql_fetchone#@#Current row as JSON, advance to next (nil when done)
sql_insertjson(db#, table$, json#)sql_insertjson@#$#Insert JSON object as new row (1/0)
sql_updatejson(db#, table$, json#, where$)sql_updatejson@#$#$Update rows with JSON SET values (1/0)
╯ plan9basic
' Insert with JSON
let user# = json_object#()
json_sets#(user#, "name", "Charlie")
json_setn#(user#, "age", 28)
sql_insertjson(db#, "users", user#)
println "New ID: " + str$(sql_lastid(db#))

' Update with JSON
let upd# = json_object#()
json_setn#(upd#, "age", 29)
sql_updatejson(db#, "users", upd#, "name = 'Charlie'")

' Fetch all as JSON array
let cursor# = sql_query#(db#, "SELECT * FROM users")
sql_step(cursor#)
let all# = sql_fetchall#(cursor#)
println json_pretty$(all#)
sql_finalize(cursor#)

Transactions

FunctionSignatureDescription
sql_begin(db#)sql_begin@#Begin transaction (1/0)
sql_commit(db#)sql_commit@#Commit transaction (1/0)
sql_rollback(db#)sql_rollback@#Rollback transaction (1/0)
sql_intrans(db#)sql_intrans@#In transaction? (1/0)
╯ transaction.bas
sql_begin(db#)

let ok = sql_exec(db#, "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if ok = 1 then
    ok = sql_exec(db#, "UPDATE accounts SET balance = balance + 100 WHERE id = 2")
endif

if ok = 1 then
    sql_commit(db#)
    println "Transfer complete"
else
    sql_rollback(db#)
    println "Transfer failed"
endif

Utility Functions

FunctionSignatureDescription
sql_lastid(db#)sql_lastid@#Last inserted rowid
sql_changes(db#)sql_changes@#Rows affected by last statement
sql_totalchanges(db#)sql_totalchanges@#Total changes since connection opened
sql_tableexists(db#, table$)sql_tableexists@#$Table exists? (1/0)
sql_tables#(db#)sql_tables#@#JSON array of all table names
sql_columns#(db#, table$)sql_columns#@#$JSON array of column info for table
sql_backup(db#, path$)sql_backup@#$Backup database to file (1/0)
sql_vacuum(db#)sql_vacuum@#Optimize/rebuild database
╯ introspection.bas
' Table introspection
let tables# = sql_tables#(db#)
for i = 0 to json_len(tables#) - 1
    let tbl$ = json_items$(tables#, i)
    println "Table: " + tbl$
    
    let cols# = sql_columns#(db#, tbl$)
    for j = 0 to json_len(cols#) - 1
        let col# = json_item#(cols#, j)
        println "  " + json_gets$(col#, "name") + " (" + json_gets$(col#, "type") + ")"
    next
next
ⓘ Note: sql_columns#() returns objects with fields: cid, name, type, notnull, pk, default.

String Helpers

FunctionSignatureDescription
sql_escape$(text$)sql_escape$@$Escape string for SQL (doubles single quotes)
sql_quote$(text$)sql_quote$@$Escape and wrap in single quotes
╯ plan9basic
let safe$ = sql_escape$("O'Brien")   ' "O''Brien"
let quoted$ = sql_quote$("O'Brien") ' "'O''Brien'"

Complete Examples

Basic CRUD

╯ crud.bas
let db# = sql_open#("contacts.db")
sql_exec(db#, "CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY, name TEXT, phone TEXT, email TEXT)")

' Insert via JSON
let c1# = json_object#()
json_sets#(c1#, "name", "Alice Smith")
json_sets#(c1#, "phone", "555-0100")
json_sets#(c1#, "email", "alice@example.com")
sql_insertjson(db#, "contacts", c1#)

' Query
let cursor# = sql_query#(db#, "SELECT * FROM contacts ORDER BY name")
while sql_step(cursor#) = 1
    println sql_gets$(cursor#, "name") + " - " + sql_gets$(cursor#, "phone")
end while
sql_finalize(cursor#)

' Update
let upd# = json_object#()
json_sets#(upd#, "email", "alice.smith@example.com")
sql_updatejson(db#, "contacts", upd#, "name = 'Alice Smith'")

sql_close(db#)

Batch Insert with JSON Array

╯ batch_insert.bas
let db# = sql_open#("products.db")
sql_exec(db#, "CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, price REAL, stock INTEGER)")

let products# = json_array#()
let p1# = json_object#()
json_sets#(p1#, "name", "Widget")
json_setn#(p1#, "price", 9.99)
json_setn#(p1#, "stock", 100)
json_push#(products#, p1#)

let p2# = json_object#()
json_sets#(p2#, "name", "Gadget")
json_setn#(p2#, "price", 19.99)
json_setn#(p2#, "stock", 50)
json_push#(products#, p2#)

sql_begin(db#)
for i = 0 to json_len(products#) - 1
    sql_insertjson(db#, "products", json_item#(products#, i))
next
sql_commit(db#)

println "Inserted " + str$(json_len(products#)) + " products"
sql_close(db#)

Best Practices

PracticeWhy
Check errors after open/exec/queryOperations can fail silently
Use transactions for batch operationsAtomicity + performance (much faster)
Use parameter binding, not string concatPrevents SQL injection
Finalize cursors when doneFrees resources held by the statement
Use JSON functions for multi-field opsCleaner code, automatic type mapping
Backup regularly with sql_backupProtect against data loss

Quick Reference

FunctionSignatureDescription
sql_error/errormsg$/strerror$/clearerrorvariousError handling (4)
sql_open#(path$) / sql_open#()sql_open#@$ / @Open file / in-memory DB
sql_close/isopen/path$/version$variousConnection info (4)
sql_exec(db#, sql$[, params#])sql_exec@#$[#]Execute non-query (1/0)
sql_query#(db#, sql$[, params#])sql_query#@#$[#]Execute SELECT (cursor)
sql_prepare#/step/reset#/finalize/eofvariousStatement lifecycle (5)
sql_bindstr#/bindnum#/bindnull#/bindjson#/clearbind#variousBinding (5)
sql_colcount/colname$/coltype/coltypename$/colindexvariousColumn info (5)
sql_getstr$/getnum/isnull/isblobvariousResults by index (4)
sql_gets$/getn/isnvariousResults by name (3)
sql_row#/fetchall#/fetchone#/insertjson/updatejsonvariousJSON integration (5)
sql_begin/commit/rollback/intransvariousTransactions (4)
sql_lastid/changes/totalchangesvariousRow info (3)
sql_tableexists/tables#/columns#/backup/vacuumvariousUtility (5)
sql_escape$/sql_quote$variousString helpers (2)

55+ functions across 12 categories. Depends on JsonLib.