97 lines
4.8 KiB
C++
97 lines
4.8 KiB
C++
#pragma once
|
|
#include "ILog.h"
|
|
// this is pure virtual interface base class between sqlite3, which speaks only C and utf8 char[]
|
|
// and wxWidgets which speaks only C++ and unicode strings.
|
|
|
|
// Usage: Call the factory function std::shared_ptr<ISqlite3> sqlite3_open(const char *) to get a shared
|
|
// pointer to the // Sqlite3 database object. Then call the factory function
|
|
// sqlite3_prepare(std::shared_ptr<ISqlite3>, const char *) to get a unique pointer to
|
|
// a compiled SQL statement
|
|
|
|
// Its primary purpose is to avoid code that needs both the wxWidgets header files,
|
|
// and the sqlite3.h header file.
|
|
//
|
|
// It speaks only utf8 char[], and needs to be called in wxWidgets code using
|
|
// wxString.utf8_str() and its return values need to be interpreted in wxWidgets code
|
|
// using wxString::FromUTF8().
|
|
//
|
|
// This header file can be included in code that has the sqlite3.h header file
|
|
// and in code that has the wxWidgets header file, for it has no dependencies on either one
|
|
//
|
|
// In code that has wxWidgets headers, we call members of this interface class,
|
|
// rather than directly calling sqlite3 functions.
|
|
//
|
|
// I originally implemented the pimpl idiom, but it turns out that pimpl has become
|
|
// substantially more difficult in C++14, because one is effectively rolling one's own
|
|
// unique pointer.
|
|
//
|
|
// It is therefore easier to implement a pure virtual base class with a virtual destructor and
|
|
// factory function that returns a smart pointer to a member of the derived implementation
|
|
//
|
|
/* This code is at a low level abstraction, because it provides low level C++ interface to inherently low level C
|
|
It is intended to be wrapped in higher level code that does not know about the nuts and bolts of sqlite3, but which supports throwing, templated functions, and all that.*/
|
|
|
|
//
|
|
//___________________________________
|
|
|
|
// This class wraps a compiled sql statement.
|
|
class Icompiled_sql
|
|
{
|
|
protected:
|
|
Icompiled_sql() = default; // needed for derived constructor
|
|
public:
|
|
virtual ~Icompiled_sql() = default; // needed for derived destructor
|
|
// Bind is used when writing stuff into the database. These objects should continue to exist until the write is finalized or reset.
|
|
virtual void Isqlite3_bind( int, const std::span<const uint8_t>) = 0; // https://sqlite.org/c3ref/bind.html
|
|
virtual void Isqlite3_bind(int, int) = 0;
|
|
virtual void Isqlite3_bind(int, int64_t) = 0;
|
|
virtual void Isqlite3_bind(int) = 0;
|
|
virtual void Isqlite3_bind(int, const char*) = 0;
|
|
enum sql_result { DONE, ROW, BUSY, SQL_ERROR, MISUSE };
|
|
virtual sql_result Isqlite3_step() = 0;
|
|
// when reading, you don't use bind. Sqlite creates a temporary in the memory that it manages. If you want the object to live beyond the next step operation, need to make a copy
|
|
// When writing objects, we reinterpret a pointer to a typed object as a blob pointer, when reading them, we need a typed copy, otherwise calling the destructor could be bad.
|
|
// We don't want Sqlite3 calling destructors on our objects, hence write them as static, and create them from raw bytes on reading.
|
|
virtual std::span<const uint8_t> Isqlite3_column_blob (int) const = 0; // returns the null pointer and zero length if null.
|
|
virtual int Isqlite3_column_int (int) const = 0;
|
|
virtual int64_t Isqlite3_column_int64 (int) const = 0;
|
|
virtual char* Isqlite3_column_text (int) const = 0; // returns pointer to zero length
|
|
// string if null. If we need to distinguish betweem zero length strings and nulls, need the
|
|
// type function.
|
|
// We can store any type in any column, and read any type from any column, but if something
|
|
// unexpected is in a column, it gets coerced to the expected type on being read back.
|
|
// Thus something stored as a number and read back as blob will come back as the decimal character string.
|
|
// It is very rarely valid to store different types in the same column, except that
|
|
// null is permissible. The difference between null and zero matters, but the case of
|
|
// null is usually dealt with by sql code, not C code.
|
|
virtual void Isqlite3_reset() = 0; // https://sqlite.org/c3ref/reset.html
|
|
};
|
|
|
|
//___________________________________
|
|
|
|
// This class wraps a database. Its derived implementation will hold an old type C pointer
|
|
// to an opened database object, which is destroyed when the class object is destroyed
|
|
class ISqlite3
|
|
{
|
|
protected:
|
|
ISqlite3() = default; // needed for derived constructor
|
|
public:
|
|
virtual ~ISqlite3() = default; // needed for derived destructor
|
|
virtual void exec(const char*) = 0;
|
|
};
|
|
|
|
// Factory method to open a database and produce a shared object wrapping the database
|
|
ISqlite3* Sqlite3_open(const char*);
|
|
|
|
// Factory method to create a database and produce a shared object wrapping the database
|
|
ISqlite3* Sqlite3_create(const char*);
|
|
|
|
// Factory method to prepare a compiled sql statement
|
|
Icompiled_sql* sqlite3_prepare(ISqlite3*, const char *);
|
|
|
|
void sqlite3_init();
|
|
extern "C" {
|
|
int sqlite3_shutdown(void);
|
|
}
|
|
|