/*========================================================================= Program: Visualization Toolkit Module: vtkSQLiteQuery.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /*------------------------------------------------------------------------- Copyright 2008 Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. -------------------------------------------------------------------------*/ #include "vtkSQLiteQuery.h" #include "vtkObjectFactory.h" #include "vtkSQLiteDatabase.h" #include "vtkStringArray.h" #include "vtkVariant.h" #include "vtkVariantArray.h" #include #include #include #include #define BEGIN_TRANSACTION "BEGIN TRANSACTION" #define COMMIT_TRANSACTION "COMMIT" #define ROLLBACK_TRANSACTION "ROLLBACK" vtkStandardNewMacro(vtkSQLiteQuery); // ---------------------------------------------------------------------- vtkSQLiteQuery::vtkSQLiteQuery() { this->Statement = nullptr; this->InitialFetch = true; this->InitialFetchResult=VTK_SQLITE_DONE; this->LastErrorText = nullptr; this->TransactionInProgress = false; } // ---------------------------------------------------------------------- vtkSQLiteQuery::~vtkSQLiteQuery() { this->SetLastErrorText(nullptr); if (this->TransactionInProgress) { this->RollbackTransaction(); } if (this->Statement != nullptr) { if (this->Database != nullptr) { vtk_sqlite3_finalize(this->Statement); this->Statement = nullptr; } } } // ---------------------------------------------------------------------- void vtkSQLiteQuery::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Statement: "; if (this->Statement) { os << this->Statement << "\n"; } else { os << "(null)" << "\n"; } os << indent << "InitialFetch: " << this->InitialFetch << "\n"; os << indent << "InitialFetchResult: " << this->InitialFetchResult << "\n"; os << indent << "TransactionInProgress: " << this->TransactionInProgress << "\n"; os << indent << "LastErrorText: " << (this->LastErrorText ? this->LastErrorText : "(null)") << endl; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::SetQuery(const char *newQuery) { vtkDebugMacro(<< this->GetClassName() << " (" << this << "): setting Query to " << (newQuery?newQuery:"(null)") ); if (this->Query == nullptr && newQuery == nullptr) { return true; } if (this->Query && newQuery && (!strcmp(this->Query, newQuery))) { return true; // we've already got that query } delete [] this->Query; if (newQuery) { // Keep a local copy of the query - this is from vtkSetGet.h size_t n = strlen(newQuery) + 1; char *cp1 = new char[n]; const char *cp2 = (newQuery); this->Query = cp1; do { *cp1++ = *cp2++; } while ( --n ); } else { this->Query = nullptr; } // If we get to this point the query has changed. We need to // finalize the already-prepared statement if one exists and then // prepare a new statement. if (this->Statement) { vtkDebugMacro(<<"Finalizing old statement"); int finalizeStatus = vtk_sqlite3_finalize(this->Statement); if (finalizeStatus != VTK_SQLITE_OK) { vtkWarningMacro(<<"SetQuery(): Finalize returned unexpected code " << finalizeStatus); } this->Statement = nullptr; } if (this->Query) { vtkSQLiteDatabase *dbContainer = vtkSQLiteDatabase::SafeDownCast(this->Database); if (dbContainer == nullptr) { vtkErrorMacro(<<"This should never happen: SetQuery() called when there is no underlying database. You probably instantiated vtkSQLiteQuery directly instead of calling vtkSQLDatabase::GetInstance(). This also happens during TestSetGet in the CDash testing."); return false; } vtk_sqlite3 *db = dbContainer->SQLiteInstance; const char *unused_statement; int prepareStatus = vtk_sqlite3_prepare_v2(db, this->Query, static_cast(strlen(this->Query)), &this->Statement, &unused_statement); if (prepareStatus != VTK_SQLITE_OK) { this->SetLastErrorText(vtk_sqlite3_errmsg(db)); vtkWarningMacro(<<"SetQuery(): vtk_sqlite3_prepare_v2() failed with error message " << this->GetLastErrorText() << " on statement: '" << this->Query << "'"); this->Active = false; return false; } } // Done preparing new statement this->Modified(); return true; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::Execute() { if (this->Query == nullptr) { vtkErrorMacro(<<"Cannot execute before a query has been set."); return false; } if (this->Statement == nullptr) { vtkErrorMacro(<<"Execute(): Query is not null but prepared statement is. There may have been an error during SetQuery()."); this->Active = false; return false; } else { vtk_sqlite3_reset(this->Statement); } vtkDebugMacro(<<"Execute(): Query ready to execute."); this->InitialFetch = true; int result = vtk_sqlite3_step(this->Statement); this->InitialFetchResult = result; if (result == VTK_SQLITE_DONE) { this->SetLastErrorText(nullptr); this->Active = true; return true; } else if (result != VTK_SQLITE_ROW) { vtkSQLiteDatabase *dbContainer = vtkSQLiteDatabase::SafeDownCast(this->Database); assert(dbContainer != nullptr); vtk_sqlite3 *db = dbContainer->SQLiteInstance; this->SetLastErrorText(vtk_sqlite3_errmsg(db)); vtkDebugMacro(<< "Execute(): vtk_sqlite3_step() returned error message " << this->GetLastErrorText()); this->Active = false; return false; } this->SetLastErrorText(nullptr); this->Active = true; return true; } // ---------------------------------------------------------------------- int vtkSQLiteQuery::GetNumberOfFields() { if (! this->Active) { vtkErrorMacro(<<"GetNumberOfFields(): Query is not active!"); return 0; } else { return vtk_sqlite3_column_count(this->Statement); } } // ---------------------------------------------------------------------- const char * vtkSQLiteQuery::GetFieldName(int column) { if (! this->Active) { vtkErrorMacro(<<"GetFieldName(): Query is not active!"); return nullptr; } else if (column < 0 || column >= this->GetNumberOfFields()) { vtkErrorMacro(<<"GetFieldName(): Illegal field index " << column); return nullptr; } else { return vtk_sqlite3_column_name(this->Statement, column); } } // ---------------------------------------------------------------------- int vtkSQLiteQuery::GetFieldType(int column) { if (! this->Active) { vtkErrorMacro(<<"GetFieldType(): Query is not active!"); return -1; } else if (column < 0 || column >= this->GetNumberOfFields()) { vtkErrorMacro(<<"GetFieldType(): Illegal field index " << column); return -1; } else { switch (vtk_sqlite3_column_type(this->Statement, column)) { case VTK_SQLITE_INTEGER: return VTK_INT; case VTK_SQLITE_FLOAT: return VTK_FLOAT; case VTK_SQLITE_TEXT: return VTK_STRING; case VTK_SQLITE_BLOB: return VTK_STRING; // until we have a BLOB type of our own case VTK_SQLITE_NULL: return VTK_VOID; // ??? what makes sense here? default: { vtkErrorMacro(<<"GetFieldType(): Unknown data type " << vtk_sqlite3_column_type(this->Statement, column) <<" from SQLite."); return VTK_VOID; } } } } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::NextRow() { if (! this->IsActive()) { vtkErrorMacro(<<"NextRow(): Query is not active!"); return false; } if (this->InitialFetch) { vtkDebugMacro(<<"NextRow(): Initial fetch being handled."); this->InitialFetch = false; if (this->InitialFetchResult == VTK_SQLITE_DONE) { return false; } else { return true; } } else { int result = vtk_sqlite3_step(this->Statement); if (result == VTK_SQLITE_DONE) { return false; } else if (result == VTK_SQLITE_ROW) { return true; } else { vtkSQLiteDatabase *dbContainer = vtkSQLiteDatabase::SafeDownCast( this->Database ); assert(dbContainer != nullptr); vtk_sqlite3 *db = dbContainer->SQLiteInstance; this->SetLastErrorText(vtk_sqlite3_errmsg(db)); vtkErrorMacro(<<"NextRow(): Database returned error code " << result << " with the following message: " << this->GetLastErrorText()); this->Active = false; return false; } } } // ---------------------------------------------------------------------- vtkVariant vtkSQLiteQuery::DataValue(vtkIdType column) { if (this->IsActive() == false) { vtkWarningMacro(<<"DataValue() called on inactive query"); return vtkVariant(); } else if (column < 0 || column >= this->GetNumberOfFields()) { vtkWarningMacro(<<"DataValue() called with out-of-range column index " << column); return vtkVariant(); } else { switch (vtk_sqlite3_column_type(this->Statement, column)) { case VTK_SQLITE_INTEGER: return vtkVariant(vtk_sqlite3_column_int(this->Statement, column)); case VTK_SQLITE_FLOAT: return vtkVariant(vtk_sqlite3_column_double(this->Statement, column)); case VTK_SQLITE_TEXT: { std::ostringstream str; str << vtk_sqlite3_column_text(this->Statement, column); return vtkVariant(vtkStdString(str.str())); } case VTK_SQLITE_BLOB: { // This is a hack ... by passing the BLOB to vtkStdString with an explicit // byte count, we ensure that the string will store all of the BLOB's bytes, // even if there are nullptr values. return vtkVariant(vtkStdString( static_cast(vtk_sqlite3_column_blob(this->Statement, column)), vtk_sqlite3_column_bytes(this->Statement, column))); } case VTK_SQLITE_NULL: default: return vtkVariant(); } } } // ---------------------------------------------------------------------- const char * vtkSQLiteQuery::GetLastErrorText() { return this->LastErrorText; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::HasError() { return (this->GetLastErrorText() != nullptr); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BeginTransaction() { if (this->TransactionInProgress) { vtkErrorMacro(<<"Cannot start a transaction. One is already in progress."); return false; } vtkSQLiteDatabase *dbContainer = vtkSQLiteDatabase::SafeDownCast( this->Database ); assert(dbContainer != nullptr); vtk_sqlite3 *db = dbContainer->SQLiteInstance; char *errorMessage = nullptr; int result = vtk_sqlite3_exec(db, BEGIN_TRANSACTION, nullptr, nullptr, &errorMessage); if (result == VTK_SQLITE_OK) { this->TransactionInProgress = true; this->SetLastErrorText(nullptr); vtkDebugMacro(<<"BeginTransaction() succeeded."); return true; } else { vtkErrorMacro(<<"BeginTransaction(): sqlite3_exec returned unexpected result code " << result); if (errorMessage) { vtkErrorMacro(<< " and error message " << errorMessage); } this->TransactionInProgress = false; return false; } } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::CommitTransaction() { if (this->Statement) { vtk_sqlite3_finalize(this->Statement); this->Statement = nullptr; } if (!this->TransactionInProgress) { vtkErrorMacro(<<"Cannot commit. There is no transaction in progress."); return false; } vtkSQLiteDatabase *dbContainer = vtkSQLiteDatabase::SafeDownCast( this->Database ); assert(dbContainer != nullptr); vtk_sqlite3 *db = dbContainer->SQLiteInstance; char *errorMessage = nullptr; int result = vtk_sqlite3_exec(db, COMMIT_TRANSACTION, nullptr, nullptr, &errorMessage); if (result == VTK_SQLITE_OK) { this->TransactionInProgress = false; this->SetLastErrorText(nullptr); vtkDebugMacro(<<"CommitTransaction() succeeded."); return true; } else { vtkErrorMacro(<<"CommitTransaction(): sqlite3_exec returned unexpected result code " << result); if (errorMessage) { this->SetLastErrorText(errorMessage); vtkErrorMacro(<< " and error message " << errorMessage); } assert(1==0); return false; } } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::RollbackTransaction() { if (!this->TransactionInProgress) { vtkErrorMacro(<<"Cannot rollback. There is no transaction in progress."); return false; } vtkSQLiteDatabase *dbContainer = vtkSQLiteDatabase::SafeDownCast( this->Database ); assert(dbContainer != nullptr); vtk_sqlite3 *db = dbContainer->SQLiteInstance; char *errorMessage = nullptr; int result = vtk_sqlite3_exec(db, ROLLBACK_TRANSACTION, nullptr, nullptr, &errorMessage); if (result == VTK_SQLITE_OK) { this->TransactionInProgress = false; this->SetLastErrorText(nullptr); vtkDebugMacro(<<"RollbackTransaction() succeeded."); return true; } else { vtkErrorMacro(<<"RollbackTransaction(): sqlite3_exec returned unexpected result code " << result); if (errorMessage) { this->SetLastErrorText(errorMessage); vtkErrorMacro(<< " and error message " << errorMessage); } return false; } } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, unsigned char value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, signed char value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, unsigned short value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, short value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, unsigned int value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, int value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, unsigned long value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, long value) { return this->BindIntegerParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, unsigned long long value) { return this->BindInt64Parameter(index, static_cast(value)); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, long long value) { return this->BindInt64Parameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, float value) { return this->BindDoubleParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, double value) { return this->BindDoubleParameter(index, value); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, const char *value) { return this->BindParameter(index, value, strlen(value)); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, const char *data, size_t length) { return this->BindStringParameter(index, data, static_cast(length)); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, const vtkStdString &value) { return this->BindParameter(index, value.c_str()); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, const void *data, size_t length) { return this->BindBlobParameter(index, data, static_cast(length)); } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindIntegerParameter(int index, int value) { if (!this->Statement) { vtkErrorMacro(<<"No statement available. Did you forget to call SetQuery?"); return false; } if (this->Active) { this->Active = false; vtk_sqlite3_reset(this->Statement); } int status = vtk_sqlite3_bind_int(this->Statement, index+1, value); if (status != VTK_SQLITE_OK) { std::ostringstream errormessage; errormessage << "sqlite_bind_int returned error: " << status; this->SetLastErrorText(errormessage.str().c_str()); vtkErrorMacro(<Statement) { vtkErrorMacro(<<"No statement available. Did you forget to call SetQuery?"); return false; } if (this->Active) { this->Active = false; vtk_sqlite3_reset(this->Statement); } int status = vtk_sqlite3_bind_int(this->Statement, index+1, static_cast(value)); if (status != VTK_SQLITE_OK) { std::ostringstream errormessage; errormessage << "sqlite_bind_int64 returned error: " << status; this->SetLastErrorText(errormessage.str().c_str()); vtkErrorMacro(<GetLastErrorText()); return false; } return true; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindDoubleParameter(int index, double value) { if (!this->Statement) { vtkErrorMacro(<<"No statement available. Did you forget to call SetQuery?"); return false; } if (this->Active) { this->Active = false; vtk_sqlite3_reset(this->Statement); } int status = vtk_sqlite3_bind_double(this->Statement, index+1, value); if (status != VTK_SQLITE_OK) { std::ostringstream errormessage; errormessage << "sqlite_bind_double returned error: " << status; this->SetLastErrorText(errormessage.str().c_str()); vtkErrorMacro(<GetLastErrorText()); return false; } return true; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindStringParameter(int index, const char *value, int length) { if (!this->Statement) { vtkErrorMacro(<<"No statement available. Did you forget to call SetQuery?"); return false; } if (this->Active) { this->Active = false; vtk_sqlite3_reset(this->Statement); } int status = vtk_sqlite3_bind_text(this->Statement, index+1, value, length, VTK_SQLITE_TRANSIENT); if (status != VTK_SQLITE_OK) { std::ostringstream errormessage; errormessage << "sqlite_bind_text returned error: " << status; this->SetLastErrorText(errormessage.str().c_str()); vtkErrorMacro(<GetLastErrorText()); return false; } return true; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindBlobParameter(int index, const void *data, int length) { if (!this->Statement) { vtkErrorMacro(<<"No statement available. Did you forget to call SetQuery?"); return false; } if (this->Active) { this->Active = false; vtk_sqlite3_reset(this->Statement); } int status = vtk_sqlite3_bind_blob(this->Statement, index+1, data, length, VTK_SQLITE_TRANSIENT); if (status != VTK_SQLITE_OK) { std::ostringstream errormessage; errormessage << "sqlite_bind_blob returned error: " << status; this->SetLastErrorText(errormessage.str().c_str()); vtkErrorMacro(<GetLastErrorText()); return false; } return true; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::ClearParameterBindings() { if (!this->Statement) { vtkErrorMacro(<<"No statement available. Did you forget to call SetQuery?"); return false; } if (this->Active) { this->Active = false; vtk_sqlite3_reset(this->Statement); } int status = vtk_sqlite3_clear_bindings(this->Statement); if (status != VTK_SQLITE_OK) { std::ostringstream errormessage; errormessage << "sqlite_clear_bindings returned error: " << status; this->SetLastErrorText(errormessage.str().c_str()); vtkErrorMacro(<GetLastErrorText()); return false; } return true; } // ---------------------------------------------------------------------- bool vtkSQLiteQuery::BindParameter(int index, vtkVariant value) { return this->Superclass::BindParameter(index, value); }