3#include <string.h> // strlen, strncpy
8#if __DCL_HAVE_ALLOC_DEBUG
9#undef __DCL_ALLOC_LEVEL
10#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
13#include <dcl/Numeric.h>
15#include <dcl/SQLCore.h>
17#include "PeConnection.h"
19#include "PeField.h" // for _getServerInfo
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
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)
37static const char_t __THIS_FILE__[] = __T("dcl/sql/PeConnection.pgc");
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__)
48IMPLEMENT_CLASSINFO(PeConnection, SQL::Connection)
50PeConnection::PeConnection(const wchar_t* pszServerTitle)
51 : Connection(pszServerTitle)
53 Connection::__canTransact = true;
56PeConnection::~PeConnection()
58 if (!__connectionID.isEmpty()) {
59 __DCL_TRACE0_N(L"Warning!! The connection was not closed\n");
64void PeConnection::destroy()
77bool PeConnection::__open(const char* _conns, size_t _connslen)
79 ListedByteStringToByteStringMap map;
80 Connection::splitConnStr(_conns, _connslen, map);
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"];
91 if (!_SERVER.isEmpty()) {
92 sb.append("tcp:postgresql://").append(_SERVER);
93 if (!_PORT.isEmpty()) {
94 sb.append(":").append(_PORT);
97 if (!_DATABASE.isEmpty()) {
103 _TARGET = sb.toByteString();
104 __DCL_TRACE1_N(L"TARGET [%hs]\n", _TARGET.data());
107 ByteString connectionID = ByteString::format("conn_%zx", (size_t)this);
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;
116 EXEC SQL CONNECT TO :target AS :connID
117 USER :user USING :pass;
120 __SET_ERROR_HANDLE(SQLCODE);
123 __connectionID = connectionID;
128bool PeConnection::__close()
130 if (__connectionID.isEmpty()) {
131 __SET_ERROR(SQL::eNotConnected);
135 EXEC SQL BEGIN DECLARE SECTION;
136 char* connID = (_CONST char*) __connectionID.data();
137 EXEC SQL END DECLARE SECTION;
139 EXEC SQL DISCONNECT :connID;
141 __SET_ERROR_HANDLE(SQLCODE);
145 __connectionID.clear();
160static STMT_PATTERN sp[] = {
161 { StmtTransBegin, "BEGIN" },
162 { StmtTransEnd, "COMMIT|ROLLBACK|END" },
166static StmtType __GetStmtType(const char* _sql)
168 for(size_t i = 0; sp[i].type != StmtOther; i++) {
170 if (Regex::test(sp[i].pattern, _sql, true))
173 catch(Exception* _e) {
180bool PeConnection::__execute(const char* _sql, size_t _sqllen)
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));
196 switch(__GetStmtType(_sql)) {
197 case StmtTransBegin :
198 __SET_STATE(stInTransaction);
201 __UNSET_STATE(stInTransaction);
211bool PeConnection::__startTrans()
213 EXEC SQL BEGIN DECLARE SECTION;
214 char* connID = (_CONST char*) __connectionID.data();
215 EXEC SQL END DECLARE SECTION;
217 EXEC SQL SET CONNECTION :connID;
219 __SET_ERROR_HANDLE(SQLCODE);
225 __SET_ERROR_HANDLE(SQLCODE);
229 __SET_STATE(stInTransaction);
233bool PeConnection::__commitTrans()
235 EXEC SQL BEGIN DECLARE SECTION;
236 char* connID = (_CONST char*) __connectionID.data();
237 EXEC SQL END DECLARE SECTION;
239 EXEC SQL SET CONNECTION :connID;
241 __SET_ERROR_HANDLE(SQLCODE);
245 EXEC SQL COMMIT WORK;
247 __SET_ERROR_HANDLE(SQLCODE);
251 __UNSET_STATE(stInTransaction);
255bool PeConnection::__rollbackTrans()
257 EXEC SQL BEGIN DECLARE SECTION;
258 char* connID = (_CONST char*) __connectionID.data();
259 EXEC SQL END DECLARE SECTION;
261 EXEC SQL SET CONNECTION :connID;
263 __SET_ERROR_HANDLE(SQLCODE);
267 EXEC SQL ROLLBACK WORK;
269 __SET_ERROR_HANDLE(SQLCODE);
273 __UNSET_STATE(stInTransaction);
277bool PeConnection::__createQueryInstance(SQL::Query** _queryHandleOut)
279 __DCL_ASSERT(_queryHandleOut != NULL);
281 SQL::Query* pNewQuery = new PeQuery(this);
283 __SET_ERROR(SQL::eOutOfMemory);
287 *_queryHandleOut = pNewQuery;
291void PeConnection::setErrorHandle(
292 SQL::Error _error, long _SQLCODE,
293 const wchar_t* _filename, int _line
296 Connection::setErrorStatus(_error, _filename, _line);
297 if (_SQLCODE == 0 /* ECPG_NO_ERROR */) {
298 __lastErrorMessage.clear();
301 ByteStringBuilder sb;
302 sb.format("SQLCODE(%d) ", _SQLCODE);
303 sb.append(sqlca.sqlerrm.sqlerrmc, sqlca.sqlerrm.sqlerrml);
304 __lastErrorMessage = sb.toByteString();
308bool PeConnection::__getErrorMessage(char* _buf, size_t* _buflen)
310 __DCL_ASSERT(Connection::__errorCode == SQL::eServerError);
311 if (__lastErrorMessage.length() < *_buflen) {
312 *_buflen = __lastErrorMessage.length();
313 *(_buf + *_buflen) = '\0';
315 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
319bool PeConnection::__getServerInfo(char* _buf, size_t* _buflen)
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)
327 PQprotocolVersion(conn) * 10000
330 int libVersion = PQlibVersion();
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
337 if (s.length() < *_buflen) {
338 *_buflen = s.length();
339 *(_buf + *_buflen) = '\0';
341 strncpy(_buf, s.data(), *_buflen);