DCL 3.7.4
Loading...
Searching...
No Matches
PqQuery.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#ifdef __APPLE__
4#include <libkern/OSByteOrder.h>
5#endif
6#include <stdlib.h> // malloc
7#include <string.h> // memset
8
9#include <libpq-fe.h>
10
11#include <dcl/Object.h>
12#if __DCL_HAVE_ALLOC_DEBUG
13#undef __DCL_ALLOC_LEVEL
14#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
15#endif
16
17#include <dcl/SQLCore.h>
18
19#include "PqConnection.h"
20#include "PqQuery.h"
21#include "PqField.h"
22#include "PqParam.h"
23#include "PqTypes.h"
24
25#define __TRACE_THIS 0
26#if __TRACE_THIS
27#define __DCL_TRACE0_N __DCL_TRACE0
28#define __DCL_TRACE1_N __DCL_TRACE1
29#define __DCL_TRACE2_N __DCL_TRACE2
30#define __DCL_TRACE3_N __DCL_TRACE3
31#define __DCL_TRACE4_N __DCL_TRACE4
32#define __DCL_TRACE5_N __DCL_TRACE5
33#define __DCL_TRACE6_N __DCL_TRACE6
34#else
35#define __DCL_TRACE0_N(fmt)
36#define __DCL_TRACE1_N(fmt, arg)
37#define __DCL_TRACE2_N(fmt, arg1, arg2)
38#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
39#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
40#define __DCL_TRACE5_N(fmt, arg1, arg2, arg3, arg4, arg5)
41#define __DCL_TRACE6_N(fmt, arg1, arg2, arg3, arg4, arg5, arg6)
42#endif
43
44#undef __THIS_FILE__
45static const char_t __THIS_FILE__[] = __T("dcl/sql/PqQuery.cpp");
46
47__DCL_BEGIN_NAMESPACE
48
49#define __SET_ERROR(_error) \
50 connection()->setErrorStatus(_error, __THIS_FILE__, __LINE__)
51#define __SET_ERROR_MSG(_message) \
52 conn()->setErrorMessage(_message, __THIS_FILE__, __LINE__)
53
55
57 : Query(pConnection)
58{
59 Query::__placeholder = L'$';
60 __stmtNo = 0;
61 __nfields = 0;
62
63 __inBINDs = NULL;
64 __fields = NULL;
65 __params = NULL;
66
67 __res = NULL;
68 __row = -1;
69}
70
72{
73#ifdef __DCL_DEBUG
74 if (!reset()) {
75 char buf[256];
76 size_t buflen = sizeof(buf) - 1;
77 bool b = conn()->__getErrorMessage(buf, &buflen);
78 buf[b ? buflen : 0] = '\0';
79 __DCL_TRACE1(L"Warning! Query reset error! %hs\n", buf);
80 }
81#else
82 (void)reset();
83#endif
84}
85
87{
88 delete this;
89}
90
92{
93 Query::__eof = true;
94 Query::__affectedRows = -1;
95
96 // clear fields
97 if (__fields) {
98 __DCL_ASSERT(Query::__fieldCount > 0);
99 delete[] __fields;
100 __fields = NULL;
101 Query::__fieldCount = 0;
102 __nfields = 0;
103 }
104
105 // clear binds
106 if (__params) {
107 __DCL_ASSERT(Query::__paramCount > 0);
108 delete[] __params;
109 __params = NULL;
110 Query::__paramCount = 0;
111 }
112
113 if (__inBINDs) {
114 free(__inBINDs);
115 __inBINDs = NULL;
116 }
117
118 if (__res) {
119 PQclear(__res);
120 __res = NULL;
121 __row = -1;
122 }
123
124 bool r = true;
125 if (!__cursorID.isEmpty()) {
126 __DCL_ASSERT(!__query.isEmpty());
127 __query.clear();
128
129 ByteString cmd = "CLOSE " + __cursorID;
130 PGresult* res = PQexec(conn()->connHandle(), cmd);
131 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
132 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
133 r = false;
134 }
135 PQclear(res);
136#ifdef LIBPQ_HAS_CLOSE_PREPARED
137 res = PQclosePortal(conn()->connHandle(), __cursorID);
138 if (r && PQresultStatus(res) != PGRES_COMMAND_OK) {
139 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
140 r = false;
141 }
142 PQclear(res);
143#endif
144 __cursorID.clear();
145 }
146
147 if (!__stmtID.isEmpty()) {
148#ifdef LIBPQ_HAS_CLOSE_PREPARED
149 PGresult* res = PQclosePrepared(conn()->connHandle(), __stmtID);
150 if (r && PQresultStatus(res) != PGRES_COMMAND_OK) {
151 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
152 r = false;
153 }
154 PQclear(res);
155#endif
156 __stmtID.clear();
157 }
158
159 return r;
160}
161
163 const char* _sql, size_t _sqllen, size_t _paramCount
164)
165{
166 if(!reset())
167 return false;
168
169 // PostgreSQL 17 이전의 libpq 에는 PQclosePrepared, PQclosePortal 이 없다.
170 // stmtID가 동일하면 prepared statement "stmt_0" already exists
171 // 연결이 지속되는 동안 이 값을 계속 증가시킨다.
172 __stmtNo = conn()->stmtNo();
173 ByteString stmtID = ByteString::format("stmt_%d", __stmtNo);
174 PGresult* res = PQprepare(conn()->connHandle(), stmtID, _sql, 0, NULL);
175 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
176 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
177 PQclear(res);
178 return false;
179 }
180 PQclear(res);
181
182 res = PQdescribePrepared(conn()->connHandle(), stmtID);
183 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
184 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
185 PQclear(res);
186 return false;
187 }
188
190 && (Query::__paramCount == 0)
191 && (__inBINDs == NULL)
192 );
193
194 int nparams = PQnparams(res);
195 if (nparams) {
196 __DCL_ASSERT((size_t)nparams == _paramCount);;
197
198#if defined(__DCL_DEBUG) && __TRACE_THIS && 0
199 for (int i = 0; i < nparams; i++) {
200 Oid oid = PQparamtype(res, i);
201 __DCL_TRACE3_N(L"[%2d] [%4d %-10ls]\n", i,
202 oid, __dataTypeName(oid)
203 );
204 }
205#endif
206 size_t alloc = nparams * (sizeof(char*) + sizeof(int) * 3);
207 __DCL_TRACE2_N(L"__inBINDs nparams[%2d] * (8+4+4+4=20) alloc[%zd]\n",
208 nparams, alloc);
209 __inBINDs = malloc(alloc);
210 if (__inBINDs == NULL) {
212 return false;
213 }
214 memset(__inBINDs, 0, alloc);
215
216#define __BIND_VALUES ((const char**)((char*)__inBINDs))
217#define __BIND_TYPES ((Oid*)((char*)__inBINDs \
218 + (Query::__paramCount * (sizeof(char*) + sizeof(int) * 0))))
219#define __BIND_LENGTHS ((int*)((char*)__inBINDs \
220 + (Query::__paramCount * (sizeof(char*) + sizeof(int) * 1))))
221#define __BIND_FORMATS ((int*)((char*)__inBINDs \
222 + (Query::__paramCount * (sizeof(char*) + sizeof(int) * 2))))
223
224#define __SET_BIND_TYPE(_index, _type) __BIND_TYPES[_index] = _type
225#define __SET_BIND_VALUE(_index, _value) __BIND_VALUES[_index] = _value
226#define __SET_BIND_LENGTH(_index, _length) __BIND_LENGTHS[_index] = _length
227#define __SET_BIND_FORMAT(_index, _format) __BIND_FORMATS[_index] = _format
228
229 Query::__paramCount = nparams;
230 __params = new PqParam[Query::__paramCount];
231 if (__params == NULL) {
233 return false;
234 }
235
236 for (size_t i = 0; i < Query::__paramCount; i++) {
237 if (!__params[i].init(this, (int)i, res))
238 return false;
239 }
240 }
241
242 __stmtID = stmtID;
243 __nfields = PQnfields(res);
244 if (__nfields) {
245 // SELECT, EXECUTE ...
246 // execute에서 CURSOR를 만든다.
247 if (__CURSOR_ENABLED(conn())) {
248 __query.assign(_sql, _sqllen);
249 }
250 }
251
252 PQclear(res);
253 return true;
254}
255
257{
258 if (__nfields && __CURSOR_ENABLED(conn())) {
259 // SELECT, EXECUTE
260 ByteString cursorID = ByteString::format("curs_%d", __stmtNo);
261 ByteStringBuilder cmd(__query.length() + 20);
262 cmd.assign("DECLARE ").append(cursorID).append(" CURSOR FOR ").append(__query);
263 PGresult* res = PQexecParams(conn()->connHandle(), cmd,
264 (int)Query::__paramCount, __BIND_TYPES,
267 );
268 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
269 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
270 PQclear(res);
271 return false;
272 }
273 PQclear(res);
274 __cursorID = cursorID;
275 Query::__eof = false;
276
277 res = PQdescribePortal(conn()->connHandle(), cursorID);
278 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
279 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
280 PQclear(res);
281 return false;
282 }
283#if defined(__DCL_DEBUG) && __TRACE_THIS && 0
284 for (int i = 0; i < PQnfields(res); i++) {
285 Oid oid = PQftype(res, i);
286 __DCL_TRACE6_N(L"[%2d] size [%6d %7d %4d] [%4d %-16hs]\n", i,
287 PQfsize(res, i), PQfmod(res, i), PQfformat(res, i),
288 oid, PQfname(res, i)
289 );
290 }
291#endif
293 (__fields == NULL)
294 && (Query::__fieldCount == 0)
295 );
296 Query::__fieldCount = __nfields;
297 __fields = new PqField[Query::__fieldCount];
298 if (__fields == NULL) {
300 return false;
301 }
302
303 for (size_t i = 0; i < Query::__fieldCount; i++) {
304 if (!__fields[i].init(this, (int)i, res))
305 return false;
306 }
307 PQclear(res);
308 }
309 else {
310 // SELECT, EXECUTE, INSERT, UPDATE, DELETE, ...
311#if defined(__DCL_DEBUG) && __TRACE_THIS && 0
312 for (size_t i = 0; i < Query::__paramCount; i++) {
313 __DCL_TRACE4_N(L"length [%8d] value[%p] type [%4d %ls] \n",
315 __BIND_TYPES[i],
317 );
318 }
319#endif
320 PGresult* res = PQexecPrepared(conn()->connHandle(), __stmtID,
321 (int)Query::__paramCount,
324 );
325 switch (PQresultStatus(res)) {
326 case PGRES_COMMAND_OK: {
327 // INSERT, UPDATE, DELETE, MERGE, MOVE, FETCH, COPY
328 char* psz = PQcmdTuples(res);
329 if (psz) {
330 Query::__affectedRows = strtoll(psz, NULL, 10);
331 }
332 PQclear(res);
333 break;
334 }
335 case PGRES_TUPLES_OK: {
336 if (__nfields != (int)Query::__fieldCount) {
338 (__fields == NULL)
339 && (Query::__fieldCount == 0)
340 );
341 Query::__fieldCount = __nfields;
342 __fields = new PqField[Query::__fieldCount];
343 if (__fields == NULL) {
345 return false;
346 }
347
348 for (size_t i = 0; i < Query::__fieldCount; i++) {
349 if (!__fields[i].init(this, (int)i, res))
350 return false;
351 }
352 }
353 __DCL_ASSERT(PQnfields(res) > 0);
354 __DCL_ASSERT(__row == -1);
355 __res = res;
356 Query::__eof = false;
357 break;
358 }
359 default: {
360 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
361 PQclear(res);
362 return false;
363 }
364 }
365 }
366
367 for(size_t i = 0; i < Query::__paramCount; i++) {
368 __SET_BIND_TYPE(i, 0);
370 __SET_BIND_LENGTH(i, 0);
372
373 if (!(__params[i].onAfterExecute()))
374 return false;
375 }
376
377 return true;
378}
379
381{
382 if (__res) {
383 __row++;
384 }
385
386 if (!__cursorID.isEmpty() && (__res == NULL || PQntuples(__res) == __row)) {
387 // cursor를 사용하도록 되어 있는데,
388 // 처음 FETCH이거나, 이전에 FETCH한 행들의 마지막에 도달했다.
389 ByteStringBuilder cmd;
390 cmd.assign("FETCH ").append(conn()->cursor() == 0 ?
391 "NEXT" : ByteString::valueOf(conn()->cursor()))
392 .append(" IN ").append(__cursorID);
393 __DCL_TRACE1_N(L"__execute [%hs]\n", cmd.data());
394 PGresult* res = PQexecParams(conn()->connHandle(), cmd,
396 );
397 if (PQresultStatus(res) != PGRES_TUPLES_OK) {
398 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
399 PQclear(res);
400 return false;
401 }
402
403 if (__res) {
404 PQclear(__res);
405 }
406 __res = res;
407 __row = 0;
408 }
409
410 bool r = true;
411 if (__row == PQntuples(__res)) {
412 Query::__eof = true;
413 if (!__cursorID.isEmpty()) {
414 // FETCH한 행이 없으므로, cursor를 닫는다.
415 __DCL_ASSERT(!__query.isEmpty());
416 __query.clear();
417
418 ByteString cmd = "CLOSE " + __cursorID;
419 PGresult* res = PQexec(conn()->connHandle(), cmd);
420 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
421 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
422 r = false;
423 }
424 PQclear(res);
425#ifdef LIBPQ_HAS_CLOSE_PREPARED
426 res = PQclosePortal(conn()->connHandle(), __cursorID);
427 if (r && PQresultStatus(res) != PGRES_COMMAND_OK) {
428 __SET_ERROR_MSG(PQerrorMessage(conn()->connHandle()));
429 r = false;
430 }
431 PQclear(res);
432#endif
433 __cursorID.clear();
434 }
435 }
436
437 return r;
438}
439
440bool PqQuery::getValue(int _fieldIndex, const char*& _value, int& _length) const
441{
443 if (PQgetisnull(__res, __row, _fieldIndex))
444 return false;
445
446 _value = PQgetvalue(__res, __row, _fieldIndex);
447 _length = PQgetlength(__res, __row, _fieldIndex);
448 return true;
449}
450
451bool PqQuery::setValue(int _paramIndex, Oid _type, const void* _value, unsigned _length, int _format)
452{
453 __SET_BIND_TYPE(_paramIndex, _type);
454 __SET_BIND_VALUE(_paramIndex, (const char*)_value);
455 __SET_BIND_LENGTH(_paramIndex, (int)_length);
456 __SET_BIND_FORMAT(_paramIndex, _format);
457
458 return true;
459}
460
461bool PqQuery::__getField(size_t _index, SQL::Field** _fieldHandleOut)
462{
463 __DCL_ASSERT(Query::__fieldCount > 0);
464 __DCL_ASSERT((0 <= _index) && (_index < Query::__fieldCount));
465 *_fieldHandleOut = &__fields[_index];
466 return true;
467}
468
469bool PqQuery::__getParam(size_t _index, SQL::Param** _paramHandleOut)
470{
471 __DCL_ASSERT(Query::__paramCount > 0);
472 __DCL_ASSERT((0 <= _index) && (_index < Query::__paramCount));
473 *_paramHandleOut = &__params[_index];
474 return true;
475}
476
477__DCL_END_NAMESPACE
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:312
wchar_t char_t
Definition Config.h:247
#define __DCL_TRACE1_N(fmt, arg)
#define __DCL_TRACE2_N(fmt, arg1, arg2)
#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
#define __SET_ERROR_MSG(_message)
const wchar_t * __dataTypeName(const ifx_sqlvar_t *_sqlvar)
Definition IFXField.cpp:304
IOException *size_t r
Definition MediaInfo.cpp:82
#define __DCL_TRACE6_N(fmt, arg1, arg2, arg3, arg4, arg5, arg6)
Definition ODBCQuery.cpp:45
#define __DCL_TRACE1(fmt, arg1)
Definition Object.h:399
#define __DCL_ASSERT(expr)
Definition Object.h:394
#define IMPLEMENT_CLASSINFO(class_name, base_class_name)
Definition Object.h:245
#define __T(str)
Definition Object.h:60
#define __SET_BIND_LENGTH(_index, _length)
#define __BIND_VALUES
#define __SET_BIND_VALUE(_index, _value)
#define __BIND_TYPES
#define __SET_BIND_FORMAT(_index, _format)
#define __BIND_FORMATS
#define __SET_BIND_TYPE(_index, _type)
#define __BIND_LENGTHS
#define __CURSOR_ENABLED(_conn)
Definition PqQuery.h:10
#define __FORMAT_DEFAULT
Definition PqQuery.h:8
#define __FORMAT_TEXT
Definition PqQuery.h:6
#define __SET_ERROR(_errorCode)
Definition SQLCore.cpp:149
virtual void __destroy()
Definition PqQuery.cpp:86
virtual ~PqQuery()
Definition PqQuery.cpp:71
int __stmtNo
Definition PqQuery.h:25
virtual bool __execute()
Definition PqQuery.cpp:256
int __row
Definition PqQuery.h:40
virtual bool __getParam(size_t _index, SQL::Param **_paramHandleOut)
Definition PqQuery.cpp:469
PqField * __fields
Definition PqQuery.h:36
ByteString __stmtID
Definition PqQuery.h:26
bool reset()
Definition PqQuery.cpp:91
PqQuery(PqConnection *pConnection)
int __nfields
Definition PqQuery.h:29
virtual bool __prepare(const char *_sql, size_t _sqllen, size_t _paramCount)
Definition PqQuery.cpp:162
PqParam * __params
Definition PqQuery.h:37
PGresult * __res
Definition PqQuery.h:39
virtual bool __getField(size_t _index, SQL::Field **_fieldHandleOut)
Definition PqQuery.cpp:461
ByteString __cursorID
Definition PqQuery.h:27
void * __inBINDs
Definition PqQuery.h:35
virtual bool __fetch()
Definition PqQuery.cpp:380
ByteString __query
Definition PqQuery.h:28
@ eOutOfMemory
Definition SQLCore.h:24