get column values

This commit is contained in:
2026-03-01 23:44:35 -05:00
parent 738483f281
commit eb29593eb0
4 changed files with 130 additions and 5 deletions

View File

@@ -47,3 +47,27 @@ pub extern step(^stmt: any): int32
pub extern reset(^stmt: any): int32
c "kk_sqlite3_reset"
pub extern column-count(^stmt: any): int
c "kk_sqlite3_column_count"
pub extern column-type(^stmt: any, col: int32): int32
c "kk_sqlite3_column_type"
pub extern int32/integer(): int32 { c inline "SQLITE_INTEGER" }
pub extern int32/float(): int32 { c inline "SQLITE_FLOAT" }
pub extern int32/blob(): int32 { c inline "SQLITE_BLOB" }
pub extern int32/null(): int32 { c inline "SQLITE_NULL" }
pub extern int32/text(): int32 { c inline "SQLITE_TEXT" }
//TODO(zephyr): waiting on a proper bytes type in koka
// pub extern column-blob(^stmt: any, col: int32): maybe<bytes>
// c "kk_sqlite3_column_blob"
pub extern column-double(^stmt: any, col: int32): float64
c "kk_sqlite3_column_double"
pub extern column-integer(^stmt: any, col: int32): int
c "kk_sqlite3_column_integer"
pub extern column-text(^stmt: any, col: int32): string
c "kk_sqlite3_column_text"

View File

@@ -81,3 +81,37 @@ int32_t kk_sqlite3_reset(kk_box_t cref, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
return (int32_t)sqlite3_reset(stmt);
}
kk_integer_t kk_sqlite3_column_count(kk_box_t cref, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
int r = sqlite3_column_count(stmt);
return kk_integer_from_int(r, _ctx);
}
int32_t kk_sqlite3_column_type(kk_box_t cref, int32_t col, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
return (int32_t)sqlite3_column_type(stmt, (int)col);
}
double kk_sqlite3_column_double(kk_box_t cref, int32_t col, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
return sqlite3_column_double(stmt, (int)col);
}
kk_integer_t kk_sqlite3_column_integer(kk_box_t cref, int32_t col, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
sqlite3_int64 r = sqlite3_column_int64(stmt, (int)col);
return kk_integer_from_int64((int64_t)r, _ctx);
}
kk_string_t kk_sqlite3_column_text(kk_box_t cref, int32_t col, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
const unsigned char *text = sqlite3_column_text(stmt, (int)col);
if (text == NULL) {
// The column value is NULL, but we can't put that in a Koka string.
// Since a string was requested, return an empty string instead.
return kk_string_empty();
}
int len = sqlite3_column_bytes(stmt, (int)col);
return kk_string_alloc_from_qutf8n((kk_ssize_t)len, (const char *)text, _ctx);
}

View File

@@ -95,6 +95,10 @@ pub fun close(db: sqlite3): sqlite-result
abstract struct statement
cref: any
// Prepare a statement.
// The Right value contains the prepared statement and the portion of `sql`
// remaining after parsing the first statement.
// Once use is finished, the statement must be `finalize`d.
pub fun prepare(db: sqlite3, sql: sslice, persistent: bool = False, disable-vtab: bool = False, disable-log: bool = False): either<sqlite-result, (statement, sslice)>
val flags = 0.i32
.or(persistent.int32 * 0x01.i32)
@@ -106,11 +110,61 @@ pub fun prepare(db: sqlite3, sql: sslice, persistent: bool = False, disable-vtab
else
Left(r.trusted-result(capi/error-offset(db.cref)))
// Release resources associated with a statement.
// Once finalized, a statement can no longer be used.
// Every statement must be finalized.
pub fun statement/finalize(stmt: statement): sqlite-result
capi/finalize(stmt.cref).trusted-result
// Perform one evaluation step of the statement.
// If the result is `Row`, values are available.
// If the result is `Done`, the statement has finished executing, and it should
// be either `reset` or `finalize`d.
pub fun statement/step(stmt: statement): sqlite-result
capi/step(stmt.cref).trusted-result
// Reset a statement so that it can be evaluated again.
pub fun statement/reset(stmt: statement): sqlite-result
capi/reset(stmt.cref).trusted-result
// Get the number of columns available in the statement's result.
pub fun statement/column-count(stmt: statement): int
capi/column-count(stmt.cref)
// Datatype codes for query results.
pub type sqlite3-type
Integer
Float
Text
Blob
Null
// Get the result affinity of a column in the statement's result.
// The leftmost column is numbered 0.
pub fun statment/column-type(stmt: statement, col: int): sqlite3-type
val t = capi/column-type(stmt.cref, col.int32)
if t == int32/integer() then Integer
else if t == int32/float() then Float
else if t == int32/blob() then Blob
else if t == int32/text() then Text
// If the DB gives us something unimaginable, pretend it's nothing.
else Null
// Get the result value of a column as a float64.
// If the result does not have float64 affinity, it is converted.
// The leftmost column is numbered 0.
pub fun statement/float64(stmt: statement, col: int): float64
capi/column-double(stmt.cref, col.int32)
// Get the result value of a column as an integer.
// If the result does not have integer affinity, it is converted.
// The leftmust column is numbered 0.
pub fun statement/int(stmt: statement, col: int): int
capi/column-integer(stmt.cref, col.int32)
// Get the result value of a column as a string.
// If the actual result is NULL, the result is the empty string.
// Otherwise, if the result does not have text affinity, it is converted.
// The leftmost column is numbeered 0.
pub fun statement/text(stmt: statement, col: int): string
capi/column-text(stmt.cref, col.int32)

View File

@@ -5,14 +5,26 @@ import sqlite/sqlite3
// TODO(zephyr): For now, we're just doing a very basic test to estimate whether
// I'm doing Koka FFI correctly. An actual test suite would be excellent.
fun stmt(db: sqlite3, text: string)
println(text)
match db.prepare(text.slice)
tail fun do-while(f: () -> <div|e> maybe<a>): <div|e> a
match f()
Nothing -> do-while(f)
Just(r) -> r
fun stmt(db: sqlite3, sql: string)
println(sql)
match db.prepare(sql.slice)
Left(r-prep) -> println(" prepare result: " ++ r-prep.show)
Right((stmt, rest)) ->
println(" prepare remainder: " ++ rest.show)
val r-step = stmt.step
println(" step result: " ++ r-step.show)
val r-step = do-while
val r = stmt.step
match r
Row ->
for(stmt.column-count) fn(i)
println(" col " ++ i.show ++ ": " ++ stmt.text(i))
Nothing
r -> Just(r)
println(" final step result: " ++ r-step.show)
val r-fin = stmt.finalize
println(" finalize result: " ++ r-fin.show)
@@ -23,5 +35,6 @@ pub fun main()
stmt(db, "DROP TABLE IF EXISTS koka; -- reset previous test runs")
stmt(db, "CREATE TABLE koka(v TEXT NOT NULL)")
stmt(db, "INSERT INTO koka VALUES ('value inserted from koka');")
stmt(db, "SELECT * FROM koka")
val r-close = db.close
println("close result: " ++ r-close.show)