1 ///////////////////////////////////////////////////////////////////////////////
\r
3 // File : $Id: database.cpp,v 1.1 2007/05/05 17:00:42 faust Exp $
\r
4 // Subject : IBPP, Database class implementation
\r
6 ///////////////////////////////////////////////////////////////////////////////
\r
8 // (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)
\r
10 // The contents of this file are subject to the IBPP License (the "License");
\r
11 // you may not use this file except in compliance with the License. You may
\r
12 // obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'
\r
13 // file which must have been distributed along with this file.
\r
15 // This software, distributed under the License, is distributed on an "AS IS"
\r
16 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
\r
17 // License for the specific language governing rights and limitations
\r
18 // under the License.
\r
20 ///////////////////////////////////////////////////////////////////////////////
\r
23 // * Tabulations should be set every four characters when editing this file.
\r
25 ///////////////////////////////////////////////////////////////////////////////
\r
28 #pragma warning(disable: 4786 4996)
\r
30 #pragma warning(disable: 4702)
\r
40 #include <algorithm>
\r
42 using namespace ibpp_internals;
\r
44 // (((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))
\r
46 void DatabaseImpl::Create(int dialect)
\r
49 throw LogicExceptionImpl("Database::Create", _("Database is already connected."));
\r
50 if (mDatabaseName.empty())
\r
51 throw LogicExceptionImpl("Database::Create", _("Unspecified database name."));
\r
52 if (mUserName.empty())
\r
53 throw LogicExceptionImpl("Database::Create", _("Unspecified user name."));
\r
54 if (dialect != 1 && dialect != 3)
\r
55 throw LogicExceptionImpl("Database::Create", _("Only dialects 1 and 3 are supported."));
\r
57 // Build the SQL Create Statement
\r
59 create.assign("CREATE DATABASE '");
\r
60 if (! mServerName.empty()) create.append(mServerName).append(":");
\r
61 create.append(mDatabaseName).append("' ");
\r
63 create.append("USER '").append(mUserName).append("' ");
\r
64 if (! mUserPassword.empty())
\r
65 create.append("PASSWORD '").append(mUserPassword).append("' ");
\r
67 if (! mCreateParams.empty()) create.append(mCreateParams);
\r
69 // Call ExecuteImmediate to create the database
\r
70 isc_tr_handle tr_handle = 0;
\r
72 (*gds.Call()->m_dsql_execute_immediate)(status.Self(), &mHandle, &tr_handle,
\r
73 0, const_cast<char*>(create.c_str()), short(dialect), NULL);
\r
74 if (status.Errors())
\r
75 throw SQLExceptionImpl(status, "Database::Create", _("isc_dsql_execute_immediate failed"));
\r
80 void DatabaseImpl::Connect()
\r
82 if (mHandle != 0) return; // Already connected
\r
84 if (mDatabaseName.empty())
\r
85 throw LogicExceptionImpl("Database::Connect", _("Unspecified database name."));
\r
86 if (mUserName.empty())
\r
87 throw LogicExceptionImpl("Database::Connect", _("Unspecified user name."));
\r
89 // Build a DPB based on the properties
\r
91 dpb.Insert(isc_dpb_user_name, mUserName.c_str());
\r
92 dpb.Insert(isc_dpb_password, mUserPassword.c_str());
\r
93 if (! mRoleName.empty()) dpb.Insert(isc_dpb_sql_role_name, mRoleName.c_str());
\r
94 if (! mCharSet.empty()) dpb.Insert(isc_dpb_lc_ctype, mCharSet.c_str());
\r
96 std::string connect;
\r
97 if (! mServerName.empty())
\r
98 connect.assign(mServerName).append(":");
\r
99 connect.append(mDatabaseName);
\r
102 (*gds.Call()->m_attach_database)(status.Self(), (short)connect.size(),
\r
103 const_cast<char*>(connect.c_str()), &mHandle, dpb.Size(), dpb.Self());
\r
104 if (status.Errors())
\r
106 mHandle = 0; // Should be, but better be sure...
\r
107 throw SQLExceptionImpl(status, "Database::Connect", _("isc_attach_database failed"));
\r
110 // Now, get ODS version information and dialect.
\r
111 // If ODS major is lower of equal to 9, we reject the connection.
\r
112 // If ODS major is 10 or higher, this is at least an InterBase 6.x Server
\r
113 // OR FireBird 1.x Server.
\r
115 char items[] = {isc_info_ods_version,
\r
116 isc_info_db_SQL_dialect,
\r
121 (*gds.Call()->m_database_info)(status.Self(), &mHandle, sizeof(items), items,
\r
122 result.Size(), result.Self());
\r
123 if (status.Errors())
\r
126 (*gds.Call()->m_detach_database)(status.Self(), &mHandle);
\r
127 mHandle = 0; // Should be, but better be sure...
\r
128 throw SQLExceptionImpl(status, "Database::Connect", _("isc_database_info failed"));
\r
131 int ODS = result.GetValue(isc_info_ods_version);
\r
135 (*gds.Call()->m_detach_database)(status.Self(), &mHandle);
\r
136 mHandle = 0; // Should be, but better be sure...
\r
137 throw LogicExceptionImpl("Database::Connect",
\r
138 _("Unsupported Server : wrong ODS version (%d), at least '10' required."), ODS);
\r
141 mDialect = result.GetValue(isc_info_db_SQL_dialect);
\r
142 if (mDialect != 1 && mDialect != 3)
\r
145 (*gds.Call()->m_detach_database)(status.Self(), &mHandle);
\r
146 mHandle = 0; // Should be, but better be sure...
\r
147 throw LogicExceptionImpl("Database::Connect", _("Dialect 1 or 3 required"));
\r
150 // Now, verify the GDS32.DLL we are using is compatible with the server
\r
151 if (ODS >= 10 && gds.Call()->mGDSVersion < 60)
\r
154 (*gds.Call()->m_detach_database)(status.Self(), &mHandle);
\r
155 mHandle = 0; // Should be, but better be sure...
\r
156 throw LogicExceptionImpl("Database::Connect", _("GDS32.DLL version 5 against IBSERVER 6"));
\r
160 void DatabaseImpl::Inactivate()
\r
162 if (mHandle == 0) return; // Not connected anyway
\r
166 // Rollback any started transaction...
\r
167 for (unsigned i = 0; i < mTransactions.size(); i++)
\r
169 if (mTransactions[i]->Started())
\r
170 mTransactions[i]->Rollback();
\r
173 // Cancel all pending event traps
\r
174 for (unsigned i = 0; i < mEvents.size(); i++)
\r
175 mEvents[i]->Clear();
\r
177 // Let's detach from all Blobs
\r
178 while (mBlobs.size() > 0)
\r
179 mBlobs.back()->DetachDatabaseImpl();
\r
181 // Let's detach from all Arrays
\r
182 while (mArrays.size() > 0)
\r
183 mArrays.back()->DetachDatabaseImpl();
\r
185 // Let's detach from all Statements
\r
186 while (mStatements.size() > 0)
\r
187 mStatements.back()->DetachDatabaseImpl();
\r
189 // Let's detach from all Transactions
\r
190 while (mTransactions.size() > 0)
\r
191 mTransactions.back()->DetachDatabaseImpl(this);
\r
193 // Let's detach from all Events
\r
194 while (mEvents.size() > 0)
\r
195 mEvents.back()->DetachDatabaseImpl();
\r
198 void DatabaseImpl::Disconnect()
\r
200 if (mHandle == 0) return; // Not connected anyway
\r
202 // Put the connection to rest
\r
205 // Detach from the server
\r
207 (*gds.Call()->m_detach_database)(status.Self(), &mHandle);
\r
209 // Should we throw, set mHandle to 0 first, because Disconnect() may
\r
210 // be called from Database destructor (keeps the object coherent).
\r
212 if (status.Errors())
\r
213 throw SQLExceptionImpl(status, "Database::Disconnect", _("isc_detach_database failed"));
\r
216 void DatabaseImpl::Drop()
\r
219 throw LogicExceptionImpl("Database::Drop", _("Database must be connected."));
\r
221 // Put the connection to a rest
\r
225 (*gds.Call()->m_drop_database)(vector.Self(), &mHandle);
\r
226 if (vector.Errors())
\r
227 throw SQLExceptionImpl(vector, "Database::Drop", _("isc_drop_database failed"));
\r
232 void DatabaseImpl::Info(int* ODSMajor, int* ODSMinor,
\r
233 int* PageSize, int* Pages, int* Buffers, int* Sweep,
\r
234 bool* Sync, bool* Reserve)
\r
237 throw LogicExceptionImpl("Database::Info", _("Database is not connected."));
\r
239 char items[] = {isc_info_ods_version,
\r
240 isc_info_ods_minor_version,
\r
241 isc_info_page_size,
\r
242 isc_info_allocation,
\r
243 isc_info_num_buffers,
\r
244 isc_info_sweep_interval,
\r
245 isc_info_forced_writes,
\r
246 isc_info_no_reserve,
\r
252 (*gds.Call()->m_database_info)(status.Self(), &mHandle, sizeof(items), items,
\r
253 result.Size(), result.Self());
\r
254 if (status.Errors())
\r
255 throw SQLExceptionImpl(status, "Database::Info", _("isc_database_info failed"));
\r
257 if (ODSMajor != 0) *ODSMajor = result.GetValue(isc_info_ods_version);
\r
258 if (ODSMinor != 0) *ODSMinor = result.GetValue(isc_info_ods_minor_version);
\r
259 if (PageSize != 0) *PageSize = result.GetValue(isc_info_page_size);
\r
260 if (Pages != 0) *Pages = result.GetValue(isc_info_allocation);
\r
261 if (Buffers != 0) *Buffers = result.GetValue(isc_info_num_buffers);
\r
262 if (Sweep != 0) *Sweep = result.GetValue(isc_info_sweep_interval);
\r
264 *Sync = result.GetValue(isc_info_forced_writes) == 1 ? true : false;
\r
266 *Reserve = result.GetValue(isc_info_no_reserve) == 1 ? false : true;
\r
269 void DatabaseImpl::Statistics(int* Fetches, int* Marks, int* Reads, int* Writes)
\r
272 throw LogicExceptionImpl("Database::Statistics", _("Database is not connected."));
\r
274 char items[] = {isc_info_fetches,
\r
283 (*gds.Call()->m_database_info)(status.Self(), &mHandle, sizeof(items), items,
\r
284 result.Size(), result.Self());
\r
285 if (status.Errors())
\r
286 throw SQLExceptionImpl(status, "Database::Statistics", _("isc_database_info failed"));
\r
288 if (Fetches != 0) *Fetches = result.GetValue(isc_info_fetches);
\r
289 if (Marks != 0) *Marks = result.GetValue(isc_info_marks);
\r
290 if (Reads != 0) *Reads = result.GetValue(isc_info_reads);
\r
291 if (Writes != 0) *Writes = result.GetValue(isc_info_writes);
\r
294 void DatabaseImpl::Counts(int* Insert, int* Update, int* Delete,
\r
295 int* ReadIdx, int* ReadSeq)
\r
298 throw LogicExceptionImpl("Database::Counts", _("Database is not connected."));
\r
300 char items[] = {isc_info_insert_count,
\r
301 isc_info_update_count,
\r
302 isc_info_delete_count,
\r
303 isc_info_read_idx_count,
\r
304 isc_info_read_seq_count,
\r
310 (*gds.Call()->m_database_info)(status.Self(), &mHandle, sizeof(items), items,
\r
311 result.Size(), result.Self());
\r
312 if (status.Errors())
\r
313 throw SQLExceptionImpl(status, "Database::Counts", _("isc_database_info failed"));
\r
315 if (Insert != 0) *Insert = result.GetCountValue(isc_info_insert_count);
\r
316 if (Update != 0) *Update = result.GetCountValue(isc_info_update_count);
\r
317 if (Delete != 0) *Delete = result.GetCountValue(isc_info_delete_count);
\r
318 if (ReadIdx != 0) *ReadIdx = result.GetCountValue(isc_info_read_idx_count);
\r
319 if (ReadSeq != 0) *ReadSeq = result.GetCountValue(isc_info_read_seq_count);
\r
322 void DatabaseImpl::Users(std::vector<std::string>& users)
\r
325 throw LogicExceptionImpl("Database::Users", _("Database is not connected."));
\r
327 char items[] = {isc_info_user_names,
\r
333 (*gds.Call()->m_database_info)(status.Self(), &mHandle, sizeof(items), items,
\r
334 result.Size(), result.Self());
\r
335 if (status.Errors())
\r
338 throw SQLExceptionImpl(status, "Database::Users", _("isc_database_info failed"));
\r
342 char* p = result.Self();
\r
343 while (*p == isc_info_user_names)
\r
345 p += 3; // Get to the length byte (there are two undocumented bytes which we skip)
\r
346 int len = (int)(*p);
\r
347 ++p; // Get to the first char of username
\r
348 if (len != 0) users.push_back(std::string().append(p, len));
\r
349 p += len; // Skip username
\r
354 IBPP::IDatabase* DatabaseImpl::AddRef()
\r
356 ASSERTION(mRefCount >= 0);
\r
361 void DatabaseImpl::Release()
\r
363 // Release cannot throw, except in DEBUG builds on assertion
\r
364 ASSERTION(mRefCount >= 0);
\r
366 try { if (mRefCount <= 0) delete this; }
\r
370 // (((((((( OBJECT INTERNAL METHODS ))))))))
\r
372 void DatabaseImpl::AttachTransactionImpl(TransactionImpl* tr)
\r
375 throw LogicExceptionImpl("Database::AttachTransaction",
\r
376 _("Transaction object is null."));
\r
378 mTransactions.push_back(tr);
\r
381 void DatabaseImpl::DetachTransactionImpl(TransactionImpl* tr)
\r
384 throw LogicExceptionImpl("Database::DetachTransaction",
\r
385 _("ITransaction object is null."));
\r
387 mTransactions.erase(std::find(mTransactions.begin(), mTransactions.end(), tr));
\r
390 void DatabaseImpl::AttachStatementImpl(StatementImpl* st)
\r
393 throw LogicExceptionImpl("Database::AttachStatement",
\r
394 _("Can't attach a null Statement object."));
\r
396 mStatements.push_back(st);
\r
399 void DatabaseImpl::DetachStatementImpl(StatementImpl* st)
\r
402 throw LogicExceptionImpl("Database::DetachStatement",
\r
403 _("Can't detach a null Statement object."));
\r
405 mStatements.erase(std::find(mStatements.begin(), mStatements.end(), st));
\r
408 void DatabaseImpl::AttachBlobImpl(BlobImpl* bb)
\r
411 throw LogicExceptionImpl("Database::AttachBlob",
\r
412 _("Can't attach a null Blob object."));
\r
414 mBlobs.push_back(bb);
\r
417 void DatabaseImpl::DetachBlobImpl(BlobImpl* bb)
\r
420 throw LogicExceptionImpl("Database::DetachBlob",
\r
421 _("Can't detach a null Blob object."));
\r
423 mBlobs.erase(std::find(mBlobs.begin(), mBlobs.end(), bb));
\r
426 void DatabaseImpl::AttachArrayImpl(ArrayImpl* ar)
\r
429 throw LogicExceptionImpl("Database::AttachArray",
\r
430 _("Can't attach a null Array object."));
\r
432 mArrays.push_back(ar);
\r
435 void DatabaseImpl::DetachArrayImpl(ArrayImpl* ar)
\r
438 throw LogicExceptionImpl("Database::DetachArray",
\r
439 _("Can't detach a null Array object."));
\r
441 mArrays.erase(std::find(mArrays.begin(), mArrays.end(), ar));
\r
444 void DatabaseImpl::AttachEventsImpl(EventsImpl* ev)
\r
447 throw LogicExceptionImpl("Database::AttachEventsImpl",
\r
448 _("Can't attach a null Events object."));
\r
450 mEvents.push_back(ev);
\r
453 void DatabaseImpl::DetachEventsImpl(EventsImpl* ev)
\r
456 throw LogicExceptionImpl("Database::DetachEventsImpl",
\r
457 _("Can't detach a null Events object."));
\r
459 mEvents.erase(std::find(mEvents.begin(), mEvents.end(), ev));
\r
462 DatabaseImpl::DatabaseImpl(const std::string& ServerName, const std::string& DatabaseName,
\r
463 const std::string& UserName, const std::string& UserPassword,
\r
464 const std::string& RoleName, const std::string& CharSet,
\r
465 const std::string& CreateParams) :
\r
467 mRefCount(0), mHandle(0),
\r
468 mServerName(ServerName), mDatabaseName(DatabaseName),
\r
469 mUserName(UserName), mUserPassword(UserPassword), mRoleName(RoleName),
\r
470 mCharSet(CharSet), mCreateParams(CreateParams),
\r
475 DatabaseImpl::~DatabaseImpl()
\r
477 try { if (Connected()) Disconnect(); }
\r