/*========================================================================= Program: Visualization Toolkit Module: vtkSQLiteDatabase.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 "vtkSQLiteDatabase.h" #include "vtkSQLiteQuery.h" #include "vtkSQLDatabaseSchema.h" #include "vtkObjectFactory.h" #include "vtkStringArray.h" #include #include #include #include vtkStandardNewMacro(vtkSQLiteDatabase); // ---------------------------------------------------------------------- vtkSQLiteDatabase::vtkSQLiteDatabase() { this->SQLiteInstance = nullptr; this->Tables = vtkStringArray::New(); this->Tables->Register(this); this->Tables->Delete(); // Initialize instance variables this->DatabaseType = nullptr; this->SetDatabaseType("sqlite"); this->DatabaseFileName = nullptr; } // ---------------------------------------------------------------------- vtkSQLiteDatabase::~vtkSQLiteDatabase() { if (this->IsOpen() ) { this->Close(); } if ( this->DatabaseType ) { this->SetDatabaseType(nullptr); } if ( this->DatabaseFileName ) { this->SetDatabaseFileName(nullptr); } this->Tables->UnRegister(this); } // ---------------------------------------------------------------------- void vtkSQLiteDatabase::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "SQLiteInstance: "; if (this->SQLiteInstance) { os << this->SQLiteInstance << "\n"; } else { os << "(null)" << "\n"; } os << indent << "DatabaseType: " << (this->DatabaseType ? this->DatabaseType : "nullptr") << endl; os << indent << "DatabaseFileName: " << (this->DatabaseFileName ? this->DatabaseFileName : "nullptr") << endl; } // ---------------------------------------------------------------------- vtkStdString vtkSQLiteDatabase::GetColumnSpecification( vtkSQLDatabaseSchema* schema, int tblHandle, int colHandle ) { std::ostringstream queryStr; queryStr << schema->GetColumnNameFromHandle( tblHandle, colHandle ); // Figure out column type int colType = schema->GetColumnTypeFromHandle( tblHandle, colHandle ); vtkStdString colTypeStr; switch ( static_cast( colType ) ) { case vtkSQLDatabaseSchema::SERIAL: colTypeStr = "INTEGER NOT NULL"; break; case vtkSQLDatabaseSchema::SMALLINT: colTypeStr = "SMALLINT"; break; case vtkSQLDatabaseSchema::INTEGER: colTypeStr = "INTEGER"; break; case vtkSQLDatabaseSchema::BIGINT: colTypeStr = "BIGINT"; break; case vtkSQLDatabaseSchema::VARCHAR: colTypeStr = "VARCHAR"; break; case vtkSQLDatabaseSchema::TEXT: colTypeStr = "TEXT"; break; case vtkSQLDatabaseSchema::REAL: colTypeStr = "REAL"; break; case vtkSQLDatabaseSchema::DOUBLE: colTypeStr = "DOUBLE"; break; case vtkSQLDatabaseSchema::BLOB: colTypeStr = "BLOB"; break; case vtkSQLDatabaseSchema::TIME: colTypeStr = "TIME"; break; case vtkSQLDatabaseSchema::DATE: colTypeStr = "DATE"; break; case vtkSQLDatabaseSchema::TIMESTAMP: colTypeStr = "TIMESTAMP"; } if ( !colTypeStr.empty() ) { queryStr << " " << colTypeStr; } else // if ( colTypeStr.size() ) { vtkGenericWarningMacro( "Unable to get column specification: unsupported data type " << colType ); return vtkStdString(); } // Decide whether size is allowed, required, or unused int colSizeType = 0; switch ( static_cast( colType ) ) { case vtkSQLDatabaseSchema::SERIAL: colSizeType = 0; break; case vtkSQLDatabaseSchema::SMALLINT: colSizeType = 0; break; case vtkSQLDatabaseSchema::INTEGER: colSizeType = 0; break; case vtkSQLDatabaseSchema::BIGINT: colSizeType = 0; break; case vtkSQLDatabaseSchema::VARCHAR: colSizeType = -1; break; case vtkSQLDatabaseSchema::TEXT: colSizeType = 0; break; case vtkSQLDatabaseSchema::REAL: colSizeType = 0; break; case vtkSQLDatabaseSchema::DOUBLE: colSizeType = 0; break; case vtkSQLDatabaseSchema::BLOB: colSizeType = 0; break; case vtkSQLDatabaseSchema::TIME: colSizeType = 0; break; case vtkSQLDatabaseSchema::DATE: colSizeType = 0; break; case vtkSQLDatabaseSchema::TIMESTAMP: colSizeType = 0; break; } // Specify size if allowed or required if ( colSizeType ) { int colSize = schema->GetColumnSizeFromHandle( tblHandle, colHandle ); // IF size is provided but absurd, // OR, if size is required but not provided OR absurd, // THEN assign the default size. if ( ( colSize < 0 ) || ( colSizeType == -1 && colSize < 1 ) ) { colSize = VTK_SQL_DEFAULT_COLUMN_SIZE; } // At this point, we have either a valid size if required, or a possibly null valid size // if not required. Thus, skip sizing in the latter case. if ( colSize > 0 ) { queryStr << "(" << colSize << ")"; } } vtkStdString attStr = schema->GetColumnAttributesFromHandle( tblHandle, colHandle ); if ( !attStr.empty() ) { queryStr << " " << attStr; } return queryStr.str(); } // ---------------------------------------------------------------------- bool vtkSQLiteDatabase::IsSupported(int feature) { switch (feature) { case VTK_SQL_FEATURE_BLOB: case VTK_SQL_FEATURE_LAST_INSERT_ID: case VTK_SQL_FEATURE_NAMED_PLACEHOLDERS: case VTK_SQL_FEATURE_POSITIONAL_PLACEHOLDERS: case VTK_SQL_FEATURE_PREPARED_QUERIES: case VTK_SQL_FEATURE_TRANSACTIONS: case VTK_SQL_FEATURE_UNICODE: return true; case VTK_SQL_FEATURE_BATCH_OPERATIONS: case VTK_SQL_FEATURE_QUERY_SIZE: case VTK_SQL_FEATURE_TRIGGERS: return false; default: { vtkErrorMacro(<< "Unknown SQL feature code " << feature << "! See " << "vtkSQLDatabase.h for a list of possible features."); return false; }; } } // ---------------------------------------------------------------------- bool vtkSQLiteDatabase::Open(const char* password) { return this->Open(password, USE_EXISTING); } // ---------------------------------------------------------------------- bool vtkSQLiteDatabase::Open(const char* password, int mode) { if (this->IsOpen()) { vtkWarningMacro("Open(): Database is already open."); return true; } if(password && strlen(password)) { vtkGenericWarningMacro("Password will be ignored by vtkSQLiteDatabase::Open()."); } if (!this->DatabaseFileName) { vtkErrorMacro("Cannot open database because DatabaseFileName is not set."); return false; } if (this->IsOpen()) { vtkGenericWarningMacro( "Open(): Database is already open." ); return true; } // Only do checks if it is not an in-memory database if (strcmp(":memory:", this->DatabaseFileName)) { bool exists = vtksys::SystemTools::FileExists(this->DatabaseFileName); if (mode == USE_EXISTING && !exists) { vtkErrorMacro("You specified using an existing database but the file does not exist.\n" "Use USE_EXISTING_OR_CREATE to allow database creation."); return false; } if (mode == CREATE && exists) { vtkErrorMacro("You specified creating a database but the file exists.\n" "Use USE_EXISTING_OR_CREATE to allow using an existing database,\n" "or CREATE_OR_CLEAR to clear any existing file."); return false; } if (mode == CREATE_OR_CLEAR && exists) { // Here we need to clear the file if it exists by opening it. std::ofstream os; os.open(this->DatabaseFileName); if (!os.is_open()) { vtkErrorMacro("Unable to create file " << this->DatabaseFileName << "."); return false; } os.close(); } } int result = vtk_sqlite3_open(this->DatabaseFileName, & (this->SQLiteInstance)); if (result != VTK_SQLITE_OK) { vtkDebugMacro(<<"SQLite open() failed. Error code is " << result << " and message is " << vtk_sqlite3_errmsg(this->SQLiteInstance) ); vtk_sqlite3_close(this->SQLiteInstance); return false; } else { vtkDebugMacro(<<"SQLite open() succeeded."); return true; } } // ---------------------------------------------------------------------- void vtkSQLiteDatabase::Close() { if (this->SQLiteInstance == nullptr) { vtkDebugMacro(<<"Close(): Database is already closed."); } else { int result = vtk_sqlite3_close(this->SQLiteInstance); if (result != VTK_SQLITE_OK) { vtkWarningMacro(<< "Close(): SQLite returned result code " << result); } this->SQLiteInstance = nullptr; } } // ---------------------------------------------------------------------- bool vtkSQLiteDatabase::IsOpen() { return (this->SQLiteInstance != nullptr); } // ---------------------------------------------------------------------- vtkSQLQuery * vtkSQLiteDatabase::GetQueryInstance() { vtkSQLiteQuery *query = vtkSQLiteQuery::New(); query->SetDatabase(this); return query; } // ---------------------------------------------------------------------- vtkStringArray * vtkSQLiteDatabase::GetTables() { this->Tables->Resize(0); if (this->SQLiteInstance == nullptr) { vtkErrorMacro(<<"GetTables(): Database is not open!"); return this->Tables; } vtkSQLQuery *query = this->GetQueryInstance(); query->SetQuery("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"); bool status = query->Execute(); if (!status) { vtkErrorMacro(<< "GetTables(): Database returned error: " << vtk_sqlite3_errmsg(this->SQLiteInstance) ); query->Delete(); return this->Tables; } else { vtkDebugMacro(<<"GetTables(): SQL query succeeded."); while (query->NextRow() ) { this->Tables->InsertNextValue(query->DataValue(0).ToString() ); } query->Delete(); return this->Tables; } } // ---------------------------------------------------------------------- vtkStringArray * vtkSQLiteDatabase::GetRecord(const char *table) { vtkSQLQuery *query = this->GetQueryInstance(); vtkStdString text("PRAGMA table_info ('"); text += table; text += "')"; query->SetQuery(text.c_str() ); bool status = query->Execute(); if (!status) { vtkErrorMacro(<< "GetRecord(" << table << "): Database returned error: " << vtk_sqlite3_errmsg(this->SQLiteInstance) ); query->Delete(); return nullptr; } else { // Each row in the results that come back from this query // describes a single column in the table. The format of each row // is as follows: // // columnID columnName columnType ??? defaultValue nullForbidden // // (I don't know what the ??? column is. It's probably maximum // length.) vtkStringArray *results = vtkStringArray::New(); while (query->NextRow() ) { results->InsertNextValue(query->DataValue(1).ToString() ); } query->Delete(); return results; } } // ---------------------------------------------------------------------- vtkStdString vtkSQLiteDatabase::GetURL() { const char* fname = this->GetDatabaseFileName(); this->TempURL = this->GetDatabaseType(); this->TempURL += "://"; if ( fname ) { this->TempURL += fname; } return this->TempURL; } // ---------------------------------------------------------------------- bool vtkSQLiteDatabase::ParseURL(const char* URL) { std::string urlstr( URL ? URL : "" ); std::string protocol; std::string dataglom; if ( ! vtksys::SystemTools::ParseURLProtocol( urlstr, protocol, dataglom)) { vtkErrorMacro( "Invalid URL: \"" << urlstr.c_str() << "\"" ); return false; } if ( protocol == "sqlite" ) { this->SetDatabaseFileName( dataglom.c_str() ); return true; } return false; } // ---------------------------------------------------------------------- bool vtkSQLiteDatabase::HasError() { return (vtk_sqlite3_errcode(this->SQLiteInstance)!=VTK_SQLITE_OK); } const char* vtkSQLiteDatabase::GetLastErrorText() { return vtk_sqlite3_errmsg(this->SQLiteInstance); }