/* * WORM - a DAL/ORM code generation framework * Copyright (C) 2011 Erik Winn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "wsqldatabase.h" #include "wsqldriver.h" #include "drivers/wsqlitedriver.h" #include "drivers/wmysqldriver.h" namespace WSql { /*! \class WSqlDatabase \brief The WSqlDatabase class represents a single database. The WSqlDatabase class provides an interface for accessing a specific database through a connection. An instance of WSqlDatabase represents the information relevant to connecting to the database, including the name of the database, the host, the username, etc. WSqlDatabase should be instantiated with a driver type (or another valid database object) - when constructed it will try to obtain the requested driver from WSqlDriverFactory(todo - currently just creates one ..). If this fails isValid() will return false. Drivers are derived from WSqlDriver, see WSql::DriverType for supported database drivers. Example: \code WSql::WSqlDatabase db( WSql::WMYSQL ); db.setDatabaseName( std::string( "sakila" ) ); db.setUserName( "root" ); if ( !db.open() ) { std::cerr << "Failed to open: " << db.error().text() << std::endl; return 1; } std::string sql = "select * from " + sometable + " limit 2;"; if(!db.query(sql)) std::cout << "Query Failed: " << db.error().text() << std::endl; }else{ WSql::WSqlResult *result = db.getResult(); std::cout << "Number of rows: " << result->size() << std::endl; WSql::WSqlRecord record = result->fetchFirst(); while(!record.empty()) { int numcols = record.count(); // or record.size(); std::cout << "Number of columns: " << record.size() << std::endl; WSql::WSqlField fld; for(int i = 0;i < record.size();i++) { //no danger - if there is nothing there we still get an empty field object fld = record.field(i); std::cout << "Field " << fld.name() << ", Origin Column "<< fld.columnName() << "Value: " << fld.data() << std::endl; } record = result->fetchNext(); } } \endcode \ingroup WSql \sa WSqlResult WSqlRecord WSqlField WSql::DriverType WSqlDriver */ /*! \brief Creates a WSqlDatabase object with a driver. * *This constructs a database object with a driver of \a type - the object will attempt to create a driver - if successful isValid() will return true. \param WSql::DriverType type - the type of driver to use */ WSqlDatabase::WSqlDatabase ( const WSql::DriverType &type ) { _driver = 0; _driverType = type; _isValid = false; init(); } /*! \brief Creates an empty, invalid WSqlDatabase object. */ WSqlDatabase::WSqlDatabase() { _driver = 0; _isValid = false; } /*! \brief Creates a copy of \a other. This constructs a new database object initialized with the values of \a other * WARNING: This also creates a new driver! If \a other * is destroyed getResult() is invalid until the next query() is called! \param WSqlDatabase other - database to copy */ WSqlDatabase::WSqlDatabase ( const WSqlDatabase &other ) { _databaseName = other._databaseName; _userName = other._userName; _password = other._password; _hostname = other._hostname; _port = other._port; _connectionOptions = other._connectionOptions; _driverType = other._driverType; init(); } /*!\brief Destroys the object and frees any allocated resources. * Note that the driver is also destroyed at this time - any WSqlResults obtained from getResult() will be invalid after this. \sa close() */ WSqlDatabase::~WSqlDatabase() { if ( isValid() ) { close(); } delete _driver; } /*!\brief Copies the values of \a other to this object. * WARNING: This also creates a new driver! If \a other is destroyed getResult() is invalid until the next query()! \param WSqlDatabase other - database to copy */ WSqlDatabase &WSqlDatabase::operator= ( const WSqlDatabase &other ) { if ( _driver ) delete _driver; _driver = 0; _databaseName = other._databaseName; _userName = other._userName; _password = other._password; _hostname = other._hostname; _port = other._port; _connectionOptions = other._connectionOptions; _driverType = other._driverType; init(); return *this; } /*! \internal Initializes the driver and state */ void WSqlDatabase::init() { if ( _driver ) delete _driver; _isValid = initDriver(); } /*! * \internal * Attempts to create a driver - returns true on success */ bool WSqlDatabase::initDriver() { bool blnToReturn = false; std::string errmsg; switch ( _driverType ) { case WSql::NONE: errmsg = "WSqlDatabase: initDriver failed - no driver type set!"; break; case WSql::WSQLITE : _driver = new WSqliteDriver ( this ); if ( 0 == _driver ) errmsg = "WSqlDatabase: initDriver failed - memory allocation error!"; else blnToReturn = true; break; case WSql::WMYSQL : _driver = new WMysqlDriver ( this ); if ( 0 == _driver ) errmsg = "WSqlDatabase: initDriver failed - memory allocation error!"; else blnToReturn = true; break; case WSql::WMONGO : case WSql::WBERKLEYDB : case WSql::WPSQL : default: errmsg = "WSqlDatabase: initDriver failed - driver unsupported!"; } if ( !errmsg.empty() ) _errorStack.push_back ( WSqlError ( errmsg, 1, WSqlError::SYSTEM, WSqlError::WARNING ) ); return blnToReturn; } /*! \brief Open a connection to current database * Opens the database connection using the current connection values. Returns true on success; otherwise returns false. Error information can be retrieved using error(). \sa error() setDatabaseName() setUserName() setPassword() \sa setHostName() setPort() setConnectOptions() */ bool WSqlDatabase::open() { if ( _driver && _isValid ) return _driver->open(); return false; } /*!\brief Open a connection using \a username and \a password \overload Opens the database connection using the given \a user name and \a password. Returns true on success; otherwise returns false. Error information can be retrieved using the error() function. \sa error() */ bool WSqlDatabase::open ( const std::string &username, const std::string &password ) { setUserName ( username ); setPassword ( password ); if ( _driver && _isValid ) return _driver->open(); return false; } /*! Closes the database connection. */ void WSqlDatabase::close() { if ( _driver && _isValid ) _driver->close(); } /*! Returns true if the database connection is currently open */ bool WSqlDatabase::isOpen() const { if ( _driver && _isValid ) return _driver->isOpen(); return false; } /*! Returns true if there is an error available. \sa error() */ bool WSqlDatabase::hasError() const { return ! _errorStack.empty(); } /*! \brief Set the the database name * Sets the connection's database name to \a name. To have effect, the database name must be set \em before opening the connection with open() Alternately, you can close() the connection, set the database name, and call open() again There is no default value. \param std::string name - the name of the database \sa databaseName() */ void WSqlDatabase::setDatabaseName ( const std::string &name ) { _databaseName = name; } /*! \brief Set the the user name * * Sets the connection's user name to \a name. To have effect, the user name must be set \em before opening the connection with open() Alternately, you can close() the connection, set the user name, and call open() again. There is no default value. \param std::string name - the name of the user \sa userName() open() close() */ void WSqlDatabase::setUserName ( const std::string &name ) { _userName = name; } /*! \brief Set the the password * * Sets the connection's password to \a password. To have effect, the password must be set \e{before} the connection is \l{open()} {opened}. Alternatively, you can close() the connection, set the password, and call open() again. There is no default value. \param std::string password - the password to use \sa password() open() close() */ void WSqlDatabase::setPassword ( const std::string &password ) { _password = password; } /*! \brief Sets the connection's host name * This method sets the connection's host name to \a hostname. To have effect, the host name must be set \em before opening the connection with open() Alternately, you can close() the connection, set the host name, and call open() again. There is no default value. \param std::string hostname - the hostname to use \sa hostName() open() close() */ void WSqlDatabase::setHostName ( const std::string &hostname ) { _hostname = hostname; } /*! \brief Sets the port number for the connection * This method sets the connection's port number to \a port. To have effect, the port number must be set \em before opening the connection with open() Alternately, you can close() the connection, set the port number, and call open() again The default value is -1. \sa port() open() close() \param int port number to use */ void WSqlDatabase::setPort ( int port ) { _port = port; } /*! Returns the connection's database name, which may be empty. \sa setDatabaseName() */ std::string WSqlDatabase::databaseName() const { return _databaseName; } /*! Returns the connection's user name; it may be empty. \sa setUserName() */ std::string WSqlDatabase::userName() const { return _userName; } /*! Returns the connection's password. If the password was not set an empty string is returned. */ std::string WSqlDatabase::password() const { return _password; } /*! Returns the connection's host name; it may be empty. \sa setHostName() */ std::string WSqlDatabase::hostName() const { return _hostname; } /*! \fn int WSqlDatabase::port()const \brief Returns the connection's port number. The value is -1 if the port number has not been set. \sa setPort() */ /*!\brief Return a pointer to the database driver - expert only. This returns a pointer to the database driver used to access the database connection. \em Caution! This is not meant to be used directly - use open(). close(), query() and getResult() for interaction with the driver instead. !This may be removed in future. \sa open() close() query() getResult() */ WSqlDriver *WSqlDatabase::driver() const { return _driver; } /*!\brief Returns the last error that occurred on the database or in the driver. * * All errors for database interactions are available here - they are popped off the * error stack. This function may be called repeatedly to retrieve all errors on the * stack. When there are no errors an empty WSqlError is returned. * \code if(database->hasError()){ WSqlError e = database->error(); while(e.isValid){ //do something with error .. e = database->error(); } } \endcode * \sa WSqlError * */ WSqlError WSqlDatabase::error() { if ( ! _errorStack.empty() ) { WSqlError e = _errorStack.back(); _errorStack.pop_back(); return e; } return WSqlError(); } /*! * Returns all the errors that have occurred on the database or in the * driver as a vector of strings. * \sa WSqlError */ // WSqlError WSqlDatabase::errors() const // { // return _driver->getError(); // } /*!\brief Returns a vector of the database's table names * This function returns a vector of strings containing the names tables and views as specified by the parameter \a type. The vector is cached for quick reference - if empty it is initialized by a request to the driver for the names. If the names are not available an empty vector is returned. \warning If the table metadata has not been initialized yet this method will invalidate any previous WSqlResult pointer returned - in this case nesting calls to this method inside of a loop iterating over WSqlResults WILL NOT WORK. Obtain the WSqlTable \em first and \em then query() a query and fetch the result set using getResult() or use initMetaData() to initialize the metadata for all tables at once. \todo Use the table type - currently does nothing. \sa WSql::TableType \param WSql::TableType \retval std::vector& - the table names in this database */ const std::vector& WSqlDatabase::tableNames ( WSql::TableType type ) { if ( _tableNames.empty() && _driver->isValid() ) _tableNames = _driver->tableNames(); return _tableNames; } /*!\brief Fetch metadata for table \a tableName * This method returns a WSqlTable object populated with WSqlColumns that contain metadata for the columns in the given table \a tableName. Example: \code meta_table = db.tableMetaData(*it); numflds = meta_table.count(); for (int i=0; i < numflds; ++i) { WSql::WSqlColumn column = meta_table.column(i); std::cout << "Column " << i << " = " << column.columnName() << std::endl; std::cout << " * Data type: " << WSql::WSqlDataType::toString(column.dataType()) << std::endl; std::cout << " * Max Length: " << column.maxLength() << std::endl; std::cout << " * Unsigned: " << (column.isUnsigned() ? "true" : "false") << std::endl; std::cout << " * Can be null: " << (column.canBeNull() ? "true" : "false") << std::endl; std::cout << " * Primary key: " << (column.isPrimaryKey() ? "true" : "false") << std::endl; std::cout << " * Autoincrement: " << (column.isAutoIncremented()?"true" : "false") << std::endl; std::cout << " * default value: " << column.defaultValue() << std::endl; * } \endcode \warning If the table metadata has not been initialized yet this method will invalidate any previous WSqlResult pointer returned - in this case nesting calls to this method inside of a loop iterating over WSqlResults WILL NOT WORK. Obtain the WSqlTable \em first and \em then query() a query and fetch the result set using getResult() or use initMetaData() to initialize the metadata for all tables at once. \param string the name of the table to use \retval WSqlTable an object containing metadata \sa WSqlTable WSqlColumn */ WSqlTable WSqlDatabase::tableMetaData ( const std::string &tablename ) const { if ( !isValid() ) return WSqlTable(); return _driver->tableMetaData ( tablename ); } /*! \brief Initializes the metadata for all tables in the database This method can be used to initialize all the metadata for the the database at once - this is convenient if one wishes to then use table metadata while also conducting queries. If this is called before tableMetaData() the metadata for all tables is cached in the driver and will be returned for a given table from the cache. \note This also initializes the referenced tables WSqlReferencedKeys - used by the ORM generation. If you need access to this kind of metadata you must use this method to initialize the referenced key lists - if you initialize table metadata individually the reference keys will be omitted. \sa tableMetaData() tableNames() */ void WSqlDatabase::initMetaData() { //load all tablenames tableNames(); //init metadata for all tables std::vector::const_iterator it = _tableNames.begin(); for ( ; it != _tableNames.end(); ++it ) tableMetaData ( *it ); std::vector::const_iterator tbl_it = _driver->_tables.begin(); //add referenced keys for ( ; tbl_it != _driver->_tables.end(); ++tbl_it ) { std::vector::const_iterator fk_it = tbl_it->foreignKeys().begin(); for ( ; fk_it != tbl_it->foreignKeys().end(); ++fk_it ) { WSqlTable *table = _driver->getTable ( fk_it->referencedTableName() ); if ( table ) { WSqlReferencedKey rfk ( *fk_it ); table->addReferencedKey ( rfk ); } } } } /*! \brief Sets the connection options for this database server Sets database-specific \a options. This must be done before the connection is opened or it has no effect (or you can close() the connection, call this function and open() the connection again). Note that the options are specific to a database server - drivers should handle this string appropriately. \param std::string& options - connection options to pass to the driver. \sa getConnectionOptions() */ void WSqlDatabase::setConnectOptions ( const std::string &options ) { _connectionOptions = options; } /*! Returns the connection options string used for this connection. The string may be empty. \sa setConnectOptions() */ std::string WSqlDatabase::connectionOptions() const { return _connectionOptions; } /*! Returns true if the WSqlDatabase has a valid driver. */ bool WSqlDatabase::isValid() const { return ( _isValid && 0 != _driver && _driver->isValid() ); } /*! \brief Executes the query in \a sql returning true on sucess * * This method sends the query SQL in string \a sql to the database server, * the results of which will be available by calling getResult(). Use this method * when you expect a result set, for non-result execution use execute() * \sa getResult() execute() * \retval bool true on success. */ bool WSqlDatabase::doQuery ( const std::string &sql ) { return _driver->doQuery ( sql ); } /*! \brief Returns a pointer to the result set from the most recent query * * This method returns a pointer to a WSqlResult object containing * the most recent result set from a query - it may be empty if no results * were returned. You must not delete this pointer yourself - it is owned * by the driver. * * The parameter \a iscached may be set to true to indicate a non-cached result * set that will be fetched row by row - the default is true and results are cached. * Note that uncached queries may or may not be implemented in a particular * driver - see the documentation for the specific driver to find out. * * \note Only use this \em after an execute() query! Do not use twice in a row as it will delete * the previous result and return a newly created object. Example: * * \code * WSqlDatabase db; * if (!db.open()) * dosomeerror(); * if (!db.query(std::string("select foo from bar"))) * dosomeerror(); * WSqlResult *result = db.getResult(); * //WSqlResult *result2 = db.getResult(); <- wrong * //...iterate over results ..._then repeat: * if (!db.query(std::string("select baz from bar"))) * dosomeerror(); * WSqlResult *result = db.getResult(); * ..etc. * \endcode * \sa WSqlResult WSqlRecord WMysqlDriver WSqliteDriver * * \param bool iscached - if true (the default) fetches entire result set at once. * \retval WSqlResult* - the result set */ WSqlResult *WSqlDatabase::getResult ( bool iscached ) { return _driver->getResult ( iscached ); } } //namespace WSql