summaryrefslogtreecommitdiff
path: root/subprojects/boost-sqlite/include/boost/sqlite/error.hpp
blob: 9fe62116a930b4566b68225951ac2752540931e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//
// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_SQLITE_ERROR_HPP
#define BOOST_SQLITE_ERROR_HPP

#include <boost/sqlite/detail/config.hpp>
#include <boost/sqlite/cstring_ref.hpp>
#include <boost/sqlite/memory.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/error_category.hpp>
#include <boost/system/result.hpp>


BOOST_SQLITE_BEGIN_NAMESPACE

BOOST_SQLITE_DECL
system::error_category & sqlite_category();


/**
 * \brief Additional information about error conditions stored in an sqlite-allocate string
 * \ingroup reference
 * \details Contains an error message describing what happened. Not all error
 * conditions are able to generate this extended information - those that
 * can't have an empty error message.
 */
struct error_info
{
    /// Default constructor.
    error_info() = default;

    /// Initialization constructor.
    error_info(core::string_view msg) noexcept : msg_(new (memory_tag{}) char[msg.size() + 1u])
    {
      std::memcpy(msg_.get(), msg.data(), (std::min)(msg.size() + 1, capacity()));
    }

    /// set the message by copy
    void set_message(core::string_view msg)
    {
      reserve(msg.size() + 1u);
      std::memcpy(msg_.get(), msg.data(), (std::min)(msg.size() + 1, capacity()));
    }
    /// transfer ownership into the message
    void reset(char * c = nullptr)
    {
      msg_.reset(c);
    }

    /// use sqlite_mprintf to generate a message
#if defined(__GNUC__)
    cstring_ref format(const char * fmt, ...) __attribute__((format (printf, 2, 3)))
    {
      va_list args;
      va_start(args, fmt);
      msg_.reset(sqlite3_vmprintf(fmt, args));
      va_end(args);
      return msg_.get();
    }

#endif
    cstring_ref format(cstring_ref fmt, ...)
    {
      va_list args;
      va_start(args, fmt);
      msg_.reset(sqlite3_vmprintf(fmt.c_str(), args));
      va_end(args);
      return msg_.get();
    }

    /// use sqlite_snprintf to generate a message
#if defined(__GNUC__)
    cstring_ref snformat(const char * fmt, ...)   __attribute__((format (printf, 2, 3)))
    {
      if (capacity() == 0)
        return "";
      va_list args;
      va_start(args, fmt);
      sqlite3_vsnprintf(static_cast<int>(capacity()), msg_.get(), fmt, args);
      va_end(args);
      return msg_.get();
    }

#endif
    cstring_ref snformat(cstring_ref fmt, ...)
    {
      if (capacity() == 0)
        return "";
      va_list args;
      va_start(args, fmt);
      sqlite3_vsnprintf(static_cast<int>(capacity()), msg_.get(), fmt.c_str(), args);
      va_end(args);
      return msg_.get();
    }
    /// reserve data in the buffer i.e. allocate
    void reserve(std::size_t sz)
    {
      if (msg_)
      {
        if (sqlite3_msize(msg_.get()) < sz)
          msg_.reset(static_cast<char*>(sqlite3_realloc64(msg_.release(), sz)));
      }
      else
        msg_.reset(static_cast<char*>(sqlite3_malloc64(sz)));
    }

    /// Get the allocated memory
    std::size_t capacity() const {return msg_ ? sqlite3_msize(msg_.get()) : 0u;}

    /// Gets the error message.
    cstring_ref message() const noexcept { return msg_ ? msg_.get() : ""; }

    char * release()
    {
      return msg_.release();
    }
    /// Restores the object to its initial state.
    void clear() noexcept { if (msg_) *msg_ = '\0'; }


    operator bool() const {return msg_.operator bool();}
  private:
    unique_ptr<char> msg_;
};


/**
 * \brief An error containing both a code & optional message.
 * \ingroup reference
 * \details Contains an error .
 */
struct error
{
  /// The code of the error.
  int code;
  /// The additional information of the error
  error_info info;

  error(int code, error_info info) : code(code), info(std::move(info)) {}
  explicit error(int code)                  : code(code)                        {}
  error(int code, core::string_view info)
          : code(code),
            info(info) {}

  error(system::error_code code, error_info info)
          : code(code.category() == sqlite_category() ? code.value() : SQLITE_FAIL),
            info(std::move(info)) {}

  error(system::error_code code) : code(code.category() == sqlite_category() ? code.value() : SQLITE_FAIL)
  {
    if (code.category() == sqlite_category())
      info = error_info{code.what()};
  }
  error() = default;
  error(error && ) noexcept = default;
};

BOOST_NORETURN BOOST_SQLITE_DECL void throw_exception_from_error( error const & e, boost::source_location const & loc );

template<typename T = void>
using result = system::result<T, error>;

template<typename T>
struct is_result_type : std::false_type {};

template<typename T>
struct is_result_type<result<T>> : std::true_type {};


BOOST_SQLITE_END_NAMESPACE


#endif // BOOST_SQLITE_ERROR_HPP