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