first steps with sqlite

This commit is contained in:
Maarten L. Hekkelman
2025-12-09 16:47:11 +01:00
parent 9f6e1e245b
commit 4a31878975
7 changed files with 2848 additions and 11 deletions

View File

@@ -65,6 +65,9 @@ set(BUILD_DOCUMENTATION OFF CACHE BOOL "Build the documentation")
# Optionally build a version to be installed inside CCP4
set(BUILD_FOR_CCP4 OFF CACHE BOOL "Build a version to be installed in CCP4")
# Create the cql/sqlite interface
set(BUILD_SQLITE_INTERFACE ON CACHE BOOL "Build the sqlite interface")
# Building shared libraries?
if(NOT(BUILD_FOR_CCP4 AND WIN32))
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build a shared library instead of a static one")
@@ -189,6 +192,10 @@ if(NOT ZLIB_FOUND)
message(FATAL_ERROR "cifpp: The zlib development files were not found you this system, please install them and try again (hint: on debian/ubuntu use apt-get install zlib1g-dev)")
endif()
if(BUILD_SQLITE_INTERFACE)
find_package(SQLite3 REQUIRED)
endif()
include(FindPkgConfig)
if(PKG_CONFIG_FOUND)
@@ -352,6 +359,10 @@ if(NOT STD_CHARCONV_COMPILING)
target_link_libraries(cifpp PUBLIC FastFloat::fast_float)
endif()
if(BUILD_SQLITE_INTERFACE)
target_link_libraries(cifpp PRIVATE SQLite::SQLite3)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")

View File

@@ -437,15 +437,24 @@ class result
class transaction
{
public:
transaction(const datablock &db)
: m_db(const_cast<datablock &>(db))
{
}
transaction(const datablock &db);
~transaction();
transaction(const transaction &) = delete;
transaction &operator=(const transaction &) = delete;
result exec(std::string_view query);
private:
datablock &m_db;
struct transaction_impl *m_impl;
};
// // --------------------------------------------------------------------
// class connection
// {
// };
} // namespace cif::cql

View File

@@ -0,0 +1,33 @@
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.23)
project(cifpp-sqlite VERSION 1.0.0 LANGUAGES C)
find_package(SQLite3 REQUIRED)
add_library(cifpp-sqlite SHARED
${CMAKE_CURRENT_SOURCE_DIR}/cifpp2sqlite.c
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

269
sqlite-backend/template.c Normal file
View File

@@ -0,0 +1,269 @@
/*
** 2018-04-19
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements a template virtual-table.
** Developers can make a copy of this file as a baseline for writing
** new virtual tables and/or table-valued functions.
**
** Steps for writing a new virtual table implementation:
**
** (1) Make a copy of this file. Perhaps call it "mynewvtab.c"
**
** (2) Replace this header comment with something appropriate for
** the new virtual table
**
** (3) Change every occurrence of "templatevtab" to some other string
** appropriate for the new virtual table. Ideally, the new string
** should be the basename of the source file: "mynewvtab". Also
** globally change "TEMPLATEVTAB" to "MYNEWVTAB".
**
** (4) Run a test compilation to make sure the unmodified virtual
** table works.
**
** (5) Begin making incremental changes, testing as you go, to evolve
** the new virtual table to do what you want it to do.
**
** This template is minimal, in the sense that it uses only the required
** methods on the sqlite3_module object. As a result, templatevtab is
** a read-only and eponymous-only table. Those limitation can be removed
** by adding new methods.
**
** This template implements an eponymous-only virtual table with a rowid and
** two columns named "a" and "b". The table as 10 rows with fixed integer
** values. Usage example:
**
** SELECT rowid, a, b FROM templatevtab;
*/
#if !defined(SQLITEINT_H)
#include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1
#include <string.h>
#include <assert.h>
/* templatevtab_vtab is a subclass of sqlite3_vtab which is
** underlying representation of the virtual table
*/
typedef struct templatevtab_vtab templatevtab_vtab;
struct templatevtab_vtab {
sqlite3_vtab base; /* Base class - must be first */
/* Add new fields here, as necessary */
};
/* templatevtab_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct templatevtab_cursor templatevtab_cursor;
struct templatevtab_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
/* Insert new fields here. For this templatevtab we only keep track
** of the rowid */
sqlite3_int64 iRowid; /* The rowid */
};
/*
** The templatevtabConnect() method is invoked to create a new
** template virtual table.
**
** Think of this routine as the constructor for templatevtab_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the templatevtab_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against the virtual table will look like.
*/
static int templatevtabConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
templatevtab_vtab *pNew;
int rc;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(a,b)"
);
/* For convenience, define symbolic names for the index to each column. */
#define TEMPLATEVTAB_A 0
#define TEMPLATEVTAB_B 1
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for templatevtab_vtab objects.
*/
static int templatevtabDisconnect(sqlite3_vtab *pVtab){
templatevtab_vtab *p = (templatevtab_vtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Constructor for a new templatevtab_cursor object.
*/
static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
templatevtab_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a templatevtab_cursor.
*/
static int templatevtabClose(sqlite3_vtab_cursor *cur){
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
** Advance a templatevtab_cursor to its next row of output.
*/
static int templatevtabNext(sqlite3_vtab_cursor *cur){
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the templatevtab_cursor
** is currently pointing.
*/
static int templatevtabColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
switch( i ){
case TEMPLATEVTAB_A:
sqlite3_result_int(ctx, 1000 + pCur->iRowid);
break;
default:
assert( i==TEMPLATEVTAB_B );
sqlite3_result_int(ctx, 2000 + pCur->iRowid);
break;
}
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int templatevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int templatevtabEof(sqlite3_vtab_cursor *cur){
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
return pCur->iRowid>=10;
}
/*
** This method is called to "rewind" the templatevtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to templatevtabColumn() or templatevtabRowid() or
** templatevtabEof().
*/
static int templatevtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
templatevtab_cursor *pCur = (templatevtab_cursor *)pVtabCursor;
pCur->iRowid = 1;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
*/
static int templatevtabBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
pIdxInfo->estimatedCost = (double)10;
pIdxInfo->estimatedRows = 10;
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module templatevtabModule = {
/* iVersion */ 0,
/* xCreate */ 0,
/* xConnect */ templatevtabConnect,
/* xBestIndex */ templatevtabBestIndex,
/* xDisconnect */ templatevtabDisconnect,
/* xDestroy */ 0,
/* xOpen */ templatevtabOpen,
/* xClose */ templatevtabClose,
/* xFilter */ templatevtabFilter,
/* xNext */ templatevtabNext,
/* xEof */ templatevtabEof,
/* xColumn */ templatevtabColumn,
/* xRowid */ templatevtabRowid,
/* xUpdate */ 0,
/* xBegin */ 0,
/* xSync */ 0,
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
};
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_templatevtab_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "templatevtab", &templatevtabModule, 0);
return rc;
}

View File

@@ -27,13 +27,18 @@
#include "cif++/cql/transaction.hpp"
#include "cif++/category.hpp"
#include "cif++/condition.hpp"
#include "cif++/datablock.hpp"
#include "cif++/row.hpp"
#include "cif++/text.hpp"
#include "cif++/validate.hpp"
#include <mcfp/mcfp.hpp>
#include <algorithm>
#include <iterator>
#include <memory>
#include <sqlite3.h>
#include <stdexcept>
namespace cif::cql
@@ -349,7 +354,6 @@ class SimpleColumnValueExpression : public ValueExpression
std::string mColumn;
};
// // -----------------------------------------------------------------------
// class StatementList : public Statement
@@ -1253,8 +1257,6 @@ std::vector<std::pair<std::string, std::string>> Parser::ParseItemList()
{
Match(Token::BRACE_OPEN);
Match(Token::BRACE_CLOSE);
}
else
@@ -1518,6 +1520,245 @@ cif::condition Parser::ParseWhereClause(cif::category &cat, const column_list &c
// --------------------------------------------------------------------
struct virtual_table
{
static int Connect(sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr);
static int Disconnect(sqlite3_vtab *pVtab);
static int Open(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor);
static int Close(sqlite3_vtab_cursor *cur);
static int Next(sqlite3_vtab_cursor *cur);
static int Column(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i); /* Which column to return */
static int Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid);
static int Eof(sqlite3_vtab_cursor *cur);
static int Filter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv);
static int BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo);
static sqlite3_module s_module;
};
struct transaction_impl
{
const datablock &m_db;
sqlite3 *m_sqlite_db = nullptr;
transaction_impl(const datablock &db);
~transaction_impl()
{
sqlite3_close(m_sqlite_db);
}
};
sqlite3_module virtual_table::s_module{
/* iVersion */ 0,
/* xCreate */ 0,
/* xConnect */ Connect,
/* xBestIndex */ BestIndex,
/* xDisconnect */ Disconnect,
/* xDestroy */ 0,
/* xOpen */ Open,
/* xClose */ Close,
/* xFilter */ Filter,
/* xNext */ Next,
/* xEof */ Eof,
/* xColumn */ Column,
/* xRowid */ Rowid,
/* xUpdate */ 0,
/* xBegin */ 0,
/* xSync */ 0,
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindFunction */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*
** The templatevtabConnect() method is invoked to create a new
** template virtual table.
**
** Think of this routine as the constructor for virtual_table objects.
**
** All this routine needs to do is:
**
** (1) Allocate the virtual_table object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against the virtual table will look like.
*/
int virtual_table::Connect(sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr)
{
virtual_table *pNew;
int rc;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(a,b)");
/* For convenience, define symbolic names for the index to each column. */
#define TEMPLATEVTAB_A 0
#define TEMPLATEVTAB_B 1
if (rc == SQLITE_OK)
{
pNew = sqlite3_malloc(sizeof(*pNew));
*ppVtab = (sqlite3_vtab *)pNew;
if (pNew == 0)
return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for virtual_table objects.
*/
int virtual_table::Disconnect(sqlite3_vtab *pVtab)
{
virtual_table *p = (virtual_table *)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Constructor for a new templatevtab_cursor object.
*/
int virtual_table::Open(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor)
{
templatevtab_cursor *pCur;
pCur = sqlite3_malloc(sizeof(*pCur));
if (pCur == 0)
return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a templatevtab_cursor.
*/
int virtual_table::Close(sqlite3_vtab_cursor *cur)
{
templatevtab_cursor *pCur = (templatevtab_cursor *)cur;
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
** Advance a templatevtab_cursor to its next row of output.
*/
int virtual_table::Next(sqlite3_vtab_cursor *cur)
{
templatevtab_cursor *pCur = (templatevtab_cursor *)cur;
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the templatevtab_cursor
** is currently pointing.
*/
int virtual_table::Column(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
)
{
templatevtab_cursor *pCur = (templatevtab_cursor *)cur;
switch (i)
{
case TEMPLATEVTAB_A:
sqlite3_result_int(ctx, 1000 + pCur->iRowid);
break;
default:
assert(i == TEMPLATEVTAB_B);
sqlite3_result_int(ctx, 2000 + pCur->iRowid);
break;
}
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
int virtual_table::Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid)
{
templatevtab_cursor *pCur = (templatevtab_cursor *)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
int virtual_table::Eof(sqlite3_vtab_cursor *cur)
{
templatevtab_cursor *pCur = (templatevtab_cursor *)cur;
return pCur->iRowid >= 10;
}
/*
** This method is called to "rewind" the templatevtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to templatevtabColumn() or templatevtabRowid() or
** templatevtabEof().
*/
int virtual_table::Filter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv)
{
templatevtab_cursor *pCur = (templatevtab_cursor *)pVtabCursor;
pCur->iRowid = 1;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
*/
int virtual_table::BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo)
{
pIdxInfo->estimatedCost = (double)10;
pIdxInfo->estimatedRows = 10;
return SQLITE_OK;
}
transaction_impl::transaction_impl(const datablock &db)
: m_db(db)
{
auto rc = sqlite3_open(":memory:", &m_sqlite_db);
if (rc)
throw std::runtime_error(std::format("Cannot open databank: {}", sqlite3_errmsg(m_sqlite_db)));
rc = sqlite3_create_module(m_sqlite_db, "cifpp", &virtual_table::s_module, &m_db);
if (rc)
throw std::runtime_error(std::format("Cannot open databank: {}", sqlite3_errmsg(m_sqlite_db)));
}
// --------------------------------------------------------------------
transaction::transaction(const datablock &db)
: m_impl(new transaction_impl(db))
{
}
transaction::~transaction()
{
delete m_impl;
}
result transaction::exec(std::string_view query)
{
struct membuf : public std::streambuf
@@ -1528,9 +1769,9 @@ result transaction::exec(std::string_view query)
}
} buffer(const_cast<char *>(query.data()), query.size());
Parser p(m_db);
auto stmt = p.Parse(&buffer);
return { *stmt->Execute() };
// Parser p(m_db);
// auto stmt = p.Parse(&buffer);
// return { *stmt->Execute() };
}
} // namespace cif::cql