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 | |
37 | namespace Jrd { |
38 | |
39 | class thread_db; |
40 | class jrd_tra; |
41 | class Attachment; |
42 | class dsql_req; |
43 | class dsql_msg; |
44 | class ResultSet; |
45 | |
46 | |
47 | class PreparedStatement : public Firebird::PermanentStorage |
48 | { |
49 | friend class ResultSet; |
50 | |
51 | public: |
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 = ¶m.specified; |
108 | return ret; |
109 | } |
110 | |
111 | template <typename T> OutputParam operator ()(const char* chunk, T& param) |
112 | { |
113 | addOutput(getType(param), ¶m, 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 = ¶m.specified; |
139 | return *this; |
140 | } |
141 | |
142 | template <typename T> Builder& operator <<(const T& param) |
143 | { |
144 | addInput(getType(param), ¶m, 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 | |
194 | private: |
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 | |
228 | public: |
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 | |
265 | public: |
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 | |
272 | private: |
273 | void init(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction, |
274 | const Firebird::string& text, bool isInternalRequest); |
275 | |
276 | public: |
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 | |
358 | private: |
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 | |
367 | typedef Firebird::AutoPtr<PreparedStatement> AutoPreparedStatement; |
368 | |
369 | |
370 | } // namespace |
371 | |
372 | #endif // JRD_PREPARED_STATEMENT_H |
373 | |