A C++ DAL / ORM code generation framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

694 lines
19 KiB

/*
* WORM - a DAL/ORM code generation framework
* Copyright (C) 2011 Erik Winn <sidewalksoftware@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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::string>() << 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<std::string>& - the table names in this database
*/
const std::vector<std::string>& 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::string>() << 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<std::string>::const_iterator it = _tableNames.begin();
for ( ; it != _tableNames.end(); ++it )
tableMetaData ( *it );
std::vector<WSqlTable>::const_iterator tbl_it = _driver->_tables.begin();
//add referenced keys
for ( ; tbl_it != _driver->_tables.end(); ++tbl_it )
{
std::vector<WSqlForeignKey>::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