DCL 3.7.4
Loading...
Searching...
No Matches
PqConnection.cpp
Go to the documentation of this file.
1#include <dcl/Config.h>
2
3#include <stdlib.h> // strtol
4#include <string.h> // strlen, strncpy
5
6#include <libpq-fe.h>
7
8#include <dcl/Object.h>
9#if __DCL_HAVE_ALLOC_DEBUG
10#undef __DCL_ALLOC_LEVEL
11#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
12#endif
13
14#include <dcl/size_t.h>
15#include <dcl/Numeric.h>
16#include <dcl/Regex.h>
17#include <dcl/SQLCore.h>
18
19#include "PqConnection.h"
20#include "PqQuery.h"
21
22#define __TRACE_THIS 0
23#if __TRACE_THIS
24#define __DCL_TRACE0_N __DCL_TRACE0
25#define __DCL_TRACE1_N __DCL_TRACE1
26#define __DCL_TRACE2_N __DCL_TRACE2
27#define __DCL_TRACE3_N __DCL_TRACE3
28#define __DCL_TRACE4_N __DCL_TRACE4
29#else
30#define __DCL_TRACE0_N(fmt)
31#define __DCL_TRACE1_N(fmt, arg)
32#define __DCL_TRACE2_N(fmt, arg1, arg2)
33#define __DCL_TRACE3_N(fmt, arg1, arg2, arg3)
34#define __DCL_TRACE4_N(fmt, arg1, arg2, arg3, arg4)
35#endif
36
37#undef __THIS_FILE__
38static const char_t __THIS_FILE__[] = __T("dcl/sql/PqConnection.cpp");
39
40__DCL_BEGIN_NAMESPACE
41
42#define __SET_ERROR(_error) \
43 setErrorStatus(_error, __THIS_FILE__, __LINE__)
44#define __SET_ERROR_MSG(_message) \
45 setErrorMessage(_message, __THIS_FILE__, __LINE__)
46
48
49PqConnection::PqConnection(const wchar_t* pszServerTitle)
50 : Connection(pszServerTitle)
51{
52 Connection::__canTransact = true;
53 __conn = NULL;
54 __cursor = __CURSOR_FETCH_DEFAULT;
55 __stmtNo = 0;
56}
57
59{
60 if (__conn) {
61 __DCL_TRACE0_N(L"Warning!! The connection was not closed\n");
62 close();
63 }
64}
65
67{
68 delete this;
69}
70
71/*
72_conns에 가능한 프로퍼티
73 USER,
74 PASSWORD,
75 SERVER, -- host name
76 DATABASE
77*/
78
79bool PqConnection::__open(const char* _conns, size_t _connslen)
80{
81 ByteString conns(_conns, _connslen);
82 const char* regex = "CURSOR *= *[^&;]*[&;]*";
83 if (conns.searches(regex, true)) {
84 ByteString cursor = conns.substring(regex, true);
85 cursor = cursor.substring(cursor.indexOf('=') + 1).trim();
86 char* endptr;
87 __cursor = strtol(cursor, &endptr, 10);
88
89 conns = conns.replace_r(regex, "", true);
90 }
91
92 ListedByteStringToByteStringMap map;
93 Connection::splitConnStr(conns, conns.length(), map);
94
95 ByteString _USER = map["USER"];
96 ByteString _PASSWORD = map["PASSWORD"];
97 ByteString _SERVER = map["SERVER"];
98 ByteString _PORT = map["PORT"];
99 ByteString _DATABASE = map["DATABASE"];
100 ByteString _CONNECT_TIMEOUT = map["CONNECT_TIMEOUT"];
101 ByteString _APPLICATION = map["APPLICATION"];
102
103 ByteString uri;
104 if (!(_USER.isEmpty() || _SERVER.isEmpty() || _DATABASE.isEmpty())) {
105 // URI를 만든다.
106 ByteStringBuilder sb("postgresql://");
107 sb.append(_USER);
108 if (!_PASSWORD.isEmpty()) {
109 sb.append(":").append(_PASSWORD);
110 }
111 sb.append("@").append(_SERVER);
112 if (!_PORT.isEmpty()) {
113 sb.append(":").append(_PORT);
114 }
115 sb.append("/").append(_DATABASE)
116 .append("?connect_timeout=")
117 .append(_CONNECT_TIMEOUT.isEmpty() ? "10" : _CONNECT_TIMEOUT)
118 .append("&application_name=")
119 .append(_APPLICATION.isEmpty() ? "DCL" : _APPLICATION)
120 ;
121 uri = sb.toByteString();
122 }
123 else {
124 // _conns가 URI이다.
125 uri = conns.replace_r("client_encoding=[^&]*&*", "", true);
126 if (uri.indexOf('?') == (size_t)-1) {
127 uri = uri + '?';
128 }
129 regex = "connect_timeout=[^&]*&*";
130 if (!uri.searches(regex, true)) {
131 uri = uri + "&connect_timeout=10";
132 }
133 regex = "application_name=[^&]*&*";
134 if (!uri.searches(regex, true)) {
135 uri = uri + "&application_name=DCL";
136 }
137
138 uri = uri.replace_r("&{2,}", "&", true)
139 .replace_r("\\?&", "?", true)
140 .trimRight("&")
141 ;
142 }
143 uri = uri + "&client_encoding=UTF8";
144 __DCL_TRACE2_N(L"connection URI [%hs] cursor[%ls]\n",
145 uri.data(), String::valueOf(__cursor).data()
146 );
147
148 PGconn* conn = PQconnectdb(uri.data());
149 if (PQstatus(conn) != CONNECTION_OK) {
150 __SET_ERROR_MSG(PQerrorMessage(conn));
151 return false;
152 }
153
154 __conn = conn;
155
156 if (!__execute("SET DateStyle TO 'ISO'; SET IntervalStyle TO 'iso_8601'", (size_t)-1))
157 return false;
158
159 __DCL_TRACE1_N(L"client_encoding [%hs]\n", PQparameterStatus(conn, "client_encoding"));
160 __DCL_TRACE1_N(L"DateStyle [%hs]\n", PQparameterStatus(conn, "DateStyle"));
161 __DCL_TRACE1_N(L"integer_datetimes [%hs]\n", PQparameterStatus(conn, "integer_datetimes"));
162 __DCL_TRACE1_N(L"IntervalStyle [%hs]\n", PQparameterStatus(conn, "IntervalStyle"));;
163 __DCL_TRACE1_N(L"server_encoding [%hs]\n", PQparameterStatus(conn, "server_encoding"));
164 __DCL_TRACE1_N(L"server_version [%hs]\n", PQparameterStatus(conn, "server_version"));
165 __DCL_TRACE1_N(L"TimeZone [%hs]\n", PQparameterStatus(conn, "TimeZone"));
166
167 return true;
168}
169
171{
172 if (!__conn) {
174 return false;
175 }
176
177 PQfinish(__conn);
178 __conn = NULL;
179
180 return true;
181}
182
188
189typedef struct {
190 StmtType type;
191 const char* pattern;
193
194static STMT_PATTERN sp[] = {
195 { StmtTransBegin, "BEGIN" },
196 { StmtTransEnd, "COMMIT|ROLLBACK|END" },
197 { StmtOther, NULL }
198};
199
200static StmtType __GetStmtType(const char* _sql)
201{
202 for(size_t i = 0; sp[i].type != StmtOther; i++) {
203 try {
204 if (Regex::test(sp[i].pattern, _sql, true))
205 return sp[i].type;
206 }
207 catch(Exception* _e) {
208 _e->destroy();
209 }
210 }
211 return StmtOther;
212}
213
214bool PqConnection::__execute(const char* _sql, size_t _sqllen)
215{
216 PGresult* res = PQexec(__conn, _sql);
217 if (PQresultStatus(res) != PGRES_COMMAND_OK) {
218 __SET_ERROR_MSG(PQerrorMessage(connHandle()));
219 PQclear(res);
220 return false;
221 }
222 PQclear(res);
223
224 switch(__GetStmtType(_sql)) {
225 case StmtTransBegin :
227 break;
228 case StmtTransEnd :
230 break;
231 case StmtOther :
232 default :
233 ;
234 }
235
236 return true;
237}
238
240{
241 return __execute("BEGIN", (size_t)-1);
242}
243
245{
246 return __execute("COMMIT", (size_t)-1);
247}
248
250{
251 return __execute("ROLLBACK", (size_t)-1);
252}
253
255{
256 __DCL_ASSERT(_queryHandleOut != NULL);
257
258 SQL::Query* newQuery = new PqQuery(this);
259 if (!newQuery) {
261 return false;
262 }
263
264 *_queryHandleOut = newQuery;
265
266 return true;
267}
268
269bool PqConnection::__getErrorMessage(char* _buf, size_t* _buflen)
270{
271 __DCL_ASSERT(Connection::__errorCode == SQL::eServerError);
272 if (__lastErrorMessage.length() < *_buflen) {
273 *_buflen = __lastErrorMessage.length();
274 *(_buf + *_buflen) = '\0';
275 }
276 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
277 return true;
278}
279
280bool PqConnection::__getServerInfo(char* _buf, size_t* _buflen)
281{
282 int serverVersion = PQserverVersion(__conn);
283 int protocolVersion =
284#ifdef LIBPQ_HAS_FULL_PROTOCOL_VERSION
285 PQfullProtocolVersion(__conn)
286#else
287 PQprotocolVersion(__conn) * 10000
288#endif
289 ;
290 int libVersion = PQlibVersion();
291
292 ByteString s = ByteString::format("PostgreSQL %d.%d/%d.%d/%d.%d",
293 serverVersion / 10000, serverVersion % 10000,
294 protocolVersion / 10000, protocolVersion % 10000,
295 libVersion / 10000, libVersion % 10000
296 );
297 if (s.length() < *_buflen) {
298 *_buflen = s.length();
299 *(_buf + *_buflen) = '\0';
300 }
301 strncpy(_buf, s.data(), *_buflen);
302 return true;
303}
304
305__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 __SET_ERROR_MSG(_message)
@ StmtTransEnd
@ StmtOther
@ StmtTransBegin
#define __DCL_TRACE0_N(fmt)
#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 __CURSOR_FETCH_DEFAULT
Definition PqQuery.h:9
#define __SET_ERROR(_errorCode)
Definition SQLCore.cpp:149
#define __UNSET_STATE(state)
Definition SQLCore.h:483
#define __SET_STATE(state)
Definition SQLCore.h:482
virtual void destroy()
Definition Exception.cpp:74
PqConnection(const wchar_t *_serverTitle)
virtual bool __open(const char *_conns, size_t _connslen)
int cursor() const
virtual bool __getServerInfo(char *_buf, size_t *_buflen)
virtual void destroy()
virtual bool __getErrorMessage(char *_buf, size_t *_buflen)
virtual bool __close()
virtual bool __execute(const char *_sql, size_t _sqllen)
PGconn * connHandle() const
virtual bool __commitTrans()
virtual bool __createQueryInstance(SQL::Query **_queryHandleOut)
virtual bool __startTrans()
virtual bool __rollbackTrans()
virtual ~PqConnection()
static bool test(const wchar_t *_regex, const wchar_t *_string, bool _icase=false) __DCL_THROWS1(RegexException *)
Definition Regex.cpp:250
@ eOutOfMemory
Definition SQLCore.h:24
@ eServerError
Definition SQLCore.h:21
@ eNotConnected
Definition SQLCore.h:33