diff --git a/include/cif++/cql.hpp b/include/cif++/cql.hpp index 596a2bc..f786666 100644 --- a/include/cif++/cql.hpp +++ b/include/cif++/cql.hpp @@ -393,7 +393,8 @@ class transaction final transaction(const transaction &) = delete; transaction &operator=(const transaction &) = delete; - result exec(const std::string &query); + result exec(std::string query); + result exec(std::string query, std::string &tail); template cql_iterator_proxy stream(const std::string &sql) @@ -419,6 +420,9 @@ class connection final friend class transaction; + /// \brief Return true if the string @a sql contains a complete statement. + bool statementIsComplete(const std::string &sql) const; + private: struct connection_impl *m_impl; }; diff --git a/src/cql.cpp b/src/cql.cpp index 0b77a95..d29d8da 100644 --- a/src/cql.cpp +++ b/src/cql.cpp @@ -862,6 +862,11 @@ connection::~connection() delete m_impl; } +bool connection::statementIsComplete(const std::string &sql) const +{ + return sqlite3_complete(sql.c_str()) == 1; +} + // -------------------------------------------------------------------- transaction::transaction(connection &conn) @@ -927,7 +932,19 @@ void transaction::rollback() } } -result transaction::exec(const std::string &query) +result transaction::exec(std::string query) +{ + trim(query); + + for (;;) + { + auto r = exec(query, query); + if (query.empty()) + return r; + } +} + +result transaction::exec(std::string query, std::string &tail) { category cat; @@ -935,72 +952,69 @@ result transaction::exec(const std::string &query) try { - std::string sql = query; - while (not sql.empty()) + const char *tail_ptr = nullptr; + int rc = sqlite3_prepare_v2(m_conn.m_impl->m_sqlite_db, query.data(), query.length(), + &stmt, &tail_ptr); + + auto used = tail_ptr - query.data(); + auto sql = query.substr(0, used); + tail = trim_copy(query.substr(used)); + + if (rc != SQLITE_OK) + throw std::runtime_error(std::format("Error preparing statement: {}", sqlite3_errmsg(m_conn.m_impl->m_sqlite_db))); + + category ncat((std::format("result{}", m_conn.m_impl->m_next_result_nr++))); + std::swap(cat, ncat); + + // for (int i = 0; i < sqlite3_column_count(stmt); ++i) + // cat.add_item(sqlite3_column_name(stmt, i)); + + for (;;) { - const char *tail = nullptr; - int rc = sqlite3_prepare_v2(m_conn.m_impl->m_sqlite_db, sql.data(), sql.length(), - &stmt, &tail); - auto used = tail - sql.data(); - sql = sql.substr(used, sql.length() - used); - trim(sql); - - if (rc != SQLITE_OK) - throw std::runtime_error(std::format("Error preparing statement: {}", sqlite3_errmsg(m_conn.m_impl->m_sqlite_db))); - - category ncat((std::format("result{}", m_conn.m_impl->m_next_result_nr++))); - std::swap(cat, ncat); - - // for (int i = 0; i < sqlite3_column_count(stmt); ++i) - // cat.add_item(sqlite3_column_name(stmt, i)); - - for (;;) + rc = sqlite3_step(stmt); + if (rc == SQLITE_ROW) { - rc = sqlite3_step(stmt); - if (rc == SQLITE_ROW) + row_initializer data; + + for (int i = 0; i < sqlite3_column_count(stmt); ++i) { - row_initializer data; - - for (int i = 0; i < sqlite3_column_count(stmt); ++i) + switch (sqlite3_column_type(stmt, i)) { - switch (sqlite3_column_type(stmt, i)) - { - case SQLITE_INTEGER: - data.emplace_back(sqlite3_column_name(stmt, i), sqlite3_column_int64(stmt, i)); - break; - case SQLITE_FLOAT: - data.emplace_back(sqlite3_column_name(stmt, i), sqlite3_column_double(stmt, i)); - break; - case SQLITE_TEXT: - data.emplace_back(sqlite3_column_name(stmt, i), (const char *)sqlite3_column_text(stmt, i)); - break; - case SQLITE_BLOB: - throw std::runtime_error("Unexpected: blob in result"); - break; - case SQLITE_NULL: - // data.emplace_back(sqlite3_column_name(stmt, i), "."); - break; - } + case SQLITE_INTEGER: + data.emplace_back(sqlite3_column_name(stmt, i), sqlite3_column_int64(stmt, i)); + break; + case SQLITE_FLOAT: + data.emplace_back(sqlite3_column_name(stmt, i), sqlite3_column_double(stmt, i)); + break; + case SQLITE_TEXT: + data.emplace_back(sqlite3_column_name(stmt, i), (const char *)sqlite3_column_text(stmt, i)); + break; + case SQLITE_BLOB: + throw std::runtime_error("Unexpected: blob in result"); + break; + case SQLITE_NULL: + // data.emplace_back(sqlite3_column_name(stmt, i), "."); + break; } - - cat.emplace(std::move(data)); - continue; } - if (rc == SQLITE_BUSY) - throw std::runtime_error("Oops, busy?"); - if (rc == SQLITE_DONE) - break; - if (rc == SQLITE_ERROR) - throw std::runtime_error(std::format("Error in sqlite: {}", sqlite3_errmsg(m_conn.m_impl->m_sqlite_db))); - - throw std::runtime_error("Unknown result from step"); + cat.emplace(std::move(data)); + continue; } - sqlite3_finalize(stmt); + if (rc == SQLITE_BUSY) + throw std::runtime_error("Oops, busy?"); + if (rc == SQLITE_DONE) + break; + if (rc == SQLITE_ERROR) + throw std::runtime_error(std::format("Error in sqlite: {}", sqlite3_errmsg(m_conn.m_impl->m_sqlite_db))); + + throw std::runtime_error("Unknown result from step"); } - return result(std::move(cat), query); + sqlite3_finalize(stmt); + + return result(std::move(cat), sql); } catch (const std::exception &ex) {