.section Libraries
.topic sql.overview
.title dvxSql -- SQL Database Interface
.toc 0 dvxSql -- SQL Database Interface
.default
.index dvxSql
.index SQL
.index SQLite
.index Database

.h2 dvxSql -- SQL Database Interface

High-level wrapper around SQLite3 for DVX applications. Manages database connections and result set cursors via integer handles so BASIC code never touches raw pointers. All handles are 1-based; 0 indicates an error or invalid handle.

Header: sql/dvxSql.h

.h3 Handle Model

Database and cursor handles are int32_t values. A successful open or query returns a handle greater than zero. Handle 0 is reserved as the invalid/error sentinel. Closing a database automatically finalizes all cursors that belong to it.

.link sql.db Database Operations
.link sql.cursor Cursor Operations
.link sql.utility Utility Functions
.link sql.example Example

.topic sql.db
.title Database Operations
.toc 1 Database Operations
.index dvxSqlOpen
.index dvxSqlClose
.index dvxSqlExec
.index dvxSqlError
.index dvxSqlAffectedRows

.h2 Database Operations

.h3 dvxSqlOpen

.code
int32_t dvxSqlOpen(const char *path);
.endcode

Open a SQLite database file. Creates the file if it does not exist.

.table
  Parameter    Description
  ---------    -----------
  path         Path to the database file.
.endtable

Returns a database handle greater than 0 on success, or 0 on error (null path, open failure, or no free slots).

.h3 dvxSqlClose

.code
void dvxSqlClose(int32_t db);
.endcode

Close a database and free all associated resources. Any open cursors belonging to this database are automatically finalized.

.table
  Parameter    Description
  ---------    -----------
  db           Database handle returned by dvxSqlOpen.
.endtable

Safe to call with an invalid handle (no-op).

.h3 dvxSqlExec

.code
bool dvxSqlExec(int32_t db, const char *sql);
.endcode

Execute one or more SQL statements that return no result rows. Suitable for DDL (CREATE TABLE, DROP TABLE, etc.) and DML (INSERT, UPDATE, DELETE).

.table
  Parameter    Description
  ---------    -----------
  db           Database handle.
  sql          SQL statement(s) to execute.
.endtable

Returns true on success, false on error. On failure, the error message is available via dvxSqlError. On success, the affected row count is available via dvxSqlAffectedRows.

.h3 dvxSqlError

.code
const char *dvxSqlError(int32_t db);
.endcode

Return the last error message for a database handle. The returned string is stored internally and valid until the next operation on the same handle.

.table
  Parameter    Description
  ---------    -----------
  db           Database handle.
.endtable

Returns the error string, or "Invalid database handle" if the handle is invalid.

.h3 dvxSqlAffectedRows

.code
int32_t dvxSqlAffectedRows(int32_t db);
.endcode

Return the number of rows inserted, updated, or deleted by the last dvxSqlExec call on this handle.

.table
  Parameter    Description
  ---------    -----------
  db           Database handle.
.endtable

Returns the row count, or 0 if the handle is invalid.

.topic sql.cursor
.title Cursor Operations
.toc 1 Cursor Operations
.index dvxSqlQuery
.index dvxSqlNext
.index dvxSqlEof
.index dvxSqlFieldCount
.index dvxSqlFieldName
.index dvxSqlFieldText
.index dvxSqlFieldByName
.index dvxSqlFieldInt
.index dvxSqlFieldDbl
.index dvxSqlFreeResult

.h2 Cursor Operations

Result set cursors are created by dvxSqlQuery and must be freed with dvxSqlFreeResult when no longer needed. A new cursor is positioned before the first row; call dvxSqlNext to advance to the first row before reading field values.

.h3 dvxSqlQuery

.code
int32_t dvxSqlQuery(int32_t db, const char *sql);
.endcode

Execute a SELECT query and return a cursor handle for iterating the results. The cursor is positioned before the first row.

.table
  Parameter    Description
  ---------    -----------
  db           Database handle.
  sql          SQL SELECT statement.
.endtable

Returns a cursor handle greater than 0 on success, or 0 on error (invalid handle, null SQL, SQL syntax error, or no free cursor slots). On failure, the error message is available via dvxSqlError on the database handle.

.h3 dvxSqlNext

.code
bool dvxSqlNext(int32_t rs);
.endcode

Advance the cursor to the next row.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle returned by dvxSqlQuery.
.endtable

Returns true if a row is now available for reading, false if the cursor has reached the end or the handle is invalid.

.h3 dvxSqlEof

.code
bool dvxSqlEof(int32_t rs);
.endcode

Test whether the cursor is past the last row.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
.endtable

Returns true if the cursor is exhausted or the handle is invalid, false otherwise.

.h3 dvxSqlFieldCount

.code
int32_t dvxSqlFieldCount(int32_t rs);
.endcode

Return the number of columns in the result set.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
.endtable

Returns the column count, or 0 if the handle is invalid.

.h3 dvxSqlFieldName

.code
const char *dvxSqlFieldName(int32_t rs, int32_t col);
.endcode

Return the name of a column by index.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
  col          Column index (0-based).
.endtable

Returns the column name, or "" if the handle or index is invalid.

.h3 dvxSqlFieldText

.code
const char *dvxSqlFieldText(int32_t rs, int32_t col);
.endcode

Return the value of a column as a string.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
  col          Column index (0-based).
.endtable

Returns the text value, or "" if the handle or index is invalid or the value is NULL.

.h3 dvxSqlFieldByName

.code
const char *dvxSqlFieldByName(int32_t rs, const char *name);
.endcode

Return the value of a column identified by name as a string. The name match is case-insensitive.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
  name         Column name (case-insensitive).
.endtable

Returns the text value, or "" if the handle is invalid, the name is NULL, or no column with that name exists.

.h3 dvxSqlFieldInt

.code
int32_t dvxSqlFieldInt(int32_t rs, int32_t col);
.endcode

Return the value of a column as a 32-bit integer.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
  col          Column index (0-based).
.endtable

Returns the integer value, or 0 if the handle or index is invalid.

.h3 dvxSqlFieldDbl

.code
double dvxSqlFieldDbl(int32_t rs, int32_t col);
.endcode

Return the value of a column as a double-precision floating point number.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
  col          Column index (0-based).
.endtable

Returns the double value, or 0.0 if the handle or index is invalid.

.h3 dvxSqlFreeResult

.code
void dvxSqlFreeResult(int32_t rs);
.endcode

Close a result set cursor and free its resources. Must be called for every cursor returned by dvxSqlQuery.

.table
  Parameter    Description
  ---------    -----------
  rs           Cursor handle.
.endtable

Safe to call with an invalid handle (no-op).

.topic sql.utility
.title Utility Functions
.toc 1 Utility Functions
.index dvxSqlEscape

.h2 Utility Functions

.h3 dvxSqlEscape

.code
int32_t dvxSqlEscape(const char *src, char *dst, int32_t dstSize);
.endcode

Escape a string for safe inclusion in SQL string literals. Doubles single quotes so that O'Brien becomes O''Brien.

.table
  Parameter    Description
  ---------    -----------
  src          Source string to escape.
  dst          Destination buffer for the escaped string.
  dstSize      Size of the destination buffer in bytes.
.endtable

Returns the length of the escaped string (not including the null terminator), or -1 if the buffer was too small or any parameter is NULL/invalid.

.topic sql.example
.title Example Usage
.toc 1 Example Usage
.index SQL Example

.h2 Example Usage

.h3 Creating a Table and Inserting Data

.code
int32_t db = dvxSqlOpen("mydata.db");
if (!db) {
    // handle error
}

dvxSqlExec(db, "CREATE TABLE IF NOT EXISTS contacts ("
               "id INTEGER PRIMARY KEY, "
               "name TEXT, "
               "phone TEXT)");

dvxSqlExec(db, "INSERT INTO contacts (name, phone) "
               "VALUES ('Alice', '555-0100')");
.endcode

.h3 Querying and Iterating Results

.code
int32_t rs = dvxSqlQuery(db, "SELECT id, name, phone FROM contacts");
if (!rs) {
    printf("Error: %s\n", dvxSqlError(db));
}

while (dvxSqlNext(rs)) {
    int32_t     id    = dvxSqlFieldInt(rs, 0);
    const char *name  = dvxSqlFieldText(rs, 1);
    const char *phone = dvxSqlFieldText(rs, 2);
    printf("%d: %s -- %s\n", id, name, phone);
}

dvxSqlFreeResult(rs);
dvxSqlClose(db);
.endcode

.h3 Escaping User Input

.code
char escaped[512];
dvxSqlEscape(userInput, escaped, sizeof(escaped));

char sql[1024];
snprintf(sql, sizeof(sql),
         "INSERT INTO notes (text) VALUES ('%s')", escaped);
dvxSqlExec(db, sql);
.endcode
