1 // This may look like C code, but it's really -*- C++ -*- 2 /* 3 * Copyright (C) 2008 Emweb bv, Herent, Belgium. 4 * 5 * See the LICENSE file for terms of use. 6 */ 7 #ifndef WT_DBO_SESSION_H_ 8 #define WT_DBO_SESSION_H_ 9 10 #include <map> 11 #include <set> 12 #include <string> 13 #include <typeinfo> 14 15 #include <Wt/Dbo/ptr.h> 16 #include <Wt/Dbo/Field.h> 17 #include <Wt/Dbo/Query.h> 18 #include <Wt/Dbo/Transaction.h> 19 #include <Wt/Dbo/SqlConnection.h> 20 21 namespace Wt { 22 namespace Dbo { 23 namespace Impl { 24 struct MetaDboBaseSet; 25 26 extern WTDBO_API std::string quoteSchemaDot(const std::string& table); 27 template <class C, typename T> struct LoadHelper; 28 29 struct WTDBO_API SetInfo { 30 enum SetInfoFlags { 31 // Normally, if there is no surrogate key, the field name of the natural id 32 // is appended to joinSelfId/joinOtherId. These flags prevent that. 33 LiteralSelfId = 0x1, // joinSelfId is literal, don't append primary key name 34 LiteralOtherId = 0x2 // joinOtherId is literal, don't append primary key name 35 }; 36 37 const char *tableName; 38 std::string joinName; 39 std::string joinSelfId, joinOtherId; 40 int flags; 41 RelationType type; 42 int fkConstraints, otherFkConstraints; 43 44 SetInfo(const char *aTableName, RelationType type, 45 const std::string& aJoinName, 46 const std::string& aJoinSelfId, 47 int someFkConstraints); 48 }; 49 50 struct WTDBO_API MappingInfo { 51 bool initialized_; 52 const char *tableName; 53 const char *versionFieldName; 54 const char *surrogateIdFieldName; 55 56 std::string naturalIdFieldName; // for non-auto generated id 57 int naturalIdFieldSize; // for non-auto generated id 58 59 std::string idCondition; 60 61 std::vector<FieldInfo> fields; 62 std::vector<SetInfo> sets; 63 64 std::vector<std::string> statements; 65 66 MappingInfo(); 67 virtual ~MappingInfo(); 68 virtual void init(Session& session); 69 virtual void dropTable(Session& session, 70 std::set<std::string>& tablesDropped); 71 virtual void rereadAll(); 72 virtual MetaDboBase *create(Session& session); 73 virtual void load(Session& session, MetaDboBase *obj); 74 virtual MetaDboBase *load(Session& session, SqlStatement *statement, 75 int& column); 76 77 std::string primaryKeys() const; 78 }; 79 } 80 81 struct Null { 82 static Null null_; 83 }; 84 85 /*! \brief Enumeration that indicates the flush mode. 86 * 87 * \sa setFlushMode(), discardUnflushed() 88 */ 89 enum class FlushMode { 90 Auto, //!< Dbo decides when to flush changes to a transaction 91 Manual //!< Changes are never automatically flushed 92 }; 93 94 class Call; 95 //class SqlConnection; 96 class SqlConnectionPool; 97 class SqlStatement; 98 template <typename Result, typename BindStrategy> class Query; 99 struct DirectBinding; 100 struct DynamicBinding; 101 102 /*! \class Session Wt/Dbo/Session.h Wt/Dbo/Session.h 103 * \brief A database session. 104 * 105 * A database session manages meta data about the mapping of C++ 106 * classes to database tables, and keeps track of a working set of 107 * in-memory objects (objects which are referenced from your code or 108 * from within a transaction). 109 * 110 * It also manages an active transaction, which you need to access 111 * database objects. 112 * 113 * You can provide the session with a dedicated database connection 114 * using setConnection(), or with a connection pool (from which it 115 * will take a connection while processing a transaction) using 116 * setConnectionPool(). 117 * 118 * A session will typically be a long-lived object in your 119 * application. 120 * 121 * \ingroup dbo 122 */ 123 class WTDBO_API Session 124 { 125 public: 126 /*! \brief Creates a database session. 127 */ 128 Session(); 129 130 /*! \brief Destructor. 131 * 132 * A session must survive all database objects that have been loaded 133 * through it, and will warning during this destructor if there are 134 * still database objects that are being referenced from a ptr. 135 */ 136 virtual ~Session(); 137 138 // Sessions are not copyable 139 Session(const Session &) = delete; 140 Session& operator=(const Session &) = delete; 141 142 // Sessions are not movable 143 Session(Session &&) = delete; 144 Session& operator=(Session &&) = delete; 145 146 /*! \brief Sets a dedicated connection. 147 * 148 * The connection will be used exclusively by this session. 149 * 150 * \sa setConnectionPool() 151 */ 152 void setConnection(std::unique_ptr<SqlConnection> connection); 153 154 /*! \brief Sets a connection pool. 155 * 156 * The connection pool is typically shared with other sessions. 157 * 158 * \sa setConnection() 159 */ 160 void setConnectionPool(SqlConnectionPool& pool); 161 162 /*! \brief Maps a class to a database table. 163 * 164 * The class \p C is mapped to table with name \p tableName. You 165 * need to map classes to tables. 166 * 167 * You may provide a schema-qualified table name, if the underlying 168 * database supports this, eg. <tt>"myschema.users"</tt>. 169 */ 170 template <class C> void mapClass(const char *tableName); 171 172 /*! \brief Returns the mapped table name for a class. 173 * 174 * \sa mapClass(), tableNameQuoted() 175 */ 176 template <class C> const char *tableName() const; 177 178 /*! \brief Returns the mapped quoted table name for a class. 179 * 180 * This will quote schemas, as necessary. 181 * 182 * \sa mapClass(), tableName() 183 */ 184 template <class C> const std::string tableNameQuoted() const; 185 186 /*! \brief Persists a transient object. 187 * 188 * The transient object pointed to by \p ptr is added to the 189 * session, and will be persisted when the session is flushed. 190 * 191 * A transient object is usually a newly created object which want 192 * to add to the database. 193 * 194 * The method returns \p ptr. 195 */ 196 template <class C> ptr<C> add(ptr<C>& ptr); 197 198 /*! \brief Persists a transient object. 199 * 200 * This is an overloaded method for convenience, and is implemented as: 201 * \code 202 * return add(ptr<C>(std::move(obj))); 203 * \endcode 204 * 205 * The method returns a database pointer to the object. 206 */ 207 template <class C> ptr<C> add(std::unique_ptr<C> obj); 208 209 /*! \brief Persists a transient object. 210 * 211 * This an overloaded method for convenience, and is implemented as: 212 * \code 213 * return add(ptr<C>(std::unique_ptr<T>(new T(...)))) 214 * \endcode 215 * 216 * \sa Wt::Dbo::make_ptr() 217 */ 218 template<typename T, typename ...Args> addNew(Args &&...args)219 ptr<T> addNew( Args&& ...args ) 220 { 221 return add(std::unique_ptr<T>(new T(std::forward<Args>(args)...))); 222 } 223 224 /*! \brief Loads a persisted object. 225 * 226 * This method returns a database object with the given object 227 * id. If the object was already loaded in the session, the loaded 228 * object is returned, otherwise the object is loaded from the 229 * database. 230 * 231 * If \p forceReread is set to \c true, then a fresh copy is loaded 232 * from the database. This is almost equivalent to calling \link 233 * ptr<C>::reread() reread()\endlink on the returned object, except 234 * that it will not result in two database reads in case the object was 235 * in fact not yet loaded in the session. 236 * 237 * Throws an ObjectNotFoundException when the object was not found. 238 * 239 * \sa ptr::id(), loadLazy() 240 */ 241 template <class C> ptr<C> load(const typename dbo_traits<C>::IdType& id, 242 bool forceReread = false); 243 244 /*! \brief Lazy loads a persisted object. 245 * 246 * This method returns a database object with the given object id 247 * without directly accessing the database. 248 * 249 * If the object data is already available in the session, then it will 250 * be available upon return. Otherwise, the object data will be retrieved 251 * from the database on first access. Note: This will result in an 252 * ObjectNotFoundException if the object id is not valid. 253 * 254 * lazyLoad can be used to obtain a ptr<C> from a known id when a ptr<C> 255 * is required, but access to the C object is not anticipated. For 256 * instance, a ptr<C> may be required to add an object of class X that 257 * is in a belongsTo relationship with C. 258 * 259 * \sa ptr::id(), load() 260 */ 261 template <class C> ptr<C> loadLazy(const typename dbo_traits<C>::IdType& id); 262 263 #ifndef DOXYGEN_ONLY 264 template <class C> 265 Query< ptr<C> > find(const std::string& condition = std::string()) { 266 // implemented in-line because otherwise it crashes gcc 4.0.1 267 return find<C, DynamicBinding>(condition); 268 } 269 #endif // DOXYGEN_ONLY 270 271 /*! \brief Finds database objects. 272 * 273 * This method creates a query for finding objects of type \p C. 274 * 275 * When passing an empty \p condition parameter, it will return all 276 * objects of type \p C. Otherwise, it will add the condition, by 277 * generating an SQL <i>where</i> clause. 278 * 279 * The \p BindStrategy specifies how you want to bind parameters to 280 * your query (if any). 281 * 282 * When using \p DynamicBinding (which is the default), you will 283 * defer the binding until the query is run. This has the advantage 284 * that you can compose the query definition using helper methods 285 * provided in the query object, you can keep the query around and 286 * run the query multiple times, perhaps with different parameter 287 * values or to scroll through the query results. 288 * 289 * When using \p DirectBinding, the query must be specified entirely 290 * using the \p condition, and can be run only once. This method 291 * does have the benefit of binding parameters directly to the 292 * underlying prepared statement. 293 * 294 * This method is convenient when you are querying only results from a 295 * single table. For more generic query support, see query(). 296 * 297 * Usage example: 298 * \code 299 * // Bart is missing, let's find him. 300 * Wt::Dbo::ptr<User> bart = session.find<User>().where("name = ?").bind("Bart"); 301 * 302 * // Find all users, order by name 303 * typedef Wt::Dbo::collection< Wt::Dbo::ptr<User> > Users; 304 * Users users = session.find<User>().orderBy("name"); 305 * \endcode 306 * 307 * In the \p condition, parameters can be bound using '?' as a 308 * positional placeholder: each occurence of '?' (as a lexical 309 * token) is replaced by a bound parameter. This is actually done by 310 * most of the backends themselves using prepared statements and 311 * parameter binding. Parameter binding is possible for all types 312 * for which sql_value_traits is specialized. 313 * 314 * \sa query() 315 */ 316 #ifdef DOXYGEN_ONLY 317 template <class C, typename BindStrategy = DynamicBinding> 318 #else 319 template <class C, typename BindStrategy> 320 #endif 321 Query< ptr<C>, BindStrategy> 322 find(const std::string& condition = std::string()); 323 324 #ifndef DOXYGEN_ONLY 325 template <class Result> Query<Result> query(const std::string& sql); 326 #endif // DOXYGEN_ONLY 327 328 /*! \brief Creates a query. 329 * 330 * The sql statement should be a complete SQL statement, starting 331 * with a "select ". The items listed in the "select" must match the 332 * \p Result type. An item that corresponds to a database object 333 * (ptr) is substituted with the selection of all the fields in the 334 * dbo. 335 * 336 * For example, the following query (class User is mapped onto table 'user'): 337 * \code 338 * session.query< ptr<User> >("select u from user u").where("u.name = ?").bind("Bart"); 339 * \endcode 340 * is the more general version of: 341 * \code 342 * session.find<User>().where("name = ?").bind("Bart"); 343 * \endcode 344 * 345 * Note that "u" in this query will be expanded to select the fields of the 346 * user table (u.id, u.version, u.name, ...). The same expansion happens when 347 * using an alias in Query::groupBy(). 348 * 349 * The additional flexibility offered by %query() over find() is 350 * however that it may support other result types. 351 * 352 * Thus, it may return plain values: 353 * \code 354 * session.query<int>("select count(1) from ..."); 355 * \endcode 356 * 357 * Or std::tuple for an arbitrary combination of result values: 358 * 359 * \code 360 * session.query< std::tuple<int, int> >("select A.id, B.id from table_a A, table_b B").where("..."); 361 * \endcode 362 * 363 * A tuple may combine any kind of object that is supported as a result, 364 * including database objects (see also ptr_tuple): 365 * \code 366 * session.query< std::tuple<ptr<A>, ptr<B> > >("select A, B from table_a A, table_b B").where("..."); 367 * \endcode 368 * 369 * The \p BindStrategy specifies how you want to bind parameters to 370 * your query (if any). 371 * 372 * When using \p DynamicBinding (which is the default), you will 373 * defer the binding until the query is run. This has the advantage 374 * that you can compose the query using helper methods provided in 375 * the Query object, you can keep the query around and run the query 376 * multiple times, perhaps with different parameter values or to 377 * scroll through the query results. 378 * 379 * When using \p DirectBinding, the query must be specified entirely 380 * using the \p sql, and can be run only once. This method does have 381 * the benefit of binding parameters directly to the underlying 382 * prepared statement. 383 * 384 * This method uses query_result_traits to unmarshal the query result 385 * into the \p Result type. 386 * 387 * In the \p sql query, parameters can be bound using '?' as the 388 * positional placeholder: each occurence of '?' (as a lexical 389 * token) is replaced by a bound parameter. This is actually done by 390 * most of the backends themselves using prepared statements and 391 * parameter binding. Parameter binding is possible for all types 392 * for which sql_value_traits is specialized. 393 * 394 * \note PostgreSQL uses a literal question mark ('?') as an operator. To 395 * distinguish between a positional placeholder and a literal '?', use a 396 * double question mark ('??') if you mean a literal '?'. 397 * 398 * \note The query must be a ASCII-7 string: UTF-8 is not supported by 399 * the underlying query parser. To add a non-English string to the query 400 * use parameter binding instead (which prevents against SQL injection 401 * attacks at the same time) instead of string concatenation. 402 */ 403 #ifdef DOXYGEN_ONLY 404 template <class Result, typename BindStrategy = DynamicBinding> 405 #else 406 template <class Result, typename BindStrategy> 407 #endif 408 Query<Result, BindStrategy> query(const std::string& sql); 409 410 /*! \brief Executs an Sql command. 411 * 412 * This executs an Sql command. It differs from query() in that no 413 * result is expected from the call. 414 * 415 * Usage example: 416 * \code 417 * session.execute("update user set name = ? where name = ?").bind("Bart").bind("Sarah"); 418 * \endcode 419 */ 420 Call execute(const std::string& sql); 421 422 /*! \brief Creates the database schema. 423 * 424 * This will create the database schema of the mapped tables. Schema 425 * creation will fail if one or more tables already existed. The creation 426 * of the tables is executed in a transaction that is rolled back when 427 * an error occurs. 428 * 429 * This method throws an Wt::Dbo::Exception if the table creation failed. 430 * 431 * \sa mapClass(), dropTables() 432 */ 433 void createTables(); 434 435 /*! \brief Returns database creation SQL. 436 */ 437 std::string tableCreationSql(); 438 439 /*! \brief Drops the database schema. 440 * 441 * This will drop the database schema. Dropping the schema will fail 442 * if one or more tables did not exist. 443 * 444 * \sa createTables() 445 */ 446 void dropTables(); 447 448 /*! \brief Flushes the session. 449 * 450 * This flushes all modified objects to the database. This does not 451 * commit the transaction. 452 * 453 * Normally, you need not to call this method as the session is 454 * flushed automatically before committing a transaction, or before 455 * running a query (to be sure to take into account pending 456 * modifications). 457 */ 458 void flush(); 459 460 /*! \brief Rereads all objects. 461 * 462 * This rereads all objects from the database, possibly discarding 463 * unflushed modifications. This is a catch-all solution for a 464 * StaleObjectException. 465 * 466 * If a \p tableName is given, then only objects of that table are 467 * reread. 468 * 469 * \sa ptr::reread() 470 */ 471 void rereadAll(const char *tableName = nullptr); 472 473 /*! \brief Discards all unflushed changes. 474 * 475 * This method is useful when the flushMode() is set to Manual. It discards 476 * all Dbo-objects which were added to the session and rereads all existing 477 * Dbo-objects. 478 * 479 * \sa setFlushMode() 480 */ 481 void discardUnflushed(); 482 483 void getFields(const char *tableName, std::vector<FieldInfo>& result); 484 485 /*! \brief Returns the flushMode. 486 * 487 * \sa setFlushMode() 488 */ flushMode()489 FlushMode flushMode() { return flushMode_; } 490 491 /*! \brief Sets the flushMode. 492 * 493 * The default flushMode is Auto. LabelOption::Inside a transaction this means that 494 * changes are flushed when a query is affected by them. When flushMode is 495 * set to Manual, changes are only flushed when the user manually calls 496 * flush(), or resets the mode to Auto. Query's wil possibly return an 497 * inconsistent result, but collections will still keep track of changes. 498 * This also makes it possible to operate on Dbo-objects and collections 499 * outside of a transaction. When the moment comes to flush the changes, a 500 * transaction must of course be active. 501 * 502 * <em>Note:</em> only operations on a collecion are tracked in Manual mode, 503 * reciproke operations are not yet taken into account. 504 * 505 * \sa flushMode(), discardUnflushed() 506 */ setFlushMode(FlushMode mode)507 void setFlushMode(FlushMode mode) { flush(); flushMode_ = mode; } 508 509 private: 510 mutable std::string longlongType_; 511 mutable std::string intType_; 512 mutable bool haveSupportUpdateCascade_; 513 514 enum { SqlInsert = 0, 515 SqlUpdate = 1, 516 SqlDelete = 2, 517 SqlDeleteVersioned = 3, 518 SqlSelectById = 4, 519 FirstSqlSelectSet = 5 }; 520 521 struct JoinId { 522 std::string joinIdName; 523 std::string tableIdName; 524 std::string sqlType; 525 526 JoinId(const std::string& aJoinIdName, 527 const std::string& aTableIdName, 528 const std::string& aSqlType); 529 }; 530 531 template <class C> 532 struct Mapping : public Impl::MappingInfo 533 { 534 typedef std::map<typename dbo_traits<C>::IdType, MetaDbo<C> *> Registry; 535 Registry registry_; 536 537 virtual ~Mapping(); 538 virtual void init(Session& session) override; 539 virtual void dropTable(Session& session, 540 std::set<std::string>& tablesDropped) override; 541 virtual void rereadAll() override; 542 virtual MetaDbo<C> *create(Session& session) override; 543 virtual void load(Session& session, MetaDboBase *obj) override; 544 virtual MetaDbo<C> *load(Session& session, SqlStatement *statement, 545 int& column) override; 546 }; 547 548 typedef const std::type_info * const_typeinfo_ptr; 549 struct typecomp { operatortypecomp550 bool operator() (const const_typeinfo_ptr& lhs, const const_typeinfo_ptr& rhs) const { return lhs->before(*rhs) != 0; 551 } 552 }; 553 554 typedef std::map<const_typeinfo_ptr, 555 Impl::MappingInfo *, typecomp> ClassRegistry; 556 typedef std::map<std::string, Impl::MappingInfo *> TableRegistry; 557 558 ClassRegistry classRegistry_; 559 TableRegistry tableRegistry_; 560 bool schemaInitialized_; 561 mutable LimitQuery limitQueryMethod_; 562 mutable bool requireSubqueryAlias_; 563 564 Impl::MetaDboBaseSet *dirtyObjects_; 565 std::vector<MetaDboBase*> objectsToAdd_; 566 std::unique_ptr<SqlConnection> connection_; 567 SqlConnectionPool *connectionPool_; 568 Transaction::Impl *transaction_; 569 FlushMode flushMode_; 570 571 void initSchema() const; 572 void resolveJoinIds(Impl::MappingInfo *mapping); 573 void prepareStatements(Impl::MappingInfo *mapping); 574 575 void executeSql(std::vector<std::string> &sql, std::ostream *sout); 576 void executeSql(std::stringstream &sql, std::ostream *sout); 577 std::string constraintName(const char *tableName, 578 std::string foreignKeyName); 579 580 std::vector<JoinId> getJoinIds(Impl::MappingInfo *mapping, 581 const std::string& joinId, 582 bool literalJoinId); 583 584 void createTable(Impl::MappingInfo *mapping, 585 std::set<std::string>& tablesCreated, 586 std::ostream *sout, 587 bool createConstraints); 588 void createRelations(Impl::MappingInfo *mapping, 589 std::set<std::string>& tablesCreated, 590 std::ostream *sout); 591 std::string constraintString(Impl::MappingInfo *mapping, 592 const FieldInfo& field, 593 unsigned fromIndex, 594 unsigned toIndex); 595 unsigned findLastForeignKeyField(Impl::MappingInfo *mapping, 596 const FieldInfo& field, 597 unsigned index); 598 void createJoinTable(const std::string& joinName, 599 Impl::MappingInfo *mapping1, Impl::MappingInfo *mapping2, 600 const std::string& joinId1, 601 const std::string& joinId2, 602 int fkConstraints1, int fkConstraints2, 603 bool literalJoinId1, bool literalJoinId2, 604 std::set<std::string>& tablesCreated, 605 std::ostream *sout); 606 void addJoinTableFields(Impl::MappingInfo& joinTableMapping, 607 Impl::MappingInfo *mapping, const std::string& joinId, 608 const std::string& foreignKeyName, int fkConstraints, 609 bool literalJoinId); 610 void createJoinIndex(Impl::MappingInfo& joinTableMapping, 611 Impl::MappingInfo *mapping, 612 const std::string& joinId, 613 const std::string& foreignKeyName, 614 std::ostream *sout); 615 616 void needsFlush(MetaDboBase *dbo); 617 618 template <class C> Mapping<C> *getMapping() const; 619 Impl::MappingInfo *getMapping(const char *tableName) const; 620 621 void load(MetaDboBase *obj); 622 template <class C> ptr<C> load(SqlStatement *statement, int& column); 623 624 template <class C> 625 MetaDbo<C> *loadWithNaturalId(SqlStatement *statement, int& column); 626 template <class C> 627 MetaDbo<C> *loadWithLongLongId(SqlStatement *statement, int& column); 628 629 void discardChanges(MetaDboBase *obj); 630 template <class C> void prune(MetaDbo<C> *obj); 631 632 template<class C> void implSave(MetaDbo<C>& dbo); 633 template<class C> void implDelete(MetaDbo<C>& dbo); 634 template<class C> void implTransactionDone(MetaDbo<C>& dbo, bool success); 635 template<class C> void implLoad(MetaDbo<C>& dbo, SqlStatement *statement, 636 int& column); 637 638 static std::string statementId(const char *table, int statementIdx); 639 640 template <class C> SqlStatement *getStatement(int statementIdx); 641 SqlStatement *getStatement(const std::string& id); 642 SqlStatement *getStatement(const char *tableName, int statementIdx); 643 const std::string& getStatementSql(const char *tableName, int statementIdx); 644 645 SqlStatement *prepareStatement(const std::string& id, 646 const std::string& sql); 647 SqlStatement *getOrPrepareStatement(const std::string& sql); 648 649 template <class C> void prepareStatements(); 650 template <class C> std::string manyToManyJoinId(const std::string& joinName, 651 const std::string& notId); 652 653 std::unique_ptr<SqlConnection> useConnection(); 654 void returnConnection(std::unique_ptr<SqlConnection> connection); 655 SqlConnection *connection(bool openTransaction); 656 657 MetaDboBase *createDbo(Impl::MappingInfo *mapping); 658 659 template <class C> friend class MetaDbo; 660 template <class C> friend class collection; 661 template <class C> friend class weak_ptr; 662 template <class C, typename S> friend class Query; 663 friend class AbstractQuery; 664 template <class C> friend class Impl::QueryBase; 665 template <class C, typename T> friend struct Impl::LoadHelper; 666 template <typename V> friend class FieldRef; 667 template <class C> friend struct query_result_traits; 668 template <class C> friend class SaveDbAction; 669 template <class C> friend class LoadDbAction; 670 template <class C> friend class PtrRef; 671 friend class SetReciproceAction; 672 friend class ToAnysAction; 673 friend class FromAnysAction; 674 675 friend class Call; 676 friend class CollectionHelper; 677 friend class DboAction; 678 friend class DropSchema; 679 friend class FromAnyAction; 680 friend class InitSchema; 681 friend class LoadBaseAction; 682 friend class MetaDboBase; 683 friend class SaveBaseAction; 684 friend class SessionAddAction; 685 friend class Transaction; 686 friend class TransactionDoneAction; 687 688 friend struct Transaction::Impl; 689 }; 690 691 } 692 } 693 694 #endif // WT_SESSION_H_ 695