src/PreparedStatement.cpp
branchv_0
changeset 26 49919a60c747
parent 25 ec793cb3e686
child 28 498d5d3406c3
equal deleted inserted replaced
25:ec793cb3e686 26:49919a60c747
       
     1 /**
       
     2  * Relational pipes
       
     3  * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
       
     4  *
       
     5  * This program is free software: you can redistribute it and/or modify
       
     6  * it under the terms of the GNU General Public License as published by
       
     7  * the Free Software Foundation, version 3.
       
     8  *
       
     9  * This program is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       
    12  * GNU General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License
       
    15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
       
    16  */
       
    17 
       
    18 #include "PreparedStatement.h"
       
    19 
       
    20 namespace relpipe {
       
    21 namespace tr {
       
    22 namespace sql {
       
    23 
       
    24 PreparedStatement::PreparedStatement(sqlite3_stmt* stmt) : stmt(stmt) {
       
    25 }
       
    26 
       
    27 PreparedStatement::~PreparedStatement() {
       
    28 	sqlite3_finalize(stmt);
       
    29 }
       
    30 
       
    31 void PreparedStatement::setBoolean(int parameterIndex, relpipe::reader::boolean_t value) {
       
    32 	int result = sqlite3_bind_int(stmt, parameterIndex, value);
       
    33 	if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter.");
       
    34 }
       
    35 
       
    36 void PreparedStatement::setInteger(int parameterIndex, relpipe::reader::integer_t value) {
       
    37 	int result = sqlite3_bind_int64(stmt, parameterIndex, value);
       
    38 	if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter.");
       
    39 }
       
    40 
       
    41 void PreparedStatement::setString(int parameterIndex, std::string value) {
       
    42 	int result = sqlite3_bind_text(stmt, parameterIndex, value.c_str(), -1, SQLITE_TRANSIENT);
       
    43 	if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter.");
       
    44 }
       
    45 
       
    46 void PreparedStatement::setNull(int parameterIndex) {
       
    47 	int result = sqlite3_bind_null(stmt, parameterIndex);
       
    48 	if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter.");
       
    49 }
       
    50 
       
    51 bool PreparedStatement::next() {
       
    52 	int result = sqlite3_step(stmt);
       
    53 	if (result == SQLITE_ROW) return true;
       
    54 	else if (result == SQLITE_DONE) return false;
       
    55 	else throw SqlException(L"Error while iterating over SQLite result.");
       
    56 }
       
    57 
       
    58 void PreparedStatement::reset() {
       
    59 	int result = sqlite3_reset(stmt);
       
    60 	if (result != SQLITE_OK) throw SqlException(L"Unable to reset SQLite prepared statement.");
       
    61 }
       
    62 
       
    63 int PreparedStatement::getColumnCount() {
       
    64 	return sqlite3_column_count(stmt);
       
    65 }
       
    66 
       
    67 std::string PreparedStatement::getColumName(int columnIndex) {
       
    68 	const char* name = sqlite3_column_name(stmt, columnIndex);
       
    69 	if (name) return name;
       
    70 	else throw SqlException(L"Unable to get SQLite column name.");
       
    71 }
       
    72 
       
    73 relpipe::writer::TypeId PreparedStatement::getColumType(int columnIndex, relpipe::writer::TypeId defaultType) {
       
    74 	const char* type = sqlite3_column_decltype(stmt, columnIndex);
       
    75 
       
    76 	// TODO: sqlite3_column_decltype returns value only for columns of existing tables, not for dynamic expressions – SQLite uses dynamic types
       
    77 	// maybe we could write a function/module that returns result set metadata for given query (before executing it)
       
    78 	// or use at least explicit casts in SQL and modify sqlite3_column_decltype() function or add some new one to return such casted type
       
    79 	// 
       
    80 	// fprintf(stderr, "%d → %s\n", columnIndex, type);
       
    81 	// SELECT typeof(1+1); == "integer"
       
    82 	// https://www.sqlite.org/c3ref/column_decltype.html – sqlite3_column_decltype
       
    83 	// https://www.sqlite.org/c3ref/column_blob.html – sqlite3_column_type
       
    84 	// https://www.sqlite.org/datatype3.html – Datatypes In SQLite Version 3
       
    85 	// https://dba.stackexchange.com/questions/203220/sqlite-what-is-the-use-of-specifying-data-types
       
    86 	// https://www.mail-archive.com/sqlite-users@mailinglists.sqlite.org/msg118093.html
       
    87 
       
    88 	if (type == nullptr) return relpipe::writer::TypeId::STRING;
       
    89 	else if (strcmp(type, "integer") == 0) return relpipe::writer::TypeId::INTEGER;
       
    90 	else if (strcmp(type, "text") == 0) return relpipe::writer::TypeId::STRING;
       
    91 	else return defaultType;
       
    92 	// TODO: support also other data types
       
    93 }
       
    94 
       
    95 std::string PreparedStatement::getString(int columnIndex) {
       
    96 	const char* value = (const char*) sqlite3_column_text(stmt, columnIndex);
       
    97 		return value ? value : ""; // TODO: support NULL values (when supported in relpipe format)
       
    98 }
       
    99 
       
   100 }
       
   101 }
       
   102 }