DCL 3.7.4
Loading...
Searching...
No Matches
PeConnection.pgc
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <string.h> // strlen, strncpy
4
5#include <sqlda.h>
6
7#include <dcl/Object.h>
8#if __DCL_HAVE_ALLOC_DEBUG
9#undef __DCL_ALLOC_LEVEL
10#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
11#endif
12
13#include <dcl/Numeric.h>
14#include <dcl/Regex.h>
15#include <dcl/SQLCore.h>
16
17#include "PeConnection.h"
18#include "PeQuery.h"
19#include "PeField.h" // for _getServerInfo
20
21#define __TRACE_THIS 0
22#if __TRACE_THIS
23#define __DCL_TRACE0_N __DCL_TRACE0
24#define __DCL_TRACE1_N __DCL_TRACE1
25#define __DCL_TRACE2_N __DCL_TRACE2
26#define __DCL_TRACE3_N __DCL_TRACE3
27#define __DCL_TRACE4_N __DCL_TRACE4
28#else
29#define __DCL_TRACE0_N(fmt)
30#define __DCL_TRACE1_N(fmt, arg)
31#define __DCL_TRACE2_N(fmt, arg1, arg2)
32#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
33#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
34#endif
35
36#undef __THIS_FILE__
37static const char_t __THIS_FILE__[] = __T("dcl/sql/PeConnection.pgc");
38
39__DCL_BEGIN_NAMESPACE
40
41#define __SET_ERROR(_error) \
42 setErrorHandle(_error, 0L, __THIS_FILE__, __LINE__)
43#define __SET_ERROR_HANDLE(_SQLCODE) \
44 setErrorHandle(SQL::eServerError, _SQLCODE, __THIS_FILE__, __LINE__)
45#define __SET_ERROR_MSG(_message) \
46 setErrorMessage(_message, __THIS_FILE__, __LINE__)
47
48IMPLEMENT_CLASSINFO(PeConnection, SQL::Connection)
49
50PeConnection::PeConnection(const wchar_t* pszServerTitle)
51 : Connection(pszServerTitle)
52{
53 Connection::__canTransact = true;
54}
55
56PeConnection::~PeConnection()
57{
58 if (!__connectionID.isEmpty()) {
59 __DCL_TRACE0_N(L"Warning!! The connection was not closed\n");
60 close();
61 }
62}
63
64void PeConnection::destroy()
65{
66 delete this;
67}
68
69/*
70_conns에 가능한 프로퍼티
71 USER,
72 PASSWORD,
73 SERVER, -- host name
74 DATABASE
75*/
76
77bool PeConnection::__open(const char* _conns, size_t _connslen)
78{
79 ListedByteStringToByteStringMap map;
80 Connection::splitConnStr(_conns, _connslen, map);
81
82 ByteString _USER = map["USER"];
83 ByteString _PASSWORD = map["PASSWORD"];
84 ByteString _SERVER = map["SERVER"];
85 ByteString _PORT = map["PORT"];
86 ByteString _DATABASE = map["DATABASE"];
87 ByteString _TARGET;
88
89 {
90 ByteStringBuilder sb;
91 if (!_SERVER.isEmpty()) {
92 sb.append("tcp:postgresql://").append(_SERVER);
93 if (!_PORT.isEmpty()) {
94 sb.append(":").append(_PORT);
95 }
96 }
97 if (!_DATABASE.isEmpty()) {
98 if (!sb.isEmpty()) {
99 sb += "/";
100 }
101 sb += _DATABASE;
102 }
103 _TARGET = sb.toByteString();
104 __DCL_TRACE1_N(L"TARGET [%hs]\n", _TARGET.data());
105 }
106
107 ByteString connectionID = ByteString::format("conn_%zx", (size_t)this);
108
109 EXEC SQL BEGIN DECLARE SECTION;
110 char* user = (_CONST char*) _USER.data();
111 char* pass = (_CONST char*) _PASSWORD.data();
112 char* target = (_CONST char*) _TARGET.data();
113 char* connID = (_CONST char*) connectionID.data();
114 EXEC SQL END DECLARE SECTION;
115
116 EXEC SQL CONNECT TO :target AS :connID
117 USER :user USING :pass;
118
119 if (SQLCODE < 0) {
120 __SET_ERROR_HANDLE(SQLCODE);
121 return false;
122 }
123 __connectionID = connectionID;
124
125 return true;
126}
127
128bool PeConnection::__close()
129{
130 if (__connectionID.isEmpty()) {
131 __SET_ERROR(SQL::eNotConnected);
132 return false;
133 }
134
135 EXEC SQL BEGIN DECLARE SECTION;
136 char* connID = (_CONST char*) __connectionID.data();
137 EXEC SQL END DECLARE SECTION;
138
139 EXEC SQL DISCONNECT :connID;
140 if (SQLCODE < 0) {
141 __SET_ERROR_HANDLE(SQLCODE);
142 return false;
143 }
144
145 __connectionID.clear();
146 return true;
147}
148
149enum StmtType {
150 StmtOther,
151 StmtTransBegin,
152 StmtTransEnd
153};
154
155typedef struct {
156 StmtType type;
157 const char* pattern;
158} STMT_PATTERN;
159
160static STMT_PATTERN sp[] = {
161 { StmtTransBegin, "BEGIN" },
162 { StmtTransEnd, "COMMIT|ROLLBACK|END" },
163 { StmtOther, NULL }
164};
165
166static StmtType __GetStmtType(const char* _sql)
167{
168 for(size_t i = 0; sp[i].type != StmtOther; i++) {
169 try {
170 if (Regex::test(sp[i].pattern, _sql, true))
171 return sp[i].type;
172 }
173 catch(Exception* _e) {
174 _e->destroy();
175 }
176 }
177 return StmtOther;
178}
179
180bool PeConnection::__execute(const char* _sql, size_t _sqllen)
181{
182 // PostgreSQL에서 EXEC SQL EXECUTE IMMEDIATE는 DML만 사용할 수 있다!
183 PGconn* conn = ECPGget_PGconn(__connectionID.data());
184 PGresult* res = PQexec(conn, _sql);
185 ExecStatusType status = PQresultStatus(res);
186 if (status != PGRES_COMMAND_OK) {
187 ByteStringBuilder sb;
188 sb.format("ExecStatusType(%d)", status)
189 .append(PQresultErrorMessage(res));
190 __SET_ERROR_MSG(sb);
191 PQclear(res);
192 return false;
193 }
194 PQclear(res);
195
196 switch(__GetStmtType(_sql)) {
197 case StmtTransBegin :
198 __SET_STATE(stInTransaction);
199 break;
200 case StmtTransEnd :
201 __UNSET_STATE(stInTransaction);
202 break;
203 case StmtOther :
204 default :
205 ;
206 }
207
208 return true;
209}
210
211bool PeConnection::__startTrans()
212{
213 EXEC SQL BEGIN DECLARE SECTION;
214 char* connID = (_CONST char*) __connectionID.data();
215 EXEC SQL END DECLARE SECTION;
216
217 EXEC SQL SET CONNECTION :connID;
218 if (SQLCODE < 0) {
219 __SET_ERROR_HANDLE(SQLCODE);
220 return false;
221 }
222
223 EXEC SQL BEGIN WORK;
224 if (SQLCODE < 0) {
225 __SET_ERROR_HANDLE(SQLCODE);
226 return false;
227 }
228
229 __SET_STATE(stInTransaction);
230 return true;
231}
232
233bool PeConnection::__commitTrans()
234{
235 EXEC SQL BEGIN DECLARE SECTION;
236 char* connID = (_CONST char*) __connectionID.data();
237 EXEC SQL END DECLARE SECTION;
238
239 EXEC SQL SET CONNECTION :connID;
240 if (SQLCODE < 0) {
241 __SET_ERROR_HANDLE(SQLCODE);
242 return false;
243 }
244
245 EXEC SQL COMMIT WORK;
246 if (SQLCODE < 0) {
247 __SET_ERROR_HANDLE(SQLCODE);
248 return false;
249 }
250
251 __UNSET_STATE(stInTransaction);
252 return true;
253}
254
255bool PeConnection::__rollbackTrans()
256{
257 EXEC SQL BEGIN DECLARE SECTION;
258 char* connID = (_CONST char*) __connectionID.data();
259 EXEC SQL END DECLARE SECTION;
260
261 EXEC SQL SET CONNECTION :connID;
262 if (SQLCODE < 0) {
263 __SET_ERROR_HANDLE(SQLCODE);
264 return false;
265 }
266
267 EXEC SQL ROLLBACK WORK;
268 if (SQLCODE < 0) {
269 __SET_ERROR_HANDLE(SQLCODE);
270 return false;
271 }
272
273 __UNSET_STATE(stInTransaction);
274 return true;
275}
276
277bool PeConnection::__createQueryInstance(SQL::Query** _queryHandleOut)
278{
279 __DCL_ASSERT(_queryHandleOut != NULL);
280
281 SQL::Query* pNewQuery = new PeQuery(this);
282 if (!pNewQuery) {
283 __SET_ERROR(SQL::eOutOfMemory);
284 return false;
285 }
286
287 *_queryHandleOut = pNewQuery;
288 return true;
289}
290
291void PeConnection::setErrorHandle(
292 SQL::Error _error, long _SQLCODE,
293 const wchar_t* _filename, int _line
294)
295{
296 Connection::setErrorStatus(_error, _filename, _line);
297 if (_SQLCODE == 0 /* ECPG_NO_ERROR */) {
298 __lastErrorMessage.clear();
299 }
300 else {
301 ByteStringBuilder sb;
302 sb.format("SQLCODE(%d) ", _SQLCODE);
303 sb.append(sqlca.sqlerrm.sqlerrmc, sqlca.sqlerrm.sqlerrml);
304 __lastErrorMessage = sb.toByteString();
305 }
306}
307
308bool PeConnection::__getErrorMessage(char* _buf, size_t* _buflen)
309{
310 __DCL_ASSERT(Connection::__errorCode == SQL::eServerError);
311 if (__lastErrorMessage.length() < *_buflen) {
312 *_buflen = __lastErrorMessage.length();
313 *(_buf + *_buflen) = '\0';
314 }
315 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
316 return true;
317}
318
319bool PeConnection::__getServerInfo(char* _buf, size_t* _buflen)
320{
321 PGconn* conn = ECPGget_PGconn(__connectionID.data());
322 int serverVersion = PQserverVersion(conn);
323 int protocolVersion =
324#ifdef LIBPQ_HAS_FULL_PROTOCOL_VERSION
325 PQfullProtocolVersion(conn)
326#else
327 PQprotocolVersion(conn) * 10000
328#endif
329 ;
330 int libVersion = PQlibVersion();
331
332 ByteString s = ByteString::format("PostgreSQL %d.%d/%d.%d/%d.%d/ecpg",
333 serverVersion / 10000, serverVersion % 10000,
334 protocolVersion / 10000, protocolVersion % 10000,
335 libVersion / 10000, libVersion % 10000
336 );
337 if (s.length() < *_buflen) {
338 *_buflen = s.length();
339 *(_buf + *_buflen) = '\0';
340 }
341 strncpy(_buf, s.data(), *_buflen);
342 return true;
343}
344
345__DCL_END_NAMESPACE