// // Session.h // // Library: Data // Package: DataCore // Module: Session // // Definition of the Session class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #ifndef Data_Session_INCLUDED #define Data_Session_INCLUDED #include "Poco/Data/Data.h" #include "Poco/Data/SessionImpl.h" #include "Poco/Data/Statement.h" #include "Poco/Data/StatementCreator.h" #include "Poco/Data/Binding.h" #include "Poco/SharedPtr.h" #include "Poco/AutoPtr.h" #include "Poco/Any.h" #include namespace Poco { namespace Data { class StatementImpl; class Data_API Session /// A Session holds a connection to a Database and creates Statement objects. /// /// Sessions are always created via the SessionFactory: /// /// Session ses(SessionFactory::instance().create(connectorKey, connectionString)); /// /// where the first param presents the type of session one wants to create (e.g., for SQLite one would choose "SQLite", /// for ODBC the key is "ODBC") and the second param is the connection string that the session implementation /// requires to connect to the database. The format of the connection string is specific to the actual connector. /// /// A simpler form to create the session is to pass the connector key and connection string directly to /// the Session constructor. /// /// A concrete example to open an SQLite database stored in the file "dummy.db" would be /// /// Session ses("SQLite", "dummy.db"); /// /// Via a Session one can create two different types of statements. First, statements that should only be executed once and immediately, and /// second, statements that should be executed multiple times, using a separate execute() call. /// The simple one is immediate execution: /// /// ses << "CREATE TABLE Dummy (data INTEGER(10))", now; /// /// The now at the end of the statement is required, otherwise the statement /// would not be executed. /// /// If one wants to reuse a Statement (and avoid the overhead of repeatedly parsing an SQL statement) /// one uses an explicit Statement object and its execute() method: /// /// int i = 0; /// Statement stmt = (ses << "INSERT INTO Dummy VALUES(:data)", use(i)); /// /// for (i = 0; i < 100; ++i) /// { /// stmt.execute(); /// } /// /// The above example assigns the variable i to the ":data" placeholder in the SQL query. The query is parsed and compiled exactly /// once, but executed 100 times. At the end the values 0 to 99 will be present in the Table "DUMMY". /// /// A faster implementaton of the above code will simply create a vector of int /// and use the vector as parameter to the use clause (you could also use set or multiset instead): /// /// std::vector data; /// for (int i = 0; i < 100; ++i) /// { /// data.push_back(i); /// } /// ses << "INSERT INTO Dummy VALUES(:data)", use(data); /// /// NEVER try to bind to an empty collection. This will give a BindingException at run-time! /// /// Retrieving data from a database works similar, you could use simple data types, vectors, sets or multiset as your targets: /// /// std::set retData; /// ses << "SELECT * FROM Dummy", into(retData)); /// /// Due to the blocking nature of the above call it is possible to partition the data retrieval into chunks by setting a limit to /// the maximum number of rows retrieved from the database: /// /// std::set retData; /// Statement stmt = (ses << "SELECT * FROM Dummy", into(retData), limit(50)); /// while (!stmt.done()) /// { /// stmt.execute(); /// } /// /// The "into" keyword is used to inform the statement where output results should be placed. The limit value ensures /// that during each run at most 50 rows are retrieved. Assuming Dummy contains 100 rows, retData will contain 50 /// elements after the first run and 100 after the second run, i.e. /// the collection is not cleared between consecutive runs. After the second execute stmt.done() will return true. /// /// A prepared Statement will behave exactly the same but a further call to execute() will simply reset the Statement, /// execute it again and append more data to the result set. /// /// Note that it is possible to append several "bind" or "into" clauses to the statement. Theoretically, one could also have several /// limit clauses but only the last one that was added will be effective. /// Also several preconditions must be met concerning binds and intos. /// Take the following example: /// /// ses << "CREATE TABLE Person (LastName VARCHAR(30), FirstName VARCHAR, Age INTEGER(3))"; /// std::vector nameVec; // [...] add some elements /// std::vector ageVec; // [...] add some elements /// ses << "INSERT INTO Person (LastName, Age) VALUES(:ln, :age)", use(nameVec), use(ageVec); /// /// The size of all use parameters MUST be the same, otherwise an exception is thrown. Furthermore, /// the amount of use clauses must match the number of wildcards in the query (to be more precise: /// each binding has a numberOfColumnsHandled() value which defaults to 1. The sum of all these values /// must match the wildcard count in the query. /// However, this is only important if you have written your own TypeHandler specializations. /// If you plan to map complex object types to tables see the TypeHandler documentation. /// For now, we simply assume we have written one TypeHandler for Person objects. Instead of having n different vectors, /// we have one collection: /// /// std::vector people; // [...] add some elements /// ses << "INSERT INTO Person (LastName, FirstName, Age) VALUES(:ln, :fn, :age)", use(people); /// /// which will insert all Person objects from the people vector to the database (and again, you can use set, multiset too, /// even map and multimap if Person provides an operator() which returns the key for the map). /// The same works for a SELECT statement with "into" clauses: /// /// std::vector people; /// ses << "SELECT * FROM PERSON", into(people); /// /// Mixing constants or variables with manipulators is allowed provided there are corresponding placeholders for the constants provided in /// the SQL string, such as in following example: /// /// std::vector people; /// ses << "SELECT * FROM %s", into(people), "PERSON"; /// /// Formatting only kicks in if there are values to be injected into the SQL string, otherwise it is skipped. /// If the formatting will occur and the percent sign is part of the query itself, it can be passed to the query by entering it twice (%%). /// However, if no formatting is used, one percent sign is sufficient as the string will be passed unaltered. /// For complete list of supported data types with their respective specifications, see the documentation for format in Foundation. /// /// Transactions are supported via the begin(), commit() and rollback() methods. /// If the session is in autocommit mode, begin() will temporarily disable autocommit mode for the duration of the transaction. /// Calls to either commit() or rollback() will re-enable it. /// Poco::Data::Transaction, a convenient session wrapper class, automates this process and provides RAII-based transactional behavior. /// For more information, see Transation class documentation. { public: static const std::size_t LOGIN_TIMEOUT_DEFAULT = SessionImpl::LOGIN_TIMEOUT_DEFAULT; static const Poco::UInt32 TRANSACTION_READ_UNCOMMITTED = 0x00000001L; static const Poco::UInt32 TRANSACTION_READ_COMMITTED = 0x00000002L; static const Poco::UInt32 TRANSACTION_REPEATABLE_READ = 0x00000004L; static const Poco::UInt32 TRANSACTION_SERIALIZABLE = 0x00000008L; Session(Poco::AutoPtr ptrImpl); /// Creates the Session from SessionImpl. Session(const std::string& connector, const std::string& connectionString, std::size_t loginTimeout = LOGIN_TIMEOUT_DEFAULT); /// Creates a new session, using the given connector (which must have /// been registered), and connectionString. Session(const std::string& connection, std::size_t loginTimeout = LOGIN_TIMEOUT_DEFAULT); /// Creates a new session, using the given connection (must be in /// "connection:///connectionString" format). Session(const Session&); /// Creates a session by copying another one. Session(Session&&) noexcept; /// Creates a session by moving another one. Session& operator = (const Session&); /// Assignment operator. Session& operator = (Session&&) noexcept; /// Move assignment. ~Session(); /// Destroys the Session. void swap(Session& other); /// Swaps the session with another one. template Statement operator << (const T& t) /// Creates a Statement with the given string as SQLContent. { return (_statementCreator << t); } const std::string& dbmsName() const; /// Returns the DBMS name. The name must be set by the /// implementation. /// Defaults to "unknown". SharedPtr createStatementImpl(); /// Creates a StatementImpl. void open(const std::string& connect = ""); /// Opens the session using the supplied string. /// Can also be used with default empty string to /// reconnect a disconnected session. /// If the connection is not established, /// a ConnectionFailedException is thrown. /// Zero timout means indefinite void close(); /// Closes the session. bool isConnected(); /// Returns true iff session is connected, false otherwise. void reconnect(); /// Closes the session and opens it. bool isGood(); /// Returns true iff the session is good and can be used, false otherwise. bool isAutocommit() const; /// Returns true iff the session is in autocommit mode, false otherwise. /// If the session does not support autocommit, it is assumed not to /// be in auto commit mode. /// This function looks for the "autoCommit" feature and returns its value. /// It is recommended for all back-end implementations to support this feature. void setLoginTimeout(std::size_t timeout); /// Sets the session login timeout value. std::size_t getLoginTimeout() const; /// Returns the session login timeout value. void setConnectionTimeout(std::size_t timeout); /// Sets the session connection timeout value. std::size_t getConnectionTimeout(); /// Returns the session connection timeout value. void begin(); /// Starts a transaction. /// If `session` is in autocommit mode, it is switched to manual commit mode /// for the duration of the transaction and reverted back to the original mode /// after transaction completes (on rollback or commit() call). void commit(); /// Commits and ends a transaction. /// If `session` was in autocommit mode when the transaction started (begin() call), /// it is switched back to autocommit mode. void rollback(); /// Rolls back and ends a transaction. /// If `session` was in autocommit mode when the transaction started (begin() call), /// it is switched back to autocommit mode. bool canTransact(); /// Returns true if session has transaction capabilities. bool isTransaction(); /// Returns true iff a transaction is in progress, false otherwise. void setTransactionIsolation(Poco::UInt32); /// Sets the transaction isolation level. Poco::UInt32 getTransactionIsolation(); /// Returns the transaction isolation level. bool hasTransactionIsolation(Poco::UInt32 ti); /// Returns true iff the transaction isolation level corresponding /// to the supplied bitmask is supported. bool isTransactionIsolation(Poco::UInt32 ti); /// Returns true iff the transaction isolation level corresponds /// to the supplied bitmask. std::string connector() const; /// Returns the connector name for this session. std::string uri() const; /// Returns the URI for this session. static std::string uri(const std::string& connector, const std::string& connectionString); /// Utility function that teturns the URI formatted from supplied /// arguments as "connector:///connectionString". bool hasFeature(const std::string& name) const; /// Returns true if session has the named feature. void setFeature(const std::string& name, bool state); /// Set the state of a feature. /// /// Features are a generic extension mechanism for session implementations. /// and are defined by the underlying SessionImpl instance. /// /// Throws a NotSupportedException if the requested feature is /// not supported by the underlying implementation. bool getFeature(const std::string& name) const; /// Look up the state of a feature. /// /// Features are a generic extension mechanism for session implementations. /// and are defined by the underlying SessionImpl instance. /// /// Throws a NotSupportedException if the requested feature is /// not supported by the underlying implementation. bool hasProperty(const std::string& name) const; /// Returns true if session has the named property. void setProperty(const std::string& name, const Poco::Any& value); /// Set the value of a property. /// /// Properties are a generic extension mechanism for session implementations. /// and are defined by the underlying SessionImpl instance. /// /// Throws a NotSupportedException if the requested property is /// not supported by the underlying implementation. Poco::Any getProperty(const std::string& name) const; /// Look up the value of a property. /// /// Properties are a generic extension mechanism for session implementations. /// and are defined by the underlying SessionImpl instance. /// /// Throws a NotSupportedException if the requested property is /// not supported by the underlying implementation. SessionImpl* impl(); /// Returns a pointer to the underlying SessionImpl. private: Session(); Poco::AutoPtr _pImpl; StatementCreator _statementCreator; bool _wasAutoCommit = false; }; // // inlines // inline const std::string& Session::dbmsName() const { return _pImpl->dbmsName(); } inline bool Session::isAutocommit() const { return _pImpl->isAutocommit(); } inline SharedPtr Session::createStatementImpl() { return _pImpl->createStatementImpl(); } inline void Session::open(const std::string& connect) { _pImpl->open(connect); } inline void Session::close() { _pImpl->close(); } inline bool Session::isConnected() { return _pImpl->isConnected(); } inline void Session::reconnect() { _pImpl->reconnect(); } inline bool Session::isGood() { return _pImpl->isGood(); } inline void Session::setLoginTimeout(std::size_t timeout) { _pImpl->setLoginTimeout(timeout); } inline std::size_t Session::getLoginTimeout() const { return _pImpl->getLoginTimeout(); } inline void Session::setConnectionTimeout(std::size_t timeout) { _pImpl->setConnectionTimeout(timeout); } inline std::size_t Session::getConnectionTimeout() { return _pImpl->getConnectionTimeout(); } inline bool Session::canTransact() { return _pImpl->canTransact(); } inline bool Session::isTransaction() { return _pImpl->isTransaction(); } inline void Session::setTransactionIsolation(Poco::UInt32 ti) { _pImpl->setTransactionIsolation(ti); } inline Poco::UInt32 Session::getTransactionIsolation() { return _pImpl->getTransactionIsolation(); } inline bool Session::hasTransactionIsolation(Poco::UInt32 ti) { return _pImpl->hasTransactionIsolation(ti); } inline bool Session::isTransactionIsolation(Poco::UInt32 ti) { return _pImpl->isTransactionIsolation(ti); } inline std::string Session::connector() const { return _pImpl->connectorName(); } inline std::string Session::uri(const std::string& connector, const std::string& connectionString) { return SessionImpl::uri(connector, connectionString); } inline std::string Session::uri() const { return _pImpl->uri(); } inline bool Session::hasFeature(const std::string& name) const { return _pImpl->hasFeature(name); } inline void Session::setFeature(const std::string& name, bool state) { _pImpl->setFeature(name, state); } inline bool Session::getFeature(const std::string& name) const { return const_cast(_pImpl.get())->getFeature(name); } inline bool Session::hasProperty(const std::string& name) const { return _pImpl->hasProperty(name); } inline void Session::setProperty(const std::string& name, const Poco::Any& value) { _pImpl->setProperty(name, value); } inline Poco::Any Session::getProperty(const std::string& name) const { return const_cast(_pImpl.get())->getProperty(name); } inline SessionImpl* Session::impl() { return _pImpl; } inline void swap(Session& s1, Session& s2) { s1.swap(s2); } } } // namespace Poco::Data namespace std { template<> inline void swap(Poco::Data::Session& s1, Poco::Data::Session& s2) noexcept /// Full template specalization of std:::swap for Session { s1.swap(s2); } } #endif // Data_Session_INCLUDED