1/*
2 * The contents of this file are subject to the Initial
3 * Developer's Public License Version 1.0 (the "License");
4 * you may not use this file except in compliance with the
5 * License. You may obtain a copy of the License at
6 * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
7 *
8 * Software distributed under the License is distributed AS IS,
9 * WITHOUT WARRANTY OF ANY KIND, either express or implied.
10 * See the License for the specific language governing rights
11 * and limitations under the License.
12 *
13 * The Original Code was created by Adriano dos Santos Fernandes
14 * for the Firebird Open Source RDBMS project.
15 *
16 * Copyright (c) 2008 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
17 * and all contributors signed below.
18 *
19 * All Rights Reserved.
20 * Contributor(s): ______________________________________.
21 */
22
23#ifndef JRD_PREPARED_STATEMENT_H
24#define JRD_PREPARED_STATEMENT_H
25
26#include "firebird.h"
27#include "../common/dsc.h"
28#include "../common/MsgMetadata.h"
29#include "../jrd/intl.h"
30#include "../common/classes/alloc.h"
31#include "../common/classes/array.h"
32#include "../common/classes/auto.h"
33#include "../common/classes/fb_string.h"
34#include "../common/classes/MetaName.h"
35#include "../common/classes/Nullable.h"
36
37namespace Jrd {
38
39class thread_db;
40class jrd_tra;
41class Attachment;
42class dsql_req;
43class dsql_msg;
44class ResultSet;
45
46
47class PreparedStatement : public Firebird::PermanentStorage
48{
49friend class ResultSet;
50
51public:
52 class Builder
53 {
54 private:
55 enum Type
56 {
57 TYPE_SSHORT,
58 TYPE_SLONG,
59 TYPE_SINT64,
60 TYPE_DOUBLE,
61 TYPE_METANAME,
62 TYPE_STRING,
63 };
64
65 // This struct and the member outputParams are used to make the C++ undefined parameter
66 // evaluation order do not interfere on the question marks / slots correspondence.
67 struct OutputParam
68 {
69 OutputParam(const char* aChunk, size_t aNumber)
70 : chunk(aChunk),
71 number(aNumber)
72 {
73 }
74
75 const char* chunk;
76 size_t number;
77 };
78
79 struct InputSlot
80 {
81 Type type;
82 unsigned number;
83 const void* address;
84 const bool* specifiedAddress;
85 };
86
87 struct OutputSlot
88 {
89 Type type;
90 unsigned number;
91 void* address;
92 bool* specifiedAddress;
93 };
94
95 public:
96 Builder()
97 : outputParams(0)
98 {
99 }
100
101 public:
102 // Output variables.
103
104 template <typename T> OutputParam operator ()(const char* chunk, Nullable<T>& param)
105 {
106 OutputParam ret = (*this)(chunk, param.value);
107 outputSlots[outputSlots.getCount() - 1].specifiedAddress = &param.specified;
108 return ret;
109 }
110
111 template <typename T> OutputParam operator ()(const char* chunk, T& param)
112 {
113 addOutput(getType(param), &param, outputSlots);
114 return OutputParam(chunk, outputSlots.getCount() - 1);
115 }
116
117 // SQL text concatenation.
118 Builder& operator <<(OutputParam outputParam)
119 {
120 text += " ";
121 text += outputParam.chunk;
122 outputSlots[outputParam.number].number = ++outputParams;
123 return *this;
124 }
125
126 // Input variables.
127
128 Builder& operator <<(const char* chunk)
129 {
130 text += " ";
131 text += chunk;
132 return *this;
133 }
134
135 template <typename T> Builder& operator <<(const Nullable<T>& param)
136 {
137 *this << param.value;
138 inputSlots[inputSlots.getCount() - 1].specifiedAddress = &param.specified;
139 return *this;
140 }
141
142 template <typename T> Builder& operator <<(const T& param)
143 {
144 addInput(getType(param), &param, inputSlots);
145 text += "?";
146 return *this;
147 }
148
149 public:
150 const Firebird::string& getText() const
151 {
152 return text;
153 }
154
155 void moveFromResultSet(thread_db* tdbb, ResultSet* rs) const;
156 void moveToStatement(thread_db* tdbb, PreparedStatement* stmt) const;
157
158 private:
159 // Make the C++ template engine return the constant for each type.
160 static Type getType(SSHORT) { return TYPE_SSHORT; }
161 static Type getType(SLONG) { return TYPE_SLONG; }
162 static Type getType(SINT64) { return TYPE_SINT64; }
163 static Type getType(double) { return TYPE_DOUBLE; }
164 static Type getType(const Firebird::AbstractString&) { return TYPE_STRING; }
165 static Type getType(const Firebird::MetaName&) { return TYPE_METANAME; }
166
167 void addInput(Type type, const void* address, Firebird::Array<InputSlot>& slots)
168 {
169 InputSlot slot;
170 slot.type = type;
171 slot.number = (unsigned) slots.getCount() + 1;
172 slot.address = address;
173 slot.specifiedAddress = NULL;
174 slots.add(slot);
175 }
176
177 void addOutput(Type type, void* address, Firebird::Array<OutputSlot>& slots)
178 {
179 OutputSlot slot;
180 slot.type = type;
181 slot.number = (unsigned) slots.getCount() + 1;
182 slot.address = address;
183 slot.specifiedAddress = NULL;
184 slots.add(slot);
185 }
186
187 private:
188 Firebird::string text;
189 Firebird::Array<InputSlot> inputSlots;
190 Firebird::Array<OutputSlot> outputSlots;
191 unsigned outputParams;
192 };
193
194private:
195 // Auxiliary class to use positional parameters with C++ variables.
196 class PosBuilder
197 {
198 public:
199 explicit PosBuilder(const Firebird::string& aText)
200 : text(aText),
201 params(0)
202 {
203 }
204
205 PosBuilder& operator <<(const char* chunk)
206 {
207 text += chunk;
208 return *this;
209 }
210
211 PosBuilder& operator <<(unsigned& param)
212 {
213 text += "?";
214 param = ++params;
215 return *this;
216 }
217
218 operator const Firebird::string& ()
219 {
220 return text;
221 }
222
223 private:
224 Firebird::string text;
225 unsigned params;
226 };
227
228public:
229 // Create a PreparedStatement builder to use positional parameters with C++ variables.
230 static PosBuilder build(const Firebird::string& text)
231 {
232 return PosBuilder(text);
233 }
234
235 // Escape a metadata name accordingly to SQL rules.
236 static Firebird::string escapeName(const Firebird::MetaName& s)
237 {
238 Firebird::string ret;
239
240 for (const char* p = s.begin(); p != s.end(); ++p)
241 {
242 ret += *p;
243 if (*p == '\"')
244 ret += '\"';
245 }
246
247 return ret;
248 }
249
250 // Escape a string accordingly to SQL rules.
251 template <typename T> static Firebird::string escapeString(const T& s)
252 {
253 Firebird::string ret;
254
255 for (const char* p = s.begin(); p != s.end(); ++p)
256 {
257 ret += *p;
258 if (*p == '\'')
259 ret += '\'';
260 }
261
262 return ret;
263 }
264
265public:
266 PreparedStatement(thread_db* tdbb, Firebird::MemoryPool& aPool, Attachment* attachment,
267 jrd_tra* transaction, const Firebird::string& text, bool isInternalRequest);
268 PreparedStatement(thread_db* tdbb, Firebird::MemoryPool& aPool, Attachment* attachment,
269 jrd_tra* transaction, const Builder& aBuilder, bool isInternalRequest);
270 ~PreparedStatement();
271
272private:
273 void init(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction,
274 const Firebird::string& text, bool isInternalRequest);
275
276public:
277 void setDesc(thread_db* tdbb, unsigned param, const dsc& value);
278
279 void setNull(unsigned param)
280 {
281 fb_assert(param > 0);
282
283 dsc* desc = &inValues[(param - 1) * 2 + 1];
284 fb_assert(desc->dsc_dtype == dtype_short);
285 *reinterpret_cast<SSHORT*>(desc->dsc_address) = -1;
286 }
287
288 void setSmallInt(thread_db* tdbb, unsigned param, SSHORT value, SCHAR scale = 0)
289 {
290 fb_assert(param > 0);
291
292 dsc desc;
293 desc.makeShort(scale, &value);
294 setDesc(tdbb, param, desc);
295 }
296
297 void setInt(thread_db* tdbb, unsigned param, SLONG value, SCHAR scale = 0)
298 {
299 fb_assert(param > 0);
300
301 dsc desc;
302 desc.makeLong(scale, &value);
303 setDesc(tdbb, param, desc);
304 }
305
306 void setBigInt(thread_db* tdbb, unsigned param, SINT64 value, SCHAR scale = 0)
307 {
308 fb_assert(param > 0);
309
310 dsc desc;
311 desc.makeInt64(scale, &value);
312 setDesc(tdbb, param, desc);
313 }
314
315 void setDouble(thread_db* tdbb, unsigned param, double value)
316 {
317 fb_assert(param > 0);
318
319 dsc desc;
320 desc.makeDouble(&value);
321 setDesc(tdbb, param, desc);
322 }
323
324 void setString(thread_db* tdbb, unsigned param, const Firebird::AbstractString& value)
325 {
326 fb_assert(param > 0);
327
328 dsc desc;
329 desc.makeText((USHORT) value.length(), inValues[(param - 1) * 2].getTextType(),
330 (UCHAR*) value.c_str());
331 setDesc(tdbb, param, desc);
332 }
333
334 void setMetaName(thread_db* tdbb, unsigned param, const Firebird::MetaName& value)
335 {
336 fb_assert(param > 0);
337
338 dsc desc;
339 desc.makeText((USHORT) value.length(), CS_METADATA, (UCHAR*) value.c_str());
340 setDesc(tdbb, param, desc);
341 }
342
343 void execute(thread_db* tdbb, jrd_tra* transaction);
344 void open(thread_db* tdbb, jrd_tra* transaction);
345 ResultSet* executeQuery(thread_db* tdbb, jrd_tra* transaction);
346 unsigned executeUpdate(thread_db* tdbb, jrd_tra* transaction);
347
348 int getResultCount() const;
349
350 dsql_req* getRequest()
351 {
352 return request;
353 }
354
355 static void parseDsqlMessage(const dsql_msg* dsqlMsg, Firebird::Array<dsc>& values,
356 Firebird::MsgMetadata* msgMetadata, Firebird::UCharBuffer& msg);
357
358private:
359 const Builder* builder;
360 dsql_req* request;
361 Firebird::Array<dsc> inValues, outValues;
362 Firebird::RefPtr<Firebird::MsgMetadata> inMetadata, outMetadata;
363 Firebird::UCharBuffer inMessage, outMessage;
364 ResultSet* resultSet;
365};
366
367typedef Firebird::AutoPtr<PreparedStatement> AutoPreparedStatement;
368
369
370} // namespace
371
372#endif // JRD_PREPARED_STATEMENT_H
373