#pragma once #include #include #include #include #include #include #include #include #ifdef FBSQLXX_DEBUG #include #endif // FBSQLXX_DEBUG #ifndef FBSQLXX_EXCEPTION_BUFFER_SIZE #define FBSQLXX_EXCEPTION_BUFFER_SIZE 1024 #endif // !FBSQLXX_EXCEPTION_BUFFER_SIZE namespace fbsqlxx2 { #ifdef FBSQLXX_DEBUG template void debug(Args&& ...args) { (std::cout << ... << std::forward(args)) << std::endl; } void debug(const char* s) { std::cout << s << std::endl; } std::string type_name(unsigned int type); inline void print_metadata(Firebird::IMessageMetadata* md, Firebird::ThrowStatusWrapper* status) { auto count = md->getCount(status); std::cout << "count:" << count << " mlen:" << md->getMessageLength(status) << " alen:" << md->getAlignedLength(status) << " align:" << md->getAlignment(status) << std::endl; for (unsigned i = 0; i < count; i++) { std::cout << "#" << i << ": " << md->getAlias(status, i) << " type:" << type_name(md->getType(status, i) & ~1u) << " subtype:" << md->getSubType(status, i) << " scale:" << md->getScale(status, i) << " len:" << md->getLength(status, i) << " off:" << md->getOffset(status, i) << " noff:" << md->getNullOffset(status, i) << " rel:" << md->getRelation(status, i) << " own:" << md->getOwner(status, i) << " nullable:" << (md->isNullable(status, i) ? "T" : "F") << std::endl; } } #else #define debug(...) #define print_metadata(...) #endif // FBSQLXX_DEBUG class connection; class transaction; class statement; class result_set; static inline Firebird::IMaster* master() { static Firebird::IMaster* _master = Firebird::fb_get_master_interface(); return _master; } static inline Firebird::IUtil* util() { static Firebird::IUtil* _util = master()->getUtilInterface(); return _util; } static inline int64_t portable_integer(const uint8_t* p, short length) { return isc_portable_integer(p, length); } // exceptions class error : public std::runtime_error { public: error(const char* msg) : std::runtime_error(msg) { } }; class sql_error : public error { public: sql_error(const char* msg, const Firebird::FbException* cause) : error(msg), m_cause{ cause } { } const Firebird::FbException* cause() const { return m_cause; } private: const Firebird::FbException* m_cause; }; class logic_error : public error { public: logic_error(const char* msg) : error(msg) { } }; #define CATCH_SQL \ catch (const Firebird::FbException& ex) { \ char buf[FBSQLXX_EXCEPTION_BUFFER_SIZE]; \ util()->formatStatus(buf, sizeof(buf), ex.getStatus()); \ throw sql_error{ buf, &ex }; } inline std::string type_name(unsigned int type) { switch (type & ~1u) { case SQL_ARRAY: return "ARRAY"; case SQL_BLOB: return "BLOB"; case SQL_BOOLEAN: return "BOOLEAN"; case SQL_DEC16: return "DEC16"; case SQL_DEC34: return "DEC34"; case SQL_DOUBLE: return "DOUBLE"; case SQL_D_FLOAT: return "D_FLOAT"; case SQL_FLOAT: return "FLOAT"; case SQL_INT128: return "INT128"; case SQL_INT64: return "BIGINT"; case SQL_LONG: return "INT"; case SQL_SHORT: return "SMALLINT"; case SQL_TEXT: return "CHAR"; case SQL_TIMESTAMP: return "TIMESTAMP"; case SQL_TIMESTAMP_TZ: return "TIMESTAMP_TZ"; case SQL_TIMESTAMP_TZ_EX: return "TIMESTAMP_TZ_EX"; case SQL_TIME_TZ: return "TIME_TZ"; case SQL_TIME_TZ_EX: return "TIME_TZ_EX"; case SQL_TYPE_DATE: return "DATE"; case SQL_TYPE_TIME: return "TIME"; case SQL_VARYING: return "VARCHAR"; default: break; } return "UNKNOWN"; } namespace _details { class non_copyable { protected: non_copyable() = default; ~non_copyable() = default; public: non_copyable(const non_copyable&) = delete; non_copyable& operator=(const non_copyable&) = delete; }; template < typename T, std::enable_if_t, T>* = nullptr> inline void destroy(T* _value) { _value->free(); } template < typename T, std::enable_if_t, T>* = nullptr> inline void destroy(T* _value) { _value->dispose(); } template < typename T, std::enable_if_t, T>* = nullptr> inline void destroy(T* _value) { _value->release(); } template class autodestroy final : private non_copyable { public: autodestroy(T* value) : _value{ value } { } ~autodestroy() { if (_value) destroy(_value); } T* operator->() const { return _value; } T* operator&() const { return _value; } private: T* _value{}; }; template inline autodestroy make_autodestroy(T* value) { return autodestroy{ value }; } struct parameter { int type; int subtype; int scale; std::string str_value; std::vector octets_value; union { int8_t bool_value; int16_t int16_value; int32_t int32_value; int64_t int64_value; float float_value; double double_value; ISC_QUAD quad_value; ISC_DATE date_value; ISC_TIME time_value; ISC_TIME_TZ time_tz_value; ISC_TIME_TZ_EX time_tz_ex_value; ISC_TIMESTAMP timestamp_value; ISC_TIMESTAMP_TZ timestamp_tz_value; ISC_TIMESTAMP_TZ_EX timestamp_tz_ex_value; FB_DEC16 dec16_value; FB_DEC34 dec34_value; FB_I128 i128_value; }; }; class input_parameters final { private: static constexpr unsigned MY_SQL_OCTETS = 10000001; public: input_parameters(Firebird::ThrowStatusWrapper* status, unsigned int field_count) : m_status{ status } { m_parameters.resize(field_count); } // buffer - new empty vector // caller owns IMessageMetadata instance Firebird::IMessageMetadata* fill_buffer(std::vector& buffer) { auto md = build_metadata(); buffer.resize(md->getMessageLength(m_status)); const auto count = static_cast(m_parameters.size()); for (unsigned i = 0; i < count; ++i) { auto const& param = m_parameters[i]; if (!param.type) // null { int16_t* p_null = (int16_t*)&buffer[md->getNullOffset(m_status, i)]; *p_null = -1; continue; } uint8_t* offset = &buffer[md->getOffset(m_status, i)]; //auto len = md->getLength(m_status, i); switch (param.type) { case SQL_INT64: cast(offset) = param.int64_value; break; case SQL_LONG: cast(offset) = param.int32_value; break; case SQL_SHORT: cast(offset) = param.int16_value; break; case SQL_DOUBLE: cast(offset) = param.double_value; break; case SQL_TEXT: memcpy(((void*)offset), param.str_value.data(), param.str_value.size()); break; case SQL_VARYING: cast(offset) = static_cast(param.str_value.size()); memcpy(offset + 2, param.str_value.data(), param.str_value.size()); break; case MY_SQL_OCTETS: memcpy(((void*)offset), param.octets_value.data(), param.octets_value.size()); break; default: std::string msg = "Not implemented parameter type: "; msg += type_name(param.type); throw logic_error(msg.data()); } } print_metadata(md, m_status); return md; } void setNull(unsigned int i) { m_parameters[i] = {}; } void set(unsigned int i, int16_t value, int scale) { parameter p{}; p.type = SQL_SHORT; p.scale = -scale; p.int16_value = value; m_parameters[i] = p; } void set(unsigned int i, int32_t value, int scale) { parameter p{}; p.type = SQL_LONG; p.scale = -scale; p.int32_value = value; m_parameters[i] = p; } void set(unsigned int i, int64_t value, int scale) { parameter p{}; p.type = SQL_INT64; p.scale = -scale; p.int64_value = value; m_parameters[i] = p; } void set(unsigned int i, double value) { parameter p{}; p.type = SQL_DOUBLE; p.double_value = value; m_parameters[i] = p; } void set(unsigned int i, const char* value) { parameter p{}; p.type = SQL_TEXT; p.str_value = value; m_parameters[i] = std::move(p); } void set(unsigned int i, const std::vector& value) { parameter p{}; p.type = MY_SQL_OCTETS; p.octets_value = value; m_parameters[i] = std::move(p); } private: Firebird::IMessageMetadata* build_metadata() { const auto count = static_cast(m_parameters.size()); auto builder = make_autodestroy(master()->getMetadataBuilder(m_status, count)); for (unsigned i = 0; i < count; ++i) { auto const& param = m_parameters[i]; if (param.type) // not null { if (param.type == SQL_TEXT) { auto length = static_cast(param.str_value.size()); builder->setLength(m_status, i, length); } if (param.type == MY_SQL_OCTETS) { builder->setType(m_status, i, SQL_TEXT + 1); auto length = static_cast(param.octets_value.size()); builder->setLength(m_status, i, length); } else { builder->setType(m_status, i, param.type + 1); builder->setSubType(m_status, i, param.subtype); if (param.scale) builder->setScale(m_status, i, param.scale); } } else builder->setType(m_status, i, SQL_BOOLEAN + 1); // null, type not important? } return builder->getMetadata(m_status); } template T& cast(void* offset) const { return *((T*)offset); } private: Firebird::ThrowStatusWrapper* m_status; std::vector m_parameters; }; } // namespace fbsqlxx2::_details ///////////////////////////////////////////////////////////////////////// /// /// Database result set (cursor) RAII wrapper class /// class result_set final : private _details::non_copyable { public: result_set& operator=(result_set&&) = delete; result_set(result_set&& rhs) noexcept : m_rs{ rhs.m_rs } , m_status{ rhs.m_status } , m_meta{ rhs.m_meta } , m_buffer{ std::move(rhs.m_buffer) } , m_fields{ std::move(rhs.m_fields) } , m_count{ rhs.m_count } { rhs.m_rs = nullptr; } ~result_set() { if (m_rs) m_rs->release(); debug("result_set destroyed"); } /// /// /// void close() { try { m_rs->close(m_status); m_rs = nullptr; debug("result_set closed"); } CATCH_SQL } /// /// /// /// unsigned int field_count() const { return m_count; } /// /// /// /// bool is_bof() const { try { return m_rs->isBof(m_status); } CATCH_SQL } /// /// /// /// bool is_eof() const { try { return m_rs->isEof(m_status); } CATCH_SQL } /// /// /// /// bool next() { try { return m_rs->fetchNext(m_status, m_buffer.data()) == Firebird::IStatus::RESULT_OK; } CATCH_SQL } /// /// /// /// bool prev() { try { return m_rs->fetchPrior(m_status, m_buffer.data()) == Firebird::IStatus::RESULT_OK; } CATCH_SQL } /// /// /// /// bool first() { try { return m_rs->fetchFirst(m_status, m_buffer.data()) == Firebird::IStatus::RESULT_OK; } CATCH_SQL } /// /// /// /// bool last() { try { return m_rs->fetchLast(m_status, m_buffer.data()) == Firebird::IStatus::RESULT_OK; } CATCH_SQL } /// /// /// /// /// bool absolute(int position) { try { return m_rs->fetchAbsolute(m_status, position, m_buffer.data()) == Firebird::IStatus::RESULT_OK; } CATCH_SQL } /// /// /// /// /// bool relative(int offset) { try { return m_rs->fetchRelative(m_status, offset, m_buffer.data()) == Firebird::IStatus::RESULT_OK; } CATCH_SQL } /// /// Check column is null and has no value /// /// - Column index (1-based) /// true if column is null bool is_null(unsigned index) const { auto const& f = m_fields.at(index - 1); return 0 != cast(m_buffer.data() + f.null); } /// /// Get value of column by index /// /// Requested column type /// - Column index (1-based) /// Column value template std::enable_if_t, T> as(unsigned index) const { auto const& f = m_fields.at(index - 1); if (sizeof(T) > f.length) { // XXX: field value size override throw logic_error("!!!"); } return cast(m_buffer.data() + f.offset); } /// /// /// /// /// /// template std::enable_if_t, T> as(unsigned index) const { auto const& f = m_fields.at(index - 1); if (sizeof(T) > f.length) { // XXX: field value size override throw logic_error("!!!"); } return portable_integer(m_buffer.data() + f.offset, sizeof(T)); } Firebird::IResultSet* operator->() const { return m_rs; } private: // buffer low level accessor template T const& cast(const void* offset) const { return *((const T*)offset); } struct field { const char* name; unsigned type, subtype, length, offset, null; int scale; }; void cache_metadata(unsigned count) { m_fields.resize(count); for (unsigned i = 0; i < count; ++i) { auto& f = m_fields[i]; f.name = m_meta->getField(m_status, i); f.type = m_meta->getType(m_status, i) & ~1u; f.subtype = m_meta->getSubType(m_status, i); f.length = m_meta->getLength(m_status, i); f.offset = m_meta->getOffset(m_status, i); f.null = m_meta->getNullOffset(m_status, i); f.scale = m_meta->getScale(m_status, i); } } /// /// Normal result set constructor /// /// /// result_set(Firebird::IResultSet* rs, Firebird::ThrowStatusWrapper* status) : m_rs{ rs } , m_status{ status } { m_meta = rs->getMetadata(status); auto length = m_meta->getMessageLength(status); m_buffer.resize(length); m_count = m_meta->getCount(status); cache_metadata(m_count); debug("result_set created"); } /// /// Special constructor, for pseudo result set /// /// /// /// result_set(Firebird::IMessageMetadata* meta, Firebird::ThrowStatusWrapper* status, std::vector&& buffer) : m_rs{ nullptr } , m_status{ status } , m_meta{ meta } , m_buffer{ std::move(buffer) } { m_count = m_meta->getCount(status); cache_metadata(m_count); debug("execute result_set created"); } private: friend class statement; Firebird::IResultSet* m_rs; Firebird::ThrowStatusWrapper* m_status; Firebird::IMessageMetadata* m_meta; std::vector m_buffer; std::vector m_fields; unsigned int m_count; }; /// /// /// /// /// template <> inline std::string result_set::as(unsigned i) const { auto const& f = m_fields.at(i - 1); auto start = m_buffer.data() + f.offset; return std::string(start, start + f.length); } /// /// /// /// /// template <> inline std::vector result_set::as>(unsigned i) const { auto const& f = m_fields.at(i - 1); auto start = m_buffer.data() + f.offset; return std::vector(start, start + f.length); } /// /// Database prepared statement RAII wrapper class /// class statement final : private _details::non_copyable { public: statement& operator=(statement&&) = delete; statement(statement&& rhs) noexcept : m_stmt{ rhs.m_stmt } , m_tx{ rhs.m_tx } , m_status{ rhs.m_status } , m_input_parameters{ std::move(rhs.m_input_parameters) } , m_input_count{ rhs.m_input_count } { rhs.m_stmt = nullptr; } ~statement() { if (m_stmt) m_stmt->release(); debug("statement destroyed: ", this); } /// /// Close statement and free resources. Do not call any methods on closed statement instance. /// void close() { try { m_stmt->free(m_status); m_stmt = nullptr; debug("statement closed: ", this); } CATCH_SQL } /// /// Execute an SQL query and return pseudo single-row result set (as for "insert ... returning" statement, or stored procedure call) /// /// Pseudo result set (may be empty), having single row and positioned on it already. Data access methods allowed only result_set execute() { using namespace _details; using namespace Firebird; try { auto omd = m_stmt->getOutputMetadata(m_status); std::vector out_buffer(omd->getMessageLength(m_status)); print_metadata(m_stmt->getOutputMetadata(m_status), m_status); if (m_input_count) { std::vector buffer; auto md = make_autodestroy(get_input_parameters()->fill_buffer(buffer)); m_stmt->execute(m_status, m_tx, &md, buffer.data(), omd, out_buffer.data()); } else { m_stmt->execute(m_status, m_tx, NULL, NULL, omd, out_buffer.data()); } debug("statement executed: ", this); return result_set{ omd, m_status, std::move(out_buffer) }; } CATCH_SQL } /// /// Execute an SQL query and return result set (cursor) /// /// Result set of executed query result_set cursor() { using namespace _details; using namespace Firebird; try { IResultSet* rs{}; if (m_input_count) { std::vector buffer; auto md = make_autodestroy(get_input_parameters()->fill_buffer(buffer)); rs = m_stmt->openCursor(m_status, m_tx, &md, buffer.data(), NULL, IStatement::CURSOR_TYPE_SCROLLABLE); } else { rs = m_stmt->openCursor(m_status, m_tx, NULL, NULL, NULL, IStatement::CURSOR_TYPE_SCROLLABLE); } debug("statement cursor: ", this); auto md2 = rs->getMetadata(m_status); print_metadata(md2, m_status); return result_set{ rs, m_status }; } CATCH_SQL } /// /// /// /// field index /// field value /// self-reference statement& setNull(unsigned i) { debug("statement: ", this, ", set #", i, " -> NULL"); get_input_parameters()->setNull(i - 1); return *this; } statement& set(unsigned i, int32_t value, int scale = 0) { debug("statement: ", this, ", set #", i, " -> ", value); get_input_parameters()->set(i - 1, value, scale); return *this; } statement& set(unsigned i, int64_t value, int scale = 0) { debug("statement: ", this, ", set #", i, " -> ", value); get_input_parameters()->set(i - 1, value, scale); return *this; } statement& set(unsigned i, double value) { debug("statement: ", this, ", set #", i, " -> ", value); get_input_parameters()->set(i - 1, value); return *this; } statement& set(unsigned i, const char* value) { debug("statement: ", this, ", set #", i, " -> ", value); if (value) get_input_parameters()->set(i - 1, value); else get_input_parameters()->setNull(i - 1); return *this; } statement& set(unsigned i, const std::vector& value) { debug("statement: ", this, ", set #", i, " -> octets"); get_input_parameters()->set(i - 1, value); return *this; } Firebird::IStatement* operator->() const { return m_stmt; } private: statement(Firebird::IStatement* stmt, Firebird::ITransaction* tx, Firebird::ThrowStatusWrapper* status) : m_stmt{ stmt } , m_tx{ tx } , m_status{ status } { auto imd = m_stmt->getInputMetadata(m_status); m_input_count = imd->getCount(status); debug("statement created: ", this); print_metadata(imd, m_status); } _details::input_parameters* get_input_parameters() { if (!m_input_parameters) m_input_parameters = std::make_unique<_details::input_parameters>(m_status, m_input_count); return m_input_parameters.get(); } private: friend class transaction; Firebird::IStatement* m_stmt; Firebird::ITransaction* m_tx; Firebird::ThrowStatusWrapper* m_status; std::unique_ptr<_details::input_parameters> m_input_parameters; unsigned m_input_count{}; }; /// /// Database transaction RAII wrapper class /// class transaction final : private _details::non_copyable { public: transaction& operator=(transaction&&) = delete; transaction(transaction&& rhs) noexcept : m_tx{ rhs.m_tx } , m_att{ rhs.m_att } , m_status{ rhs.m_status } { rhs.m_tx = nullptr; } ~transaction() { if (m_tx) m_tx->release(); debug("transaction destroyed"); } /// /// Commit transaction. Makes current instance unusable /// void commit() { try { m_tx->commit(m_status); m_tx = nullptr; debug("transaction committed"); } CATCH_SQL } /// /// Rollback transaction. Makes current instance unusable /// void rollback() { try { m_tx->rollback(m_status); m_tx = nullptr; debug("transaction rolled back"); } CATCH_SQL } /// /// Commit transaction and start a new one in-place /// /// self-reference transaction& commitRetaining() { try { m_tx->commitRetaining(m_status); debug("transaction committed"); } CATCH_SQL; return *this; } /// /// Rollback transaction and start a new one in-place /// /// self-reference transaction& rollbackRetaining() { try { m_tx->rollbackRetaining(m_status); debug("transaction rolled back"); } CATCH_SQL; return *this; } /// /// Execute an SQL query as is, no inputs, no outputs /// /// - SQL query to execute /// self-reference transaction& execute(const char* sql) { try { m_att->execute(m_status, m_tx, 0, sql, SQL_DIALECT_V6, NULL, NULL, NULL, NULL); debug("transaction executed"); } CATCH_SQL; return *this; } /// /// Prepare SQL statement /// /// - SQL query to prepare /// prepared statement statement prepare(const char* sql) const { using namespace Firebird; try { IStatement* stmt = m_att->prepare(m_status, m_tx, 0, sql, SQL_DIALECT_V6, IStatement::PREPARE_PREFETCH_METADATA); return statement{ stmt, m_tx, m_status }; } CATCH_SQL } Firebird::ITransaction* operator->() const { return m_tx; } private: transaction(Firebird::ITransaction* tx, Firebird::IAttachment* att, Firebird::ThrowStatusWrapper* status) : m_tx{ tx } , m_att{ att } , m_status{ status } { //m_tx = att->startTransaction(status, 0, NULL); debug("transaction created"); } private: friend class connection; Firebird::ITransaction* m_tx; Firebird::IAttachment* m_att; Firebird::ThrowStatusWrapper* m_status; }; /// /// Transaction parameters /// struct transaction_params { // defaults uint8_t m_isolation_level{ isc_tpb_concurrency }; uint8_t m_read_committed_mode{ 0 }; uint8_t m_lock_resolution{ isc_tpb_wait }; uint8_t m_data_access{ isc_tpb_write }; int m_lock_timeout{ -1 }; bool m_auto_commit{ false }; // snapshot transaction_params& concurrency() { m_isolation_level = isc_tpb_concurrency; return *this; } // table stability transaction_params& consistency() { m_isolation_level = isc_tpb_consistency; return *this; } transaction_params& read_committed_no_record_version() { m_isolation_level = isc_tpb_read_committed; m_read_committed_mode = isc_tpb_no_rec_version; return *this; } transaction_params& read_committed_record_version() { m_isolation_level = isc_tpb_read_committed; m_read_committed_mode = isc_tpb_rec_version; return *this; } transaction_params& read_committed_consistency() { m_isolation_level = isc_tpb_read_committed; m_read_committed_mode = isc_tpb_read_consistency; return *this; } transaction_params& lock_wait(int timeout = -1) { m_lock_resolution = isc_tpb_wait; m_lock_timeout = timeout; return *this; } transaction_params& lock_nowait() { m_lock_resolution = isc_tpb_nowait; return *this; } transaction_params& read_only() { m_data_access = isc_tpb_read; return *this; } transaction_params& auto_commit(bool ac_flag = true) { m_auto_commit = ac_flag; return *this; } }; /// /// Database connection parameters /// struct connection_params { const char* role; const char* lc_messages; // path to custom firebird.msg file const char* lc_ctype; // connection charset const char* session_time_zone; const char* trusted_role; int connect_timeout; int dialect{ SQL_DIALECT_CURRENT }; bool trusted_auth; }; connection connect(const char* database, const char* user, const char* password, const connection_params& params = {}); /// /// Database connection RAII wrapper class /// class connection final : private _details::non_copyable { public: connection& operator=(connection&&) = delete; connection(connection&& rhs) noexcept : m_att{ rhs.m_att } , m_status{ master()->getStatus() } // create new status, old would be disposed { rhs.m_att = nullptr; debug("connection moved: ", this, " from: ", &rhs); } ~connection() { bool real = m_att != nullptr; if (m_att) m_att->release(); m_status.dispose(); debug("connection destroyed: ", this, " ", real ? "REAL" : "EMPTY"); } /// /// Close database connection /// void close() { try { if (m_att) m_att->detach(&m_status); m_att = nullptr; debug("connection closed: ", this); } CATCH_SQL } /// /// Ping server /// void ping() { try { m_att->ping(&m_status); } CATCH_SQL } /// /// Start a transaction with server default parameters /// /// Transaction instance transaction begin() { auto tx = m_att->startTransaction(&m_status, 0, NULL); return transaction(tx, m_att, &m_status); } /// /// Start a transaction, requesting parameters needed /// /// Transaction instance transaction begin(const transaction_params& params) { using namespace _details; using namespace Firebird; auto tpb = make_autodestroy(util()->getXpbBuilder(&m_status, IXpbBuilder::TPB, nullptr, 0)); tpb->insertTag(&m_status, params.m_isolation_level); if (params.m_isolation_level == isc_tpb_read_committed) tpb->insertTag(&m_status, params.m_read_committed_mode); tpb->insertTag(&m_status, params.m_lock_resolution); if (params.m_lock_resolution == isc_tpb_wait && params.m_lock_timeout > 0) tpb->insertInt(&m_status, isc_tpb_lock_timeout, params.m_lock_timeout); tpb->insertTag(&m_status, params.m_data_access); if (params.m_auto_commit) tpb->insertTag(&m_status, isc_tpb_autocommit); auto tx = m_att->startTransaction(&m_status, tpb->getBufferLength(&m_status), tpb->getBuffer(&m_status)); return transaction(tx, m_att, &m_status); } /// /// Execute an SQL statement immediately /// /// - SQL statement to execute /// self-reference connection& execute(const char* sql) { begin().execute(sql).commit(); return *this; } Firebird::IAttachment* operator->() const { return m_att; } private: friend connection connect(const char* database, const char* user, const char* password, const connection_params& params); connection(const char* database, const char* user, const char* password, const connection_params& params) : m_att{} , m_status{ master()->getStatus() } { using namespace Firebird; using namespace _details; auto dpb = make_autodestroy(util()->getXpbBuilder(&m_status, IXpbBuilder::DPB, nullptr, 0)); if (user) dpb->insertString(&m_status, isc_dpb_user_name, user); if (password) dpb->insertString(&m_status, isc_dpb_password, password); if (params.role) dpb->insertString(&m_status, isc_dpb_sql_role_name, params.role); if (params.lc_ctype) dpb->insertString(&m_status, isc_dpb_lc_ctype, params.lc_ctype); if (params.lc_messages) dpb->insertString(&m_status, isc_dpb_lc_messages, params.lc_messages); if (params.session_time_zone) dpb->insertString(&m_status, isc_dpb_session_time_zone, params.session_time_zone); if (params.trusted_auth) dpb->insertTag(&m_status, isc_dpb_trusted_auth); if (params.trusted_role) dpb->insertString(&m_status, isc_dpb_trusted_role, params.trusted_role); if (params.connect_timeout > 0) dpb->insertInt(&m_status, isc_dpb_connect_timeout, params.connect_timeout); dpb->insertInt(&m_status, isc_dpb_sql_dialect, params.dialect); try { auto provider = make_autodestroy(master()->getDispatcher()); m_att = provider->attachDatabase(&m_status, database, dpb->getBufferLength(&m_status), dpb->getBuffer(&m_status)); debug("connection: ", database, " ", this); } CATCH_SQL } private: Firebird::IAttachment* m_att; Firebird::ThrowStatusWrapper m_status; }; /// /// Create database conection /// /// - URN, inet://host:port/path/to/db.fdb /// - login /// - password /// - optional connection parameters /// Connection instance inline connection connect(const char* database, const char* user, const char* password, const connection_params& params) { return connection(database, user, password, params); } } // namespace fbsqlxx2 #undef debug #undef CATCH_SQL