12#include <string.h> // strlen, strncpy
16#include <dcl/Object.h>
17#if __DCL_HAVE_ALLOC_DEBUG
18#undef __DCL_ALLOC_LEVEL
19#define __DCL_ALLOC_LEVEL __DCL_ALLOC_INTERNAL
22#include <dcl/Numeric.h>
24#include <dcl/Thread.h>
26#include <dcl/SQLCore.h>
28#include "PgConnection.h"
30#include "PgField.h" // for _getServerInfo
33static const wchar_t __THIS_FILE__[] = __T("dcl/sql/PgConnection.ec");
37IMPLEMENT_CLASSINFO(PgConnection, SQL::Connection)
39static volatile long __connectionID__ = 0;
41PgConnection::PgConnection(const wchar_t* pszServerTitle)
42 : Connection(pszServerTitle)
44 Connection::__canTransact = true;
45 __connectionID = ByteString::format("con:%ld",
46 Thread::incrementAndGet(__connectionID__));
49PgConnection::~PgConnection()
53void PgConnection::destroy()
59pszConnectionString에 가능한 프로퍼티
66bool PgConnection::__open(const char* _pszConnString, size_t _n)
68 ListedByteStringToByteStringMap map;
69 Connection::splitConnectionString(_pszConnString, _n, map);
71 ByteString strServer = map["SERVER"];
72 ByteString strUser = map["USER"];
73 ByteString strPassword = map["PASSWORD"];
74 ByteString strDatabase = map["DATABASE"];
76 ByteStringBuilder sbTarget;
77 if (!strServer.isEmpty()) {
80 if (!strDatabase.isEmpty()) {
81 if (!sbTarget.isEmpty()) {
84 sbTarget += strDatabase;
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;
94 pszConnectionID = (_CONST char*) __connectionID.data();
96 EXEC SQL CONNECT TO :pszTarget AS :pszConnectionID
97 USER :pszUserName USING :pszPassword
101 __SET_ERROR_SQLCODE(SQLCODE);
108bool PgConnection::__close()
110 EXEC SQL BEGIN DECLARE SECTION;
111 char* pszConnectionID = (_CONST char*) __connectionID.data();
112 EXEC SQL END DECLARE SECTION;
114 EXEC SQL DISCONNECT :pszConnectionID;
117 __SET_ERROR_SQLCODE(SQLCODE);
135static STMT_PATTERN sp[] = {
136 { StmtTransBegin, "BEGIN" },
137 { StmtTransEnd, "COMMIT|ROLLBACK|END" },
141static StmtType __GetStmtType(const char* _sql)
143 for(size_t i = 0; sp[i].type != StmtOther; i++)
147 if (Regex::test(sp[i].pattern, _sql, true))
158bool PgConnection::__execute(const char* _sql, size_t n)
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));
174 switch(__GetStmtType(_sql))
176 case StmtTransBegin :
177 __SET_STATE(stInTransaction);
180 __UNSET_STATE(stInTransaction);
190bool PgConnection::__startTrans()
192 EXEC SQL BEGIN DECLARE SECTION;
193 char* pszConnectionID = (_CONST char*) __connectionID.data();
194 EXEC SQL END DECLARE SECTION;
196 EXEC SQL SET CONNECTION :pszConnectionID;
199 __SET_ERROR_SQLCODE(SQLCODE);
206 __SET_ERROR_SQLCODE(SQLCODE);
210 __SET_STATE(stInTransaction);
215bool PgConnection::__commitTrans()
217 EXEC SQL BEGIN DECLARE SECTION;
218 char* pszConnectionID = (_CONST char*) __connectionID.data();
219 EXEC SQL END DECLARE SECTION;
221 EXEC SQL SET CONNECTION :pszConnectionID;
224 __SET_ERROR_SQLCODE(SQLCODE);
228 EXEC SQL COMMIT WORK;
231 __SET_ERROR_SQLCODE(SQLCODE);
235 __UNSET_STATE(stInTransaction);
240bool PgConnection::__rollbackTrans()
242 EXEC SQL BEGIN DECLARE SECTION;
243 char* pszConnectionID = (_CONST char*) __connectionID.data();
244 EXEC SQL END DECLARE SECTION;
246 EXEC SQL SET CONNECTION :pszConnectionID;
249 __SET_ERROR_SQLCODE(SQLCODE);
253 EXEC SQL ROLLBACK WORK;
256 __SET_ERROR_SQLCODE(SQLCODE);
260 __UNSET_STATE(stInTransaction);
265bool PgConnection::__createQueryInstance(SQL::Query** _queryHandleOut)
267 __DCL_ASSERT(_queryHandleOut != NULL);
269 SQL::Query* pNewQuery = new PgQuery(this);
272 __SET_ERROR(SQL::eOutOfMemory);
276 *_queryHandleOut = pNewQuery;
280void PgConnection::setErrorStatus(SQL::Error _error, long _SQLCODE,
281 const wchar_t* _filename, int _line)
283 Connection::setErrorStatus(_error, _filename, _line);
284 if (_SQLCODE == 0 /* ECPG_NO_ERROR */) {
285 __lastErrorMessage.clear();
288 ByteStringBuilder sb;
289 sb.format("SQLCODE(%d) ", _SQLCODE);
290 sb.append(sqlca.sqlerrm.sqlerrmc, sqlca.sqlerrm.sqlerrml);
291 __lastErrorMessage = sb.toByteString();
295bool PgConnection::__getErrorMessage(char* _buf, size_t* _buflen)
297 __DCL_ASSERT(Connection::__errorCode == SQL::eServerError);
298 if (__lastErrorMessage.length() < *_buflen)
299 *_buflen = __lastErrorMessage.length();
300 strncpy(_buf, __lastErrorMessage.data(), *_buflen);
304bool PgConnection::__getServerInfo(char* _buf, size_t* _buflen)
306 PgQuery* pQuery = new PgQuery(this);
307 if (pQuery == NULL) {
308 __SET_ERROR(SQL::eOutOfMemory);
312 bool localTrans = false;
313 if (!inState(SQL::Connection::stInTransaction)) {
318 const char* _sql = "select version()";
320 if (!pQuery->prepare(_sql, ByteString::length(_sql), 0))
323 if (!pQuery->execute())
326 if (!pQuery->fetch())
330 __SET_ERROR(SQL::eNotAvailable);
334 PgField* pField = NULL;
335 if (!pQuery->__getField(0, (SQL::Field**)&pField))
338 if (pField->isNull()) {
339 __SET_ERROR(SQL::eNotAvailable);
343 if (!pField->__getData(_buf, _buflen, SQL::typeText))