initial commit
This commit is contained in:
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# build results
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# IDE files
|
||||
.vs/
|
||||
.vshistory/
|
||||
.editorconfig
|
||||
*.user
|
||||
*.vcxproj.filters
|
||||
|
||||
# other files
|
||||
.old*/
|
||||
old/
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
*.temp
|
||||
logs/
|
||||
log/
|
||||
*.log
|
||||
1
.vscode/.gitignore
vendored
Normal file
1
.vscode/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
settings.json
|
||||
16
.vscode/c_cpp_properties.json
vendored
Normal file
16
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "all",
|
||||
"defines": [],
|
||||
"includePath": [
|
||||
"include",
|
||||
"src",
|
||||
"../tlibc/include",
|
||||
"${default}"
|
||||
],
|
||||
"cStandard": "c99"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "gdb_debug",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bin/tsqlite",
|
||||
"windows": { "program": "${workspaceFolder}/bin/tsqlite.exe" },
|
||||
"preLaunchTask": "build_exec_dbg",
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/bin",
|
||||
"externalConsole": false,
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
31
.vscode/tasks.json
vendored
Normal file
31
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build_exec_dbg",
|
||||
"detail": "build project with debug symbols",
|
||||
"type": "cppbuild",
|
||||
"command": "bash",
|
||||
"args": [
|
||||
"-c",
|
||||
"cbuild build_exec_dbg"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
"problemMatcher": ["$gcc"],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": true,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"clear": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
48
README.md
Normal file
48
README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# tsqlite
|
||||
A wrapper for [sqlite](https://sqlite.org) that uses [tlibc](https://timerix.ddns.net/git/Timerix/tlibc) for error handling.
|
||||
|
||||
## Build
|
||||
1. Clone this repository.
|
||||
```
|
||||
git clone https://timerix.ddns.net/git/Timerix/tsqlite.git
|
||||
```
|
||||
|
||||
2. Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild/releases).
|
||||
Select latest version compatible with the one in `project.config`.
|
||||
Example: For `2.3.0` download latest `2.3.x`.
|
||||
|
||||
3. Clone [tlibc](https://timerix.ddns.net/git/Timerix/tlibc).
|
||||
By default `dependencies/tlibc.config` expects that `tlibc/` is present in the same directory as `tsqlite/`.
|
||||
If you cloned it to another directory, change `DEPENDENCIES_DIR` in `tsqlite/project.user.config`.
|
||||
```
|
||||
git clone https://timerix.ddns.net/git/Timerix/tlibc.git
|
||||
```
|
||||
|
||||
4. Install **sqlite** library.
|
||||
MinGW: `pacman -S mingw-w64-x86_64-sqlite3`
|
||||
|
||||
5. Build and run tests
|
||||
```
|
||||
cd tsqlite
|
||||
cbuild build_exec exec
|
||||
```
|
||||
|
||||
6. To build library use tasks `build_static_lib[_dbg]` or `build_shared_lib[_dbg]`
|
||||
|
||||
|
||||
## Usage
|
||||
```c
|
||||
#include "tsqlite.h"
|
||||
|
||||
int main(){
|
||||
Deferral(32); // reserve memory for 32 defers
|
||||
// init tlibc global variables
|
||||
try_fatal_void(tlibc_init());
|
||||
// init tsqlite global variables
|
||||
try_fatal_void(tsqlite_init());
|
||||
Defer(tlibc_deinit());
|
||||
Defer(tsqlite_deinit());
|
||||
|
||||
Return 0; // call defers
|
||||
}
|
||||
```
|
||||
19
dependencies/tlibc.config
vendored
Normal file
19
dependencies/tlibc.config
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This is a dependency config.
|
||||
# You can copy it to another project to add tlibc dependency.
|
||||
|
||||
DEP_WORKING_DIR="$DEPENDENCIES_DIR/tlibc"
|
||||
if [[ "$TASK" = *_dbg ]]; then
|
||||
dep_build_target="build_static_lib_dbg"
|
||||
else
|
||||
dep_build_target="build_static_lib"
|
||||
fi
|
||||
DEP_PRE_BUILD_COMMAND=""
|
||||
DEP_BUILD_COMMAND="cbuild $dep_build_target"
|
||||
DEP_POST_BUILD_COMMAND=""
|
||||
DEP_CLEAN_COMMAND="cbuild clean"
|
||||
DEP_DYNAMIC_OUT_FILES=""
|
||||
DEP_STATIC_OUT_FILES="bin/tlibc.a"
|
||||
DEP_OTHER_OUT_FILES=""
|
||||
PRESERVE_OUT_DIRECTORY_STRUCTURE=false
|
||||
79
include/tsqlite.h
Normal file
79
include/tsqlite.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include "tlibc/tlibc.h"
|
||||
#include "tlibc/errors.h"
|
||||
#include "tlibc/string/str.h"
|
||||
#include "tlibc/collections/Array.h"
|
||||
#include "tlibc/collections/Array_impl/Array_u8.h"
|
||||
#include <sqlite3.h>
|
||||
|
||||
Result(void) tsqlite_init();
|
||||
void tsqlite_deinit();
|
||||
|
||||
/// @param code primary : 8 bits | extended : 24 bits
|
||||
/// @return heap-allocated string
|
||||
str tsqlite_ResultCode_toStr(int code);
|
||||
|
||||
ErrorCodePage_declare(SQLITE);
|
||||
|
||||
#define RESULT_ERROR_SQLITE_CODE(CODE) RESULT_ERROR_CODE(SQLITE, CODE & 0xff, tsqlite_ResultCode_toStr(CODE), true)
|
||||
|
||||
#define _try_sqlite3(CALL, N) do {\
|
||||
int _rname(N) = CALL;\
|
||||
if((_rname(N) & 0xff) != SQLITE_OK){\
|
||||
return RESULT_ERROR_SQLITE_CODE(_rname(N));\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
#define try_sqlite3(CALL) _try_sqlite3(CALL, __LINE__)
|
||||
|
||||
typedef void(*sqlite3_error_log_func_t)(void* ctx, int code, cstr msg);
|
||||
|
||||
|
||||
typedef sqlite3 tsqlite_connection;
|
||||
|
||||
/// @param file_path sqlite file
|
||||
/// @param logger
|
||||
/// @param log_func
|
||||
/// @return new sqlite connection
|
||||
Result(tsqlite_connection*) tsqlite_connection_open(cstr file_path,
|
||||
NULLABLE(void*) logger, NULLABLE(sqlite3_error_log_func_t) log_func);
|
||||
|
||||
/// all statements and blobs must be destroyed before calling this
|
||||
Result(void) tsqlite_connection_close(tsqlite_connection* db);
|
||||
|
||||
typedef struct tsqlite_statement {
|
||||
sqlite3_stmt* st;
|
||||
i32 bind_arg_pos;
|
||||
i32 result_row;
|
||||
i32 result_col;
|
||||
} tsqlite_statement;
|
||||
|
||||
/// @brief compile SQL statement to bytecode
|
||||
/// @param db
|
||||
/// @param sql_code SQL statement code. May contain placeholders
|
||||
/// @return compiled statement
|
||||
Result(tsqlite_statement*) tsqlite_statement_prepare(tsqlite_connection* db, str sql_code);
|
||||
|
||||
/// Bind value to placeholder at `self->bind_arg_pos`. Increases `self->bind_arg_pos` on success.
|
||||
Result(void) tsqlite_statement_bind_null(tsqlite_statement* self);
|
||||
Result(void) tsqlite_statement_bind_i32(tsqlite_statement* self, i32 v);
|
||||
Result(void) tsqlite_statement_bind_i64(tsqlite_statement* self, i64 v);
|
||||
Result(void) tsqlite_statement_bind_f64(tsqlite_statement* self, f64 v);
|
||||
Result(void) tsqlite_statement_bind_str(tsqlite_statement* self, str v, NULLABLE(Destructor_t) d);
|
||||
Result(void) tsqlite_statement_bind_blob(tsqlite_statement* self, Array(u8) v, NULLABLE(Destructor_t) d);
|
||||
Result(void) tsqlite_statement_bind_zeroblob(tsqlite_statement* self, i32 size);
|
||||
|
||||
/// @brief execute statement or move to next result row
|
||||
/// @return is next result row avaliable
|
||||
Result(bool) sqlite3_statement_moveNext(tsqlite_statement* self);
|
||||
|
||||
/// Get value at `self->result_col`. Increases `self->result_col` on success.
|
||||
Result(i64) tsqlite_statement_getResult_i64(tsqlite_statement* self);
|
||||
Result(i64) tsqlite_statement_getResult_f64(tsqlite_statement* self);
|
||||
Result(void) tsqlite_statement_getResult_str(tsqlite_statement* self, str* out_v);
|
||||
Result(void) tsqlite_statement_getResult_blob(tsqlite_statement* self, Array(u8)* out_v);
|
||||
|
||||
/// call this after executing prepared statement to use it again
|
||||
Result(void) tsqlite_statement_reset(tsqlite_statement* st);
|
||||
|
||||
void tsqlite_statement_free(tsqlite_statement* st);
|
||||
208
project.config
Normal file
208
project.config
Normal file
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env bash
|
||||
CBUILD_VERSION=2.3.2
|
||||
|
||||
PROJECT="tsqlite"
|
||||
CMP_C="gcc"
|
||||
CMP_CPP="g++"
|
||||
STD_C="c99"
|
||||
STD_CPP="c++11"
|
||||
WARN_C="-Wall -Wextra
|
||||
-Wduplicated-branches
|
||||
-Wduplicated-cond
|
||||
-Wformat=2
|
||||
-Wmissing-include-dirs
|
||||
-Wshadow
|
||||
-Werror=return-type
|
||||
-Werror=pointer-arith
|
||||
-Werror=init-self
|
||||
-Werror=incompatible-pointer-types"
|
||||
WARN_CPP="$WARN_C"
|
||||
SRC_C="$(find src -name '*.c')"
|
||||
SRC_CPP="$(find src -name '*.cpp')"
|
||||
TESTS_C="$(find tests -name '*.c')"
|
||||
TESTS_CPP="$(find tests -name '*.cpp')"
|
||||
|
||||
# Directory with dependency configs.
|
||||
# See cbuild/example_dependency_configs
|
||||
DEPENDENCY_CONFIGS_DIR='dependencies'
|
||||
# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space.
|
||||
ENABLED_DEPENDENCIES='tlibc'
|
||||
|
||||
# OBJDIR structure:
|
||||
# ├── objects/ - Compiled object files. Cleans on each call of build task
|
||||
# ├── static_libs/ - Symbolic links to static libraries used by linker. Cleans on each call of build task.
|
||||
# ├── static_libs/ - Symbolic links to dynamic libraries used by linker. Cleans on each call of build task.
|
||||
# └── profile/ - gcc *.gcda profiling info files
|
||||
OBJDIR="obj"
|
||||
OUTDIR="bin"
|
||||
STATIC_LIB_FILE="$PROJECT.a"
|
||||
|
||||
INCLUDE="-Isrc -Iinclude -I../tlibc/include"
|
||||
|
||||
# OS-specific options
|
||||
case "$OS" in
|
||||
WINDOWS)
|
||||
EXEC_FILE="test.exe"
|
||||
SHARED_LIB_FILE="$PROJECT.dll"
|
||||
INCLUDE="$INCLUDE "
|
||||
# example: "-lSDL2 -lSDL2_image"
|
||||
LINKER_LIBS="-lsqlite3"
|
||||
;;
|
||||
LINUX)
|
||||
EXEC_FILE="test"
|
||||
SHARED_LIB_FILE="$PROJECT.so"
|
||||
INCLUDE="$INCLUDE "
|
||||
LINKER_LIBS="-lsqlite3"
|
||||
;;
|
||||
*)
|
||||
error "operating system $OS has no configuration variants"
|
||||
;;
|
||||
esac
|
||||
|
||||
# TASKS
|
||||
case "$TASK" in
|
||||
# creates executable using profiling info if it exists
|
||||
build_exec)
|
||||
SRC_C="$SRC_C $TESTS_C"
|
||||
SRC_CPP="$SRC_CPP $TESTS_CPP"
|
||||
# -flto applies more optimizations across object files
|
||||
# -flto=auto is needed to multithreaded copilation
|
||||
# -fuse-linker-plugin is required to use static libs with lto
|
||||
# -fprofile-use enables compiler to use profiling info files to optimize executable
|
||||
# -fprofile-prefix-path sets path where profiling info about objects are be saved
|
||||
# -fdata-sections -ffunction-sections -Wl,--gc-sections removes unused code
|
||||
C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-use -fprofile-prefix-path=$(realpath $OBJDIR)/objects -fdata-sections -ffunction-sections -Wl,--gc-sections"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
|
||||
PRE_TASK_SCRIPT=""
|
||||
TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# creates executable with debug info and no optimizations
|
||||
build_exec_dbg)
|
||||
SRC_C="$SRC_C $TESTS_C"
|
||||
SRC_CPP="$SRC_CPP $TESTS_CPP"
|
||||
C_ARGS="-O0 -g3"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
|
||||
PRE_TASK_SCRIPT=""
|
||||
TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# creates shared library
|
||||
build_shared_lib)
|
||||
C_ARGS="-O2 -fpic -flto -shared"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE"
|
||||
PRE_TASK_SCRIPT=""
|
||||
TASK_SCRIPT="@cbuild/default_tasks/build_shared_lib.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# creates shared library with debug symbols and no optimizations
|
||||
build_shared_lib_dbg)
|
||||
C_ARGS="-O0 -g3 -fpic -shared"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE"
|
||||
PRE_TASK_SCRIPT=""
|
||||
TASK_SCRIPT="@cbuild/default_tasks/build_shared_lib.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# creates static library
|
||||
build_static_lib)
|
||||
C_ARGS="-O2 -fpic -fdata-sections -ffunction-sections"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
PRE_TASK_SCRIPT=""
|
||||
TASK_SCRIPT="@cbuild/default_tasks/build_static_lib.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# creates static library with debug symbols and no optimizations
|
||||
build_static_lib_dbg)
|
||||
C_ARGS="-O0 -g3"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
PRE_TASK_SCRIPT=""
|
||||
TASK_SCRIPT="@cbuild/default_tasks/build_static_lib.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# executes $EXEC_FILE
|
||||
exec)
|
||||
TASK_SCRIPT="@cbuild/default_tasks/exec.sh"
|
||||
;;
|
||||
# executes $EXEC_FILE with valgrind memory checker
|
||||
valgrind)
|
||||
VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all"
|
||||
TASK_SCRIPT="@cbuild/default_tasks/valgrind.sh"
|
||||
;;
|
||||
# generates profiling info
|
||||
profile)
|
||||
OUTDIR="$OUTDIR/profile"
|
||||
# -flto applies more optimizations across object files
|
||||
# -flto=auto is needed to multithreaded copilation
|
||||
# -fuse-linker-plugin is required to use static libs with lto
|
||||
# -pg adds code to executable, that generates file containing function call info (gmon.out)
|
||||
# -fprofile-generate generates executable with profiling code
|
||||
# -fprofile-prefix-path sets path where profiling info about objects will be saved
|
||||
C_ARGS="-O2 -flto=auto -fuse-linker-plugin -fprofile-generate -fprofile-prefix-path=$(realpath $OBJDIR)/objects"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
|
||||
PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
|
||||
TASK_SCRIPT="@cbuild/default_tasks/profile.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# compiles program with -pg and runs it with gprof
|
||||
# uses gprof2dot python script to generate function call tree (pip install gprof2dot)
|
||||
# requires graphviz (https://www.graphviz.org/download/source/)
|
||||
gprof)
|
||||
OUTDIR="$OUTDIR/gprof"
|
||||
# arguments that emit some call counter code and disable optimizations to see function names
|
||||
# https://github.com/msys2/MINGW-packages/issues/8503#issuecomment-1365475205
|
||||
C_ARGS="-O0 -g -pg -no-pie -fno-omit-frame-pointer
|
||||
-fno-inline-functions -fno-inline-functions-called-once
|
||||
-fno-optimize-sibling-calls -fopenmp"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
|
||||
PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
|
||||
TASK_SCRIPT="@cbuild/default_tasks/gprof.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# compiles program and runs it with callgrind (part of valgrind)
|
||||
# uses gprof2dot python script to generate function call tree (pip install gprof2dot)
|
||||
# requires graphviz (https://www.graphviz.org/download/source/)
|
||||
# P.S. detailed results can be viewed in KCacheGrind
|
||||
callgrind)
|
||||
OUTDIR="$OUTDIR/callgrind"
|
||||
# -pg adds code to executable, that generates file containing function call info (gmon.out)
|
||||
C_ARGS="-O2 -flto=auto -fuse-linker-plugin"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
|
||||
PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
|
||||
TASK_SCRIPT="@cbuild/default_tasks/callgrind.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# compiles executable with sanitizers and executes it to find errors and warnings
|
||||
sanitize)
|
||||
OUTDIR="$OUTDIR/sanitize"
|
||||
C_ARGS="-O0 -g3 -fsanitize=undefined,address"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
LINKER_ARGS="$CPP_ARGS $LINKER_LIBS"
|
||||
PRE_TASK_SCRIPT="@cbuild/default_tasks/build_exec.sh"
|
||||
TASK_SCRIPT="@cbuild/default_tasks/exec.sh"
|
||||
POST_TASK_SCRIPT=""
|
||||
;;
|
||||
# rebuilds specified dependencies
|
||||
# EXAMPLE: `cbuild rebuild_dependencies=libexample1,fonts`
|
||||
# 'all' can be specified to rebuild all dependencies
|
||||
rebuild_dependencies)
|
||||
TASK_SCRIPT="@cbuild/default_tasks/rebuild_dependencies.sh"
|
||||
;;
|
||||
# deletes generated files
|
||||
clean)
|
||||
TASK_SCRIPT="@cbuild/default_tasks/clean.sh"
|
||||
;;
|
||||
# nothing to do
|
||||
"" | no_task)
|
||||
;;
|
||||
# unknown task
|
||||
*)
|
||||
error "task <$PROJECT/$TASK> not found"
|
||||
;;
|
||||
esac
|
||||
11
project.config.user.default
Normal file
11
project.config.user.default
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Project user config is ignored by git.
|
||||
# Here you can add variables that users might want to change
|
||||
# on their local machine, without commiting to the repository.
|
||||
|
||||
# Directory where you install dependencies.
|
||||
# Do not confuse with DEPENDENCY_CONFIGS_DIR
|
||||
# Example:
|
||||
# libexample source code is at `../libexample`, and dependency config
|
||||
# that specifies how to build this lib is at `dependencies/libexample.config`
|
||||
DEPENDENCIES_DIR=".."
|
||||
16
src/connection.c
Normal file
16
src/connection.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "tsqlite.h"
|
||||
|
||||
Result(tsqlite_connection*) tsqlite_connection_open(cstr file_path,
|
||||
NULLABLE(void*) logger, NULLABLE(sqlite3_error_log_func_t) log_func)
|
||||
{
|
||||
tsqlite_connection* conn = NULL;
|
||||
try_sqlite3(sqlite3_open(file_path, &conn));
|
||||
try_sqlite3(sqlite3_db_config(conn, SQLITE_CONFIG_LOG, log_func, logger));
|
||||
try_sqlite3(sqlite3_extended_result_codes(conn, true));
|
||||
return RESULT_VALUE(p, conn);
|
||||
}
|
||||
|
||||
Result(void) tsqlite_connection_close(tsqlite_connection* conn){
|
||||
try_sqlite3(sqlite3_close(conn));
|
||||
return RESULT_VOID;
|
||||
}
|
||||
151
src/errors.c
Normal file
151
src/errors.c
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "tsqlite.h"
|
||||
#include "tlibc/string/StringBuilder.h"
|
||||
|
||||
#define PRIMARY_CODE_X(X)\
|
||||
X(SQLITE_OK)\
|
||||
X(SQLITE_ERROR)\
|
||||
X(SQLITE_INTERNAL)\
|
||||
X(SQLITE_PERM)\
|
||||
X(SQLITE_ABORT)\
|
||||
X(SQLITE_BUSY)\
|
||||
X(SQLITE_LOCKED)\
|
||||
X(SQLITE_NOMEM)\
|
||||
X(SQLITE_READONLY)\
|
||||
X(SQLITE_INTERRUPT)\
|
||||
X(SQLITE_IOERR)\
|
||||
X(SQLITE_CORRUPT)\
|
||||
X(SQLITE_NOTFOUND)\
|
||||
X(SQLITE_FULL)\
|
||||
X(SQLITE_CANTOPEN)\
|
||||
X(SQLITE_PROTOCOL)\
|
||||
X(SQLITE_EMPTY)\
|
||||
X(SQLITE_SCHEMA)\
|
||||
X(SQLITE_TOOBIG)\
|
||||
X(SQLITE_CONSTRAINT)\
|
||||
X(SQLITE_MISMATCH)\
|
||||
X(SQLITE_MISUSE)\
|
||||
X(SQLITE_NOLFS)\
|
||||
X(SQLITE_AUTH)\
|
||||
X(SQLITE_FORMAT)\
|
||||
X(SQLITE_RANGE)\
|
||||
X(SQLITE_NOTADB)\
|
||||
X(SQLITE_NOTICE)\
|
||||
X(SQLITE_WARNING)\
|
||||
X(SQLITE_ROW)\
|
||||
X(SQLITE_DONE)\
|
||||
|
||||
#define EXTENDED_CODE_X(X)\
|
||||
X(SQLITE_ERROR_MISSING_COLLSEQ)\
|
||||
X(SQLITE_ERROR_RETRY)\
|
||||
X(SQLITE_ERROR_SNAPSHOT)\
|
||||
X(SQLITE_ERROR_RESERVESIZE)\
|
||||
X(SQLITE_ERROR_KEY)\
|
||||
X(SQLITE_ERROR_UNABLE)\
|
||||
X(SQLITE_IOERR_READ)\
|
||||
X(SQLITE_IOERR_SHORT_READ)\
|
||||
X(SQLITE_IOERR_WRITE)\
|
||||
X(SQLITE_IOERR_FSYNC)\
|
||||
X(SQLITE_IOERR_DIR_FSYNC)\
|
||||
X(SQLITE_IOERR_TRUNCATE)\
|
||||
X(SQLITE_IOERR_FSTAT)\
|
||||
X(SQLITE_IOERR_UNLOCK)\
|
||||
X(SQLITE_IOERR_RDLOCK)\
|
||||
X(SQLITE_IOERR_DELETE)\
|
||||
X(SQLITE_IOERR_BLOCKED)\
|
||||
X(SQLITE_IOERR_NOMEM)\
|
||||
X(SQLITE_IOERR_ACCESS)\
|
||||
X(SQLITE_IOERR_CHECKRESERVEDLOCK)\
|
||||
X(SQLITE_IOERR_LOCK)\
|
||||
X(SQLITE_IOERR_CLOSE)\
|
||||
X(SQLITE_IOERR_DIR_CLOSE)\
|
||||
X(SQLITE_IOERR_SHMOPEN)\
|
||||
X(SQLITE_IOERR_SHMSIZE)\
|
||||
X(SQLITE_IOERR_SHMLOCK)\
|
||||
X(SQLITE_IOERR_SHMMAP)\
|
||||
X(SQLITE_IOERR_SEEK)\
|
||||
X(SQLITE_IOERR_DELETE_NOENT)\
|
||||
X(SQLITE_IOERR_MMAP)\
|
||||
X(SQLITE_IOERR_GETTEMPPATH)\
|
||||
X(SQLITE_IOERR_CONVPATH)\
|
||||
X(SQLITE_IOERR_VNODE)\
|
||||
X(SQLITE_IOERR_AUTH)\
|
||||
X(SQLITE_IOERR_BEGIN_ATOMIC)\
|
||||
X(SQLITE_IOERR_COMMIT_ATOMIC)\
|
||||
X(SQLITE_IOERR_ROLLBACK_ATOMIC)\
|
||||
X(SQLITE_IOERR_DATA)\
|
||||
X(SQLITE_IOERR_CORRUPTFS)\
|
||||
X(SQLITE_IOERR_IN_PAGE)\
|
||||
X(SQLITE_IOERR_BADKEY)\
|
||||
X(SQLITE_IOERR_CODEC)\
|
||||
X(SQLITE_LOCKED_SHAREDCACHE)\
|
||||
X(SQLITE_LOCKED_VTAB)\
|
||||
X(SQLITE_BUSY_RECOVERY)\
|
||||
X(SQLITE_BUSY_SNAPSHOT)\
|
||||
X(SQLITE_BUSY_TIMEOUT)\
|
||||
X(SQLITE_CANTOPEN_NOTEMPDIR)\
|
||||
X(SQLITE_CANTOPEN_ISDIR)\
|
||||
X(SQLITE_CANTOPEN_FULLPATH)\
|
||||
X(SQLITE_CANTOPEN_CONVPATH)\
|
||||
X(SQLITE_CANTOPEN_DIRTYWAL)\
|
||||
X(SQLITE_CANTOPEN_SYMLINK)\
|
||||
X(SQLITE_CORRUPT_VTAB)\
|
||||
X(SQLITE_CORRUPT_SEQUENCE)\
|
||||
X(SQLITE_CORRUPT_INDEX)\
|
||||
X(SQLITE_READONLY_RECOVERY)\
|
||||
X(SQLITE_READONLY_CANTLOCK)\
|
||||
X(SQLITE_READONLY_ROLLBACK)\
|
||||
X(SQLITE_READONLY_DBMOVED)\
|
||||
X(SQLITE_READONLY_CANTINIT)\
|
||||
X(SQLITE_READONLY_DIRECTORY)\
|
||||
X(SQLITE_ABORT_ROLLBACK)\
|
||||
X(SQLITE_CONSTRAINT_CHECK)\
|
||||
X(SQLITE_CONSTRAINT_COMMITHOOK)\
|
||||
X(SQLITE_CONSTRAINT_FOREIGNKEY)\
|
||||
X(SQLITE_CONSTRAINT_FUNCTION)\
|
||||
X(SQLITE_CONSTRAINT_NOTNULL)\
|
||||
X(SQLITE_CONSTRAINT_PRIMARYKEY)\
|
||||
X(SQLITE_CONSTRAINT_TRIGGER)\
|
||||
X(SQLITE_CONSTRAINT_UNIQUE)\
|
||||
X(SQLITE_CONSTRAINT_VTAB)\
|
||||
X(SQLITE_CONSTRAINT_ROWID)\
|
||||
X(SQLITE_CONSTRAINT_PINNED)\
|
||||
X(SQLITE_CONSTRAINT_DATATYPE)\
|
||||
X(SQLITE_NOTICE_RECOVER_WAL)\
|
||||
X(SQLITE_NOTICE_RECOVER_ROLLBACK)\
|
||||
X(SQLITE_NOTICE_RBU)\
|
||||
X(SQLITE_WARNING_AUTOINDEX)\
|
||||
X(SQLITE_AUTH_USER)\
|
||||
X(SQLITE_OK_LOAD_PERMANENTLY)\
|
||||
X(SQLITE_OK_SYMLINK)\
|
||||
|
||||
#define PRIMARY_CODE_CASE(C) case C: return STR(#C);
|
||||
|
||||
static str PrimaryCode_toStr(int code){
|
||||
switch(code & 0xff){
|
||||
default: return STR("!! ERROR: INVALID SQLITE_CODE !!");
|
||||
PRIMARY_CODE_X(PRIMARY_CODE_CASE)
|
||||
}
|
||||
}
|
||||
|
||||
#define EXTENDED_CODE_IF(C) \
|
||||
if(C & code) {\
|
||||
if(multiple_extended_codes){\
|
||||
StringBuilder_append_char(&sb, ',');\
|
||||
StringBuilder_append_char(&sb, ' ');\
|
||||
}\
|
||||
else multiple_extended_codes = true;\
|
||||
StringBuilder_append_str(&sb, STR(#C));\
|
||||
}\
|
||||
|
||||
str tsqlite_ResultCode_toStr(int code){
|
||||
StringBuilder sb = StringBuilder_alloc(128);
|
||||
bool multiple_extended_codes = false;
|
||||
|
||||
StringBuilder_append_str(&sb, PrimaryCode_toStr(code));
|
||||
StringBuilder_append_char(&sb, ':');
|
||||
StringBuilder_append_char(&sb, ' ');
|
||||
|
||||
EXTENDED_CODE_X(EXTENDED_CODE_IF);
|
||||
|
||||
return StringBuilder_getStr(&sb);
|
||||
}
|
||||
109
src/statement.c
Normal file
109
src/statement.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "tsqlite.h"
|
||||
|
||||
Result(tsqlite_statement*) tsqlite_statement_prepare(sqlite3* conn, str sql_code){
|
||||
sqlite3_stmt* st = NULL;
|
||||
i32 flags = SQLITE_PREPARE_PERSISTENT;
|
||||
try_sqlite3(sqlite3_prepare_v3(conn, sql_code.data, sql_code.len, flags, &st, NULL));
|
||||
tsqlite_statement* self = malloc(sizeof(*self));
|
||||
zeroStruct(self);
|
||||
self->st = st;
|
||||
return RESULT_VALUE(p, self);
|
||||
}
|
||||
|
||||
void tsqlite_statement_free(tsqlite_statement* self){
|
||||
if(!self)
|
||||
return;
|
||||
sqlite3_finalize(self->st);
|
||||
free(self);
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_reset(tsqlite_statement* self){
|
||||
try_sqlite3(sqlite3_reset(self->st));
|
||||
self->bind_arg_pos = 0;
|
||||
self->result_row = 0;
|
||||
self->result_col = 0;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
|
||||
Result(void) tsqlite_statement_bind_null(tsqlite_statement* self){
|
||||
try_sqlite3(sqlite3_bind_null(self->st, self->bind_arg_pos));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_bind_i32(tsqlite_statement* self, i32 v){
|
||||
try_sqlite3(sqlite3_bind_int(self->st, self->bind_arg_pos, v));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_bind_i64(tsqlite_statement* self, i64 v){
|
||||
try_sqlite3(sqlite3_bind_int64(self->st, self->bind_arg_pos, v));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_bind_f64(tsqlite_statement* self, f64 v){
|
||||
try_sqlite3(sqlite3_bind_double(self->st, self->bind_arg_pos, v));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_bind_str(tsqlite_statement* self, str v, NULLABLE(Destructor_t) d){
|
||||
try_sqlite3(sqlite3_bind_text(self->st, self->bind_arg_pos, v.data, v.len, d));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_bind_blob(tsqlite_statement* self, Array(u8) v, NULLABLE(Destructor_t) d){
|
||||
try_sqlite3(sqlite3_bind_blob(self->st, self->bind_arg_pos, v.data, v.len, d));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_bind_zeroblob(tsqlite_statement* self, i32 size){
|
||||
try_sqlite3(sqlite3_bind_zeroblob(self->st, self->bind_arg_pos, size));
|
||||
self->bind_arg_pos++;
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(bool) sqlite3_statement_moveNext(tsqlite_statement* self){
|
||||
int r = sqlite3_step(self->st);
|
||||
if(r == SQLITE_ROW){
|
||||
return RESULT_VALUE(i, true);
|
||||
}
|
||||
if(r == SQLITE_DONE){
|
||||
return RESULT_VALUE(i, false);
|
||||
}
|
||||
|
||||
return RESULT_ERROR_SQLITE_CODE(r);
|
||||
}
|
||||
|
||||
Result(i64) tsqlite_statement_getResult_i64(tsqlite_statement* self){
|
||||
i64 r = sqlite3_column_int64(self->st, self->result_col);
|
||||
// TODO: error checking in sqlite3_column
|
||||
self->result_col++;
|
||||
return RESULT_VALUE(i, r);
|
||||
}
|
||||
|
||||
Result(i64) tsqlite_statement_getResult_f64(tsqlite_statement* self){
|
||||
f64 r = sqlite3_column_double(self->st, self->result_col);
|
||||
// TODO: error checking in sqlite3_column
|
||||
self->result_col++;
|
||||
return RESULT_VALUE(f, r);
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_getResult_str(tsqlite_statement* self, str* out_v){
|
||||
(void)self;
|
||||
(void)out_v;
|
||||
// TODO: tsqlite_statement_getResult_str
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
Result(void) tsqlite_statement_getResult_blob(tsqlite_statement* self, Array(u8)* out_v){
|
||||
(void)self;
|
||||
(void)out_v;
|
||||
// TODO: tsqlite_statement_getResult_blob
|
||||
return RESULT_VOID;
|
||||
}
|
||||
15
src/tsqlite.c
Normal file
15
src/tsqlite.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "tsqlite.h"
|
||||
|
||||
ErrorCodePage_define(SQLITE);
|
||||
|
||||
Result(void) tsqlite_init(){
|
||||
Deferral(4);
|
||||
|
||||
ErrorCodePage_register(SQLITE);
|
||||
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
void tsqlite_deinit(){
|
||||
|
||||
}
|
||||
12
tests/main.c
Normal file
12
tests/main.c
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "tsqlite.h"
|
||||
|
||||
int main(){
|
||||
Deferral(32);
|
||||
|
||||
try_fatal_void(tlibc_init());
|
||||
try_fatal_void(tsqlite_init());
|
||||
Defer(tlibc_deinit());
|
||||
Defer(tsqlite_deinit());
|
||||
|
||||
Return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user