DCL 4.0
Loading...
Searching...
No Matches
PgConnection.pgc
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#if __DCL_WINDOWS
4 #include <windows.h>
5 #if __DCL_PTHREAD
6 #include <pthread.h>
7 #endif
8#else
9 #include <pthread.h>
10#endif
11
12#include <string.h> // strlen, strncpy
13
14#include <sqlda.h>
15
16#include <dcl/Object.h>
17#if __DCL_HAVE_ALLOC_DEBUG
18#undef __DCL_ALLOC_LEVEL
19#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
20#endif
21
22#include <dcl/Numeric.h>
23#include <dcl/Regex.h>
24#include <dcl/Thread.h>
25
26#include <dcl/SQLCore.h>
27
28#include "PgConnection.h"
29#include "PgQuery.h"
30#include "PgField.h" // for _getServerInfo
31
32#undef __THIS_FILE__
33static const wchar_t __THIS_FILE__[] = __T("dcl/sql/PgConnection.ec");
34
35__DCL_BEGIN_NAMESPACE
36
37IMPLEMENT_CLASSINFO(PgConnection, SQL::Connection)
38
39static volatile long __connectionID__ = 0;
40
41PgConnection::PgConnection(const wchar_t* pszServerTitle)
42 : Connection(pszServerTitle)
43{
44 Connection::__canTransact = true;
45 __connectionID = ByteString::format("con:%ld",
46 Thread::incrementAndGet(__connectionID__));
47}
48
49PgConnection::~PgConnection()
50{
51}
52
53void PgConnection::destroy()
54{
55 delete this;
56}
57
58/*
59pszConnectionString에 가능한 프로퍼티
60 USER,
61 PASSWORD,
62 SERVER, -- host name
63 DATABASE
64*/
65
66bool PgConnection::__open(const char* _pszConnString, size_t _n)
67{
68 ListedByteStringToByteStringMap map;
69 Connection::splitConnectionString(_pszConnString, _n, map);
70
71 ByteString strServer = map["SERVER"];
72 ByteString strUser = map["USER"];
73 ByteString strPassword = map["PASSWORD"];
74 ByteString strDatabase = map["DATABASE"];
75
76 ByteStringBuilder sbTarget;
77 if (!strServer.isEmpty()) {
78 sbTarget = strServer;
79 }
80 if (!strDatabase.isEmpty()) {
81 if (!sbTarget.isEmpty()) {
82 sbTarget += "/";
83 }
84 sbTarget += strDatabase;
85 }
86
87 EXEC SQL BEGIN DECLARE SECTION;
88 char* pszUserName = (_CONST char*) strUser.data();
89 char* pszPassword = (_CONST char*) strPassword.data();
90 char* pszTarget = (_CONST char*) sbTarget.data();
91 char* pszConnectionID = NULL;
92 EXEC SQL END DECLARE SECTION;
93
94 pszConnectionID = (_CONST char*) __connectionID.data();
95
96 EXEC SQL CONNECT TO :pszTarget AS :pszConnectionID
97 USER :pszUserName USING :pszPassword
98 ;
99
100 if (SQLCODE < 0) {
101 __SET_ERROR_SQLCODE(SQLCODE);
102 return false;
103 }
104
105 return true;
106}
107
108bool PgConnection::__close()
109{
110 EXEC SQL BEGIN DECLARE SECTION;
111 char* pszConnectionID = (_CONST char*) __connectionID.data();
112 EXEC SQL END DECLARE SECTION;
113
114 EXEC SQL DISCONNECT :pszConnectionID;
115 if (SQLCODE < 0)
116 {
117 __SET_ERROR_SQLCODE(SQLCODE);
118 return false;
119 }
120
121 return true;
122}
123
124enum StmtType {
125 StmtOther,
126 StmtTransBegin,
127 StmtTransEnd
128};
129
130typedef struct {
131 StmtType type;
132 const char* pattern;
133} STMT_PATTERN;
134
135static STMT_PATTERN sp[] = {
136 { StmtTransBegin, "BEGIN" },
137 { StmtTransEnd, "COMMIT|ROLLBACK|END" },
138 { StmtOther, NULL }
139};
140
141static StmtType __GetStmtType(const char* _sql)
142{
143 for(size_t i = 0; sp[i].type != StmtOther; i++)
144 {
145 try
146 {
147 if (Regex::test(sp[i].pattern, _sql, true))
148 return sp[i].type;
149 }
150 catch(Exception* p)
151 {
152 p->destroy();
153 }
154 }
155 return StmtOther;
156}
157
158bool PgConnection::__execute(const char* _sql, size_t n)
159{
160 // PostgreSQL에서 EXEC SQL EXECUTE IMMEDIATE는 DML만 사용할 수 있다!
161 PGconn* conn = ECPGget_PGconn(__connectionID.data());
162 PGresult* res = PQexec(conn, _sql);
163 ExecStatusType status = PQresultStatus(res);
164 if (status != PGRES_COMMAND_OK) {
165 ByteStringBuilder sb;
166 sb.format("ExecStatusType(%d)", status)
167 .append(PQresultErrorMessage(res));
168 __SET_ERROR_MSG(sb);
169 PQclear(res);
170 return false;
171 }
172 PQclear(res);
173
174 switch(__GetStmtType(_sql))
175 {
176 case StmtTransBegin :
177 __SET_STATE(stInTransaction);
178 break;
179 case StmtTransEnd :
180 __UNSET_STATE(stInTransaction);
181 break;
182 case StmtOther :
183 default :
184 ;
185 }
186
187 return true;
188}
189
190bool PgConnection::__startTrans()
191{
192 EXEC SQL BEGIN DECLARE SECTION;
193 char* pszConnectionID = (_CONST char*) __connectionID.data();
194 EXEC SQL END DECLARE SECTION;
195
196 EXEC SQL SET CONNECTION :pszConnectionID;
197 if (SQLCODE < 0)
198 {
199 __SET_ERROR_SQLCODE(SQLCODE);
200 return false;
201 }
202
203 EXEC SQL BEGIN WORK;
204 if (SQLCODE < 0)
205 {
206 __SET_ERROR_SQLCODE(SQLCODE);
207 return false;
208 }
209
210 __SET_STATE(stInTransaction);
211
212 return true;
213}
214
215bool PgConnection::__commitTrans()
216{
217 EXEC SQL BEGIN DECLARE SECTION;
218 char* pszConnectionID = (_CONST char*) __connectionID.data();
219 EXEC SQL END DECLARE SECTION;
220
221 EXEC SQL SET CONNECTION :pszConnectionID;
222 if (SQLCODE < 0)
223 {
224 __SET_ERROR_SQLCODE(SQLCODE);
225 return false;
226 }
227
228 EXEC SQL COMMIT WORK;
229 if (SQLCODE < 0)
230 {
231 __SET_ERROR_SQLCODE(SQLCODE);
232 return false;
233 }
234
235 __UNSET_STATE(stInTransaction);
236
237 return true;
238}
239
240bool PgConnection::__rollbackTrans()
241{
242 EXEC SQL BEGIN DECLARE SECTION;
243 char* pszConnectionID = (_CONST char*) __connectionID.data();
244 EXEC SQL END DECLARE SECTION;
245
246 EXEC SQL SET CONNECTION :pszConnectionID;
247 if (SQLCODE < 0)
248 {
249 __SET_ERROR_SQLCODE(SQLCODE);
250 return false;
251 }
252
253 EXEC SQL ROLLBACK WORK;
254 if (SQLCODE < 0)
255 {
256 __SET_ERROR_SQLCODE(SQLCODE);
257 return false;
258 }
259
260 __UNSET_STATE(stInTransaction);
261
262 return true;
263}
264
265bool PgConnection::__createQueryInstance(SQL::Query** _queryHandleOut)
266{
267 __DCL_ASSERT(_queryHandleOut != NULL);
268
269 SQL::Query* pNewQuery = new PgQuery(this);
270 if (!pNewQuery)
271 {
272 __SET_ERROR(SQL::eOutOfMemory);
273 return false;
274 }
275
276 *_queryHandleOut = pNewQuery;
277 return true;
278}
279
280void PgConnection::setErrorStatus(SQL::Error _error, long _SQLCODE,
281 const wchar_t* _filename, int _line)
282{
283 Connection::setErrorStatus(_error, _filename, _line);
284 if (_SQLCODE == 0 /* ECPG_NO_ERROR */) {
285 __lastErrorMessage.clear();
286 }
287 else {
288 ByteStringBuilder sb;
289 sb.format("SQLCODE(%d) ", _SQLCODE);
290 sb.append(sqlca.sqlerrm.sqlerrmc, sqlca.sqlerrm.sqlerrml);
291 __lastErrorMessage = sb.toByteString();
292 }
293}
294
295bool PgConnection::__getErrorMessage(char* _buf, size_t* _buflen)
296{
297 __DCL_ASSERT(Connection::__errorCode == SQL::eServerError);
298 if (__lastErrorMessage.length() < *_buflen)
299 *_buflen = __lastErrorMessage.length();
300 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
301 return true;
302}
303
304bool PgConnection::__getServerInfo(char* _buf, size_t* _buflen)
305{
306 PgQuery* pQuery = new PgQuery(this);
307 if (pQuery == NULL) {
308 __SET_ERROR(SQL::eOutOfMemory);
309 return false;
310 }
311
312 bool localTrans = false;
313 if (!inState(SQL::Connection::stInTransaction)) {
314 __startTrans();
315 localTrans = true;
316 }
317
318 const char* _sql = "select version()";
319 for ( ; ; ) {
320 if (!pQuery->prepare(_sql, ByteString::length(_sql), 0))
321 break;
322
323 if (!pQuery->execute())
324 break;
325
326 if (!pQuery->fetch())
327 break;
328
329 if (pQuery->eof()) {
330 __SET_ERROR(SQL::eNotAvailable);
331 break;
332 }
333
334 PgField* pField = NULL;
335 if (!pQuery->__getField(0, (SQL::Field**)&pField))
336 break;
337
338 if (pField->isNull()) {
339 __SET_ERROR(SQL::eNotAvailable);
340 break;
341 }
342
343 if (!pField->__getData(_buf, _buflen, SQL::typeText))
344 break;
345
346 pQuery->__destroy();
347 pQuery = NULL;
348
349 if (localTrans) {
350 __commitTrans();
351 }
352 return true;
353 }
354
355 if (pQuery) {
356 pQuery->__destroy();
357 }
358
359 if (localTrans) {
360 __rollbackTrans();
361 }
362 return false;
363}
364
365__DCL_END_NAMESPACE