ROSE 0.11.145.354
Sawyer/Database.h
1// WARNING: Changes to this file must be contributed back to Sawyer or else they will
2// be clobbered by the next update from Sawyer. The Sawyer repository is at
3// https://gitlab.com/charger7534/sawyer.git.
4
5
6
7
8#ifndef Sawyer_Database_H
9#define Sawyer_Database_H
10
11#if __cplusplus >= 201103L
12
13#include <boost/iterator/iterator_facade.hpp>
14#include <boost/lexical_cast.hpp>
15#include <boost/numeric/conversion/cast.hpp>
16#include <memory.h>
17#include <Sawyer/Assert.h>
18#include <Sawyer/Map.h>
19#include <Sawyer/Optional.h>
20#include <string>
21#include <vector>
22#include <cstdint>
23
24namespace Sawyer {
25
168namespace Database {
169
170class Connection;
171class Statement;
172class Row;
173class Iterator;
174
175namespace Detail {
176 class ConnectionBase;
177 class StatementBase;
178}
179
180class Exception: public std::runtime_error {
181public:
182 Exception(const std::string &what)
183 : std::runtime_error(what) {}
184
185 ~Exception() noexcept {}
186};
187
189// Connection
191
197class Connection {
198 friend class ::Sawyer::Database::Statement;
199 friend class ::Sawyer::Database::Detail::ConnectionBase;
200
201 std::shared_ptr<Detail::ConnectionBase> pimpl_;
202
203public:
205 Connection() {};
206
207private:
208 explicit Connection(const std::shared_ptr<Detail::ConnectionBase> &pimpl);
209
210public:
214 ~Connection() = default;
215
217 static Connection fromUri(const std::string &uri);
218
222 static std::string uriDocString();
223
225 bool isOpen() const;
226
232 Connection& close();
233
248 Statement stmt(const std::string &sql);
249
251 Connection& run(const std::string &sql);
252
258 template<typename T>
259 Optional<T> get(const std::string &sql);
260
264 std::string driverName() const;
265
266 // Undocumented: Row number for the last SQL "insert" (do not use).
267 //
268 // This method is available only if the underlying database driver supports it and it has lots of caveats. In other words,
269 // don't use this method. The most portable way to identify the rows that were just inserted is to insert a UUID as part of
270 // the data.
271 size_t lastInsert() const;
272
273 // Set the pointer to implementation
274 void pimpl(const std::shared_ptr<Detail::ConnectionBase> &p) {
275 pimpl_ = p;
276 }
277};
278
280// Statement
282
287class Statement {
288 friend class ::Sawyer::Database::Detail::ConnectionBase;
289
290 std::shared_ptr<Detail::StatementBase> pimpl_;
291
292public:
294 enum State {
295 UNBOUND,
296 READY,
297 EXECUTING,
298 FINISHED,
299 DEAD
300 };
301
302public:
304 Statement() {}
305
306private:
307 explicit Statement(const std::shared_ptr<Detail::StatementBase> &stmt)
308 : pimpl_(stmt) {}
309
310public:
312 Connection connection() const;
313
323 template<typename T>
324 Statement& bind(const std::string &name, const T &value);
325
330 template<typename T>
331 Statement& rebind(const std::string &name, const T &value);
332
339 Iterator begin();
340
342 Iterator end();
343
348 Statement& run();
349
354 template<typename T>
355 Optional<T> get();
356};
357
359// Row
361
366class Row {
367 friend class ::Sawyer::Database::Iterator;
368
369 std::shared_ptr<Detail::StatementBase> stmt_;
370 size_t sequence_; // for checking validity
371
372private:
373 Row()
374 : sequence_(0) {}
375
376 explicit Row(const std::shared_ptr<Detail::StatementBase> &stmt);
377
378public:
380 template<typename T>
381 Optional<T> get(size_t columnIdx) const;
382
386 size_t rowNumber() const;
387};
388
390// Iterator
392
399class Iterator: public boost::iterator_facade<Iterator, const Row, boost::forward_traversal_tag> {
400 friend class ::Sawyer::Database::Detail::StatementBase;
401
402 Row row_;
403
404public:
406 Iterator() {}
407
408private:
409 explicit Iterator(const std::shared_ptr<Detail::StatementBase> &stmt);
410
411public:
413 bool isEnd() const {
414 return !row_.stmt_;
415 }
416
418 explicit operator bool() const {
419 return !isEnd();
420 }
421
422private:
423 friend class boost::iterator_core_access;
424 const Row& dereference() const;
425 bool equal(const Iterator&) const;
426 void increment();
427};
428
429
433//
434// Only implementation details beyond this point.
435//
439
440
441namespace Detail {
442
443// Base class for connection details. The individual drivers (SQLite3, PostgreSQL) will be derived from this class.
444//
445// Connection detail objects are reference counted. References come from only two places:
446// 1. Each top-level Connection object that's in a connected state has a reference to this connection.
447// 2. Each low-level Statement object that's in an "executing" state has a reference to this connection.
448// Additionally, all low-level statement objects have a weak reference to a connection.
449//
450class ConnectionBase: public std::enable_shared_from_this<ConnectionBase> {
451 friend class ::Sawyer::Database::Connection;
452
453protected:
454 ConnectionBase() {}
455
456public:
457 virtual ~ConnectionBase() {}
458
459protected:
460 // Close any low-level connection.
461 virtual void close() = 0;
462
463 // Create a prepared statement from the specified high-level SQL. By "high-level" we mean the binding syntax used by this
464 // API such as "?name" (whereas low-level means the syntax passed to the driver such as "?").
465 virtual Statement prepareStatement(const std::string &sql) = 0;
466
467 // Row number for the last inserted row if supported by this driver. It's better to use a table column that holds a value
468 // generated from a sequence.
469 virtual size_t lastInsert() const = 0;
470
471 Statement makeStatement(const std::shared_ptr<Detail::StatementBase> &detail);
472
473 virtual std::string driverName() const = 0;
474};
475
476// Describes the location of "?name" parameters in high-level SQL by associating them with one or more "?" parameters in
477// low-level SQL. WARNIN: the low-level parameters are numbered starting at one instead of zero, which is inconsistent with how
478// the low-level APIs index other things like query result columns (not to mention being surprising for C and C++ developers).
479class Parameter {
480 friend class ::Sawyer::Database::Detail::StatementBase;
481
482 std::vector<size_t> indexes; // "?" indexes
483 bool isBound = false;
484
485 void append(size_t idx) {
486 indexes.push_back(idx);
487 }
488};
489
490template<typename T>
491class ColumnReader {
492 friend class ::Sawyer::Database::Detail::StatementBase;
493 Optional<T> operator()(StatementBase *stmt, size_t idx);
494};
495
496//template<>
497//class ColumnReader<std::vector<uint8_t>> {
498// friend class ::Sawyer::Database::Detail::StatementBase;
499// Optional<std::vector<uint8_t>> operator()(StatementBase *stmt, size_t idx);
500//};
501
502// Reference counted prepared statement details. Objects of this class are referenced from the high-level Statement objects and
503// the query iterator rows. This class is the base class for driver-specific statements.
504class StatementBase: public std::enable_shared_from_this<StatementBase> {
505 friend class ::Sawyer::Database::Iterator;
506 friend class ::Sawyer::Database::Row;
507 friend class ::Sawyer::Database::Statement;
508 template<class T> friend class ::Sawyer::Database::Detail::ColumnReader;
509
510 using Parameters = Container::Map<std::string, Parameter>;
511
512 std::shared_ptr<ConnectionBase> connection_; // non-null while statement is executing
513 std::weak_ptr<ConnectionBase> weakConnection_; // refers to the originating connection
514 Parameters params_; // mapping from param names to question marks
515 Statement::State state_ = Statement::DEAD; // don't set directly; use "state" member function
516 size_t sequence_ = 0; // sequence number for invalidating row iterators
517 size_t rowNumber_ = 0; // result row number
518
519public:
520 virtual ~StatementBase() {}
521
522protected:
523 explicit StatementBase(const std::shared_ptr<ConnectionBase> &connection)
524 : weakConnection_(connection) { // save only a weak pointer, no shared pointer
525 ASSERT_not_null(connection);
526 }
527
528 // Parse the high-level SQL (with "?name" parameters) into low-level SQL (with "?" parameters). Returns the low-level SQL
529 // and the number of low-level "?" parameters and has the following side effects:
530 // 1. Re-initializes this object's parameter list
531 // 2. Sets this object's state to READY, UNBOUND, or DEAD.
532 std::pair<std::string, size_t> parseParameters(const std::string &highSql) {
533 params_.clear();
534 std::string lowSql;
535 bool inString = false;
536 size_t nLowParams = 0;
537 state(Statement::READY); // possibly reset below
538 for (size_t i = 0; i < highSql.size(); ++i) {
539 if ('\'' == highSql[i]) {
540 inString = !inString; // works for "''" escape too
541 lowSql += highSql[i];
542 } else if ('?' == highSql[i] && !inString) {
543 lowSql += '?';
544 std::string paramName;
545 while (i+1 < highSql.size() && (::isalnum(highSql[i+1]) || '_' == highSql[i+1]))
546 paramName += highSql[++i];
547 if (paramName.empty())
548 throw Exception("invalid parameter name at character position " + boost::lexical_cast<std::string>(i));
549 Parameter &param = params_.insertMaybeDefault(paramName);
550 param.append(nLowParams++); // 0-origin low-level parameter numbers
551 state(Statement::UNBOUND);
552 } else {
553 lowSql += highSql[i];
554 }
555 }
556 if (inString) {
557 state(Statement::DEAD);
558 throw Exception("mismatched quotes in SQL statement");
559 }
560 return std::make_pair(lowSql, nLowParams);
561 }
562
563 // Invalidate all iterators and their rows by incrementing this statements sequence number.
564 void invalidateIteratorsAndRows() {
565 ++sequence_;
566 }
567
568 // Sequence number used for checking iterator validity.
569 size_t sequence() const {
570 return sequence_;
571 }
572
573 // Cause this statement to lock the database connection by maintaining a shared pointer to the low-level
574 // connection. Returns true if the connection could be locked, or false if unable.
575 bool lockConnection() {
576 return (connection_ = weakConnection_.lock()) != nullptr;
577 }
578
579 // Release the connection lock by throwing away the shared pointer to the connection. This statement will still maintain
580 // a weak reference to the connection.
581 void unlockConnection() {
582 connection_.reset();
583 }
584
585 // Returns an indication of whether this statement holds a lock on the low-level connection, preventing the connection from
586 // being destroyed.
587 bool isConnectionLocked() const {
588 return connection_ != nullptr;
589 }
590
591 // Returns the connection details associated with this statement. The connection is not locked by querying this property.
592 std::shared_ptr<ConnectionBase> connection() const {
593 return weakConnection_.lock();
594 }
595
596 // Return the current statement state.
597 Statement::State state() const {
598 return state_;
599 }
600
601 // Change the statement state. A statement in the EXECUTING state will lock the connection to prevent it from being
602 // destroyed, but a statement in any other state will unlock the connection causing the last reference to destroy the
603 // connection and will invalidate all iterators and rows.
604 void state(Statement::State newState) {
605 switch (newState) {
606 case Statement::DEAD:
607 case Statement::FINISHED:
608 case Statement::UNBOUND:
609 case Statement::READY:
610 invalidateIteratorsAndRows();
611 unlockConnection();
612 break;
613 case Statement::EXECUTING:
614 ASSERT_require(isConnectionLocked());
615 break;
616 }
617 state_ = newState;
618 }
619
620 // Returns true if this statement has parameters that have not been bound to a value.
621 bool hasUnboundParameters() const {
622 ASSERT_forbid(state() == Statement::DEAD);
623 for (const Parameter &param: params_.values()) {
624 if (!param.isBound)
625 return true;
626 }
627 return false;
628 }
629
630 // Causes all parameters to become unbound and changes the state to either UNBOUND or READY (depending on whether there are
631 // any parameters or not, respectively).
632 virtual void unbindAllParams() {
633 ASSERT_forbid(state() == Statement::DEAD);
634 for (Parameter &param: params_.values())
635 param.isBound = false;
636 state(params_.isEmpty() ? Statement::READY : Statement::UNBOUND);
637 }
638
639 // Reset the statement by invalidating all iterators, unbinding all parameters, and changing the state to either UNBOUND or
640 // READY depending on whether or not it has any parameters.
641 virtual void reset(bool doUnbind) {
642 ASSERT_forbid(state() == Statement::DEAD);
643 invalidateIteratorsAndRows();
644 if (doUnbind) {
645 unbindAllParams();
646 } else {
647 state(hasUnboundParameters() ? Statement::UNBOUND : Statement::READY);
648 }
649 }
650
651 // Bind a value to a parameter. If isRebind is set and the statement is in the EXECUTING state, then rewind back to the
652 // READY state, preserve all previous bindings, and adjust only the specified binding.
653 template<typename T>
654 void bind(const std::string &name, const T &value, bool isRebind) {
655 if (!connection())
656 throw Exception("connection is closed");
657 switch (state()) {
658 case Statement::DEAD:
659 throw Exception("statement is dead");
660 case Statement::FINISHED:
661 case Statement::EXECUTING:
662 reset(!isRebind);
663 // fall through
664 case Statement::READY:
665 case Statement::UNBOUND: {
666 if (!params_.exists(name))
667 throw Exception("no such parameter \"" + name + "\" in statement");
668 Parameter &param = params_[name];
669 bool wasUnbound = !param.isBound;
670 for (size_t idx: param.indexes) {
671 try {
672 bindLow(idx, value);
673 } catch (const Exception &e) {
674 if (param.indexes.size() > 1)
675 state(Statement::DEAD); // might be only partly bound now
676 throw e;
677 }
678 }
679 param.isBound = true;
680
681 if (wasUnbound && !hasUnboundParameters())
682 state(Statement::READY);
683 break;
684 }
685 }
686 }
687
688 // Bind a value to an optional parameter.
689 template<typename T>
690 void bind(const std::string &name, const Sawyer::Optional<T> &value, bool isRebind) {
691 if (value) {
692 bind(name, *value, isRebind);
693 } else {
694 bind(name, Nothing(), isRebind);
695 }
696 }
697
698 // Driver-specific part of binding by specifying the 0-origin low-level "?" number and the value.
699 virtual void bindLow(size_t idx, int value) = 0;
700 virtual void bindLow(size_t idx, int64_t value) = 0;
701 virtual void bindLow(size_t idx, size_t value) = 0;
702 virtual void bindLow(size_t idx, double value) = 0;
703 virtual void bindLow(size_t idx, const std::string &value) = 0;
704 virtual void bindLow(size_t idx, const char *cstring) = 0;
705 virtual void bindLow(size_t idx, Nothing) = 0;
706 virtual void bindLow(size_t idx, const std::vector<uint8_t> &data) = 0;
707
708 Iterator makeIterator() {
709 return Iterator(shared_from_this());
710 }
711
712 // Begin execution of a statement in the READY state. If the statement is in the FINISHED or EXECUTING state it will be
713 // restarted.
714 Iterator begin() {
715 if (!connection())
716 throw Exception("connection is closed");
717 switch (state()) {
718 case Statement::DEAD:
719 throw Exception("statement is dead");
720 case Statement::UNBOUND: {
721 std::string s;
722 for (Parameters::Node &param: params_.nodes()) {
723 if (!param.value().isBound)
724 s += (s.empty() ? "" : ", ") + param.key();
725 }
726 ASSERT_forbid(s.empty());
727 throw Exception("unbound parameters: " + s);
728 }
729 case Statement::FINISHED:
730 case Statement::EXECUTING:
731 reset(false);
732 // fall through
733 case Statement::READY: {
734 if (!lockConnection())
735 throw Exception("connection has been closed");
736 state(Statement::EXECUTING);
737 rowNumber_ = 0;
738 Iterator iter = beginLow();
739 rowNumber_ = 0; // in case beginLow changed it
740 return iter;
741 }
742 }
743 ASSERT_not_reachable("invalid state");
744 }
745
746 // The driver-specific component of "begin". The statement is guaranteed to be in the EXECUTING state when called,
747 // but could be in some other state after returning.
748 virtual Iterator beginLow() = 0;
749
750 // Advance an executing statement to the next row
751 Iterator next() {
752 if (!connection())
753 throw Exception("connection is closed");
754 ASSERT_require(state() == Statement::EXECUTING); // no other way to get here
755 invalidateIteratorsAndRows();
756 ++rowNumber_;
757 return nextLow();
758 }
759
760 // Current row number
761 size_t rowNumber() const {
762 return rowNumber_;
763 }
764
765 // The driver-specific component of "next". The statement is guaranteed to be in the EXECUTING state when called, but
766 // could be in some other state after returning.
767 virtual Iterator nextLow() = 0;
768
769 // Get a column value from the current row of result
770 template<typename T>
771 Optional<T> get(size_t columnIdx) {
772 if (!connection())
773 throw Exception("connection is closed");
774 ASSERT_require(state() == Statement::EXECUTING); // no other way to get here
775 if (columnIdx >= nColumns())
776 throw Exception("column index " + boost::lexical_cast<std::string>(columnIdx) + " is out of range");
777 return ColumnReader<T>()(this, columnIdx);
778 }
779
780 // Number of columns returned by a query.
781 virtual size_t nColumns() const = 0;
782
783 // Get the value of a particular column of the current row.
784 virtual Optional<std::string> getString(size_t idx) = 0;
785 virtual Optional<std::vector<std::uint8_t>> getBlob(size_t idx) = 0;
786};
787
788template<typename T>
789inline Optional<T>
790ColumnReader<T>::operator()(StatementBase *stmt, size_t idx) {
791 std::string str;
792 if (!stmt->getString(idx).assignTo(str))
793 return Nothing();
794 return boost::lexical_cast<T>(str);
795}
796
797template<>
798inline Optional<std::vector<uint8_t>>
799ColumnReader<std::vector<uint8_t>>::operator()(StatementBase *stmt, size_t idx) {
800 return stmt->getBlob(idx);
801}
802
803inline Statement
804ConnectionBase::makeStatement(const std::shared_ptr<Detail::StatementBase> &detail) {
805 return Statement(detail);
806}
807
808} // namespace
809
811// Implementations Connection
813
814
815inline Connection::Connection(const std::shared_ptr<Detail::ConnectionBase> &pimpl)
816 : pimpl_(pimpl) {}
817
818inline bool
819Connection::isOpen() const {
820 return pimpl_ != nullptr;
821}
822
823inline Connection&
824Connection::close() {
825 pimpl_ = nullptr;
826 return *this;
827}
828
829inline std::string
830Connection::driverName() const {
831 if (pimpl_) {
832 return pimpl_->driverName();
833 } else {
834 return "";
835 }
836}
837
838inline Statement
839Connection::stmt(const std::string &sql) {
840 if (pimpl_) {
841 return pimpl_->prepareStatement(sql);
842 } else {
843 throw Exception("no active database connection");
844 }
845}
846
847inline Connection&
848Connection::run(const std::string &sql) {
849 stmt(sql).begin();
850 return *this;
851}
852
853template<typename T>
854inline Optional<T>
855Connection::get(const std::string &sql) {
856 for (auto row: stmt(sql))
857 return row.get<T>(0);
858 return Nothing();
859}
860
861inline size_t
862Connection::lastInsert() const {
863 if (pimpl_) {
864 return pimpl_->lastInsert();
865 } else {
866 throw Exception("no active database connection");
867 }
868}
869
871// Implementations for Statement
873
874inline Connection
875Statement::connection() const {
876 if (pimpl_) {
877 return Connection(pimpl_->connection());
878 } else {
879 return Connection();
880 }
881}
882
883template<typename T>
884inline Statement&
885Statement::bind(const std::string &name, const T &value) {
886 if (pimpl_) {
887 pimpl_->bind(name, value, false);
888 } else {
889 throw Exception("no active database connection");
890 }
891 return *this;
892}
893
894template<typename T>
895inline Statement&
896Statement::rebind(const std::string &name, const T &value) {
897 if (pimpl_) {
898 pimpl_->bind(name, value, true);
899 } else {
900 throw Exception("no active database connection");
901 }
902 return *this;
903}
904
905inline Iterator
906Statement::begin() {
907 if (pimpl_) {
908 return pimpl_->begin();
909 } else {
910 throw Exception("no active database connection");
911 }
912}
913
914inline Iterator
915Statement::end() {
916 return Iterator();
917}
918
919inline Statement&
920Statement::run() {
921 begin();
922 return *this;
923}
924
925template<typename T>
926inline Optional<T>
927Statement::get() {
928 Iterator row = begin();
929 if (row.isEnd())
930 throw Exception("query did not return a row");
931 return row->get<T>(0);
932}
933
935// Implementations for Iterator
937
938inline
939Iterator::Iterator(const std::shared_ptr<Detail::StatementBase> &stmt)
940 : row_(stmt) {}
941
942inline const Row&
943Iterator::dereference() const {
944 if (isEnd())
945 throw Exception("dereferencing the end iterator");
946 if (row_.sequence_ != row_.stmt_->sequence())
947 throw Exception("iterator has been invalidated");
948 return row_;
949}
950
951inline bool
952Iterator::equal(const Iterator &other) const {
953 return row_.stmt_ == other.row_.stmt_ && row_.sequence_ == other.row_.sequence_;
954}
955
956inline void
957Iterator::increment() {
958 if (isEnd())
959 throw Exception("incrementing the end iterator");
960 *this = row_.stmt_->next();
961}
962
964// Implementations for Row
966
967inline
968Row::Row(const std::shared_ptr<Detail::StatementBase> &stmt)
969 : stmt_(stmt), sequence_(stmt ? stmt->sequence() : 0) {}
970
971template<typename T>
972inline Optional<T>
973Row::get(size_t columnIdx) const {
974 ASSERT_not_null(stmt_);
975 if (sequence_ != stmt_->sequence())
976 throw Exception("row has been invalidated");
977 return stmt_->get<T>(columnIdx);
978}
979
980inline size_t
981Row::rowNumber() const {
982 ASSERT_not_null(stmt_);
983 if (sequence_ != stmt_->sequence())
984 throw Exception("row has been invalidated");
985 return stmt_->rowNumber();
986}
987
988} // namespace
989} // namespace
990
991#endif
992#endif
Holds a value or nothing.
Definition Optional.h:54
bool get(const Word *words, size_t idx)
Return a single bit.
bool increment(Word *vec1, const BitRange &range1)
Increment.
Sawyer support library.