aboutsummaryrefslogtreecommitdiff
path: root/odb/prepared-query.hxx
blob: 37d2b76681511fd9081917b3befb2b662e7b6ef0 (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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// file      : odb/prepared-query.hxx
// license   : GNU GPL v2; see accompanying LICENSE file

#ifndef ODB_PREPARED_QUERY_HXX
#define ODB_PREPARED_QUERY_HXX

#include <odb/pre.hxx>

#include <odb/forward.hxx> // odb::core
#include <odb/traits.hxx>
#include <odb/result.hxx>
#include <odb/statement.hxx>

#include <odb/details/export.hxx>
#include <odb/details/shared-ptr.hxx>

namespace odb
{
  class LIBODB_EXPORT prepared_query_impl: public details::shared_base
  {
  public:
    virtual
    ~prepared_query_impl ();

    prepared_query_impl (connection&);

    bool cached;
    connection& conn;
    const char* name;
    details::shared_ptr<statement> stmt;
    details::shared_ptr<result_impl> (*execute) (prepared_query_impl&);

  private:
    prepared_query_impl (const prepared_query_impl&);
    prepared_query_impl& operator= (const prepared_query_impl&);

    // Doubly-linked list of results.
    //
    // prev_ ==    0 means we are the first element.
    // next_ ==    0 means we are the last element.
    // next_ == this means we are not on the list.
    //
  protected:
    friend class connection;

    void
    list_remove ();

    prepared_query_impl* prev_;
    prepared_query_impl* next_;
  };

  template <typename T>
  struct prepared_query
  {
    // Cached version.
    //
    explicit
    prepared_query (prepared_query_impl* impl = 0): impl_ (impl) {}

    // Uncached version.
    //
    explicit
    prepared_query (const details::shared_ptr<prepared_query_impl>& impl)
        : impl_ (impl.get ())
    {
      impl_->_inc_ref ();
    }

    result<T>
    execute (bool cache = true)
    {
      typedef
      typename result_base<T, class_traits<T>::kind>::result_impl_type
      derived_type;

      details::shared_ptr<result_impl> ri (impl_->execute (*impl_));
      result<T> r (
        details::shared_ptr<derived_type> (
          static_cast<derived_type*> (ri.release ())));

      if (cache)
        r.cache ();

      return r;
    }

    typename object_traits<T>::pointer_type
    execute_one ()
    {
      return execute (false).one ();
    }

    bool
    execute_one (T& object)
    {
      return execute (false).one (object);
    }

    T
    execute_value ()
    {
      // Compiler error pointing here? The object must be default-
      // constructible in order to use the return-by-value API.
      //
      T o;
      execute (false).value (o);
      return o;
    }

    const char*
    name () const
    {
      return impl_->name;
    }

    typedef odb::statement statement_type;

    statement_type&
    statement () const
    {
      return *impl_->stmt;
    }

    typedef prepared_query_impl* prepared_query::*unspecified_bool_type;
    operator unspecified_bool_type () const
    {
      return impl_ ? &prepared_query::impl_ : 0;
    }

  public:
    ~prepared_query ()
    {
      if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ())
        delete impl_;
    }

    prepared_query (const prepared_query& x)
        : impl_ (x.impl_)
    {
      if (!impl_->cached)
        impl_->_inc_ref ();
    }

    prepared_query&
    operator= (const prepared_query& x)
    {
      if (impl_ != x.impl_)
      {
        if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ())
          delete impl_;

        impl_ = x.impl_;

        if (!impl_->cached)
          impl_->_inc_ref ();
      }

      return *this;
    }

  private:
    // Ideally, we would just use shared_ptr to manage the impl object.
    // However, there is a problem if the prepared query is cached on
    // the connection and the connection is released early when the
    // transaction is committed or rolled back. In this case, the
    // prepared_query object might still be around pointing to impl. If
    // this connection and the prepared query are then used by another
    // thread while we release the impl object, then we have a race
    // condition.
    //
    // To work around this problem we will simply "reference" the impl
    // object without counting if the prepared query is cached. For
    // transition from pointer to reference, see cache_query_() in
    // connection.cxx.
    //
    // You may also observe that in order to know whether this is a
    // cached prepared query or not, we have to read the cached data
    // member in the impl object. This does not cause a race because,
    // unlike the reference count, this member is immutable once set
    // to true.
    //
    friend class connection;
    prepared_query_impl* impl_;
  };

  namespace common
  {
    using odb::prepared_query;
  }
}

#include <odb/post.hxx>

#endif // ODB_PREPARED_QUERY_HXX