1 | /* |
2 | * The contents of this file are subject to the Initial |
3 | * Developer's Public License Version 1.0 (the "License"); |
4 | * you may not use this file except in compliance with the |
5 | * License. You may obtain a copy of the License at |
6 | * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. |
7 | * |
8 | * Software distributed under the License is distributed AS IS, |
9 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. |
10 | * See the License for the specific language governing rights |
11 | * and limitations under the License. |
12 | * |
13 | * The Original Code was created by Vlad Khorsun |
14 | * for the Firebird Open Source RDBMS project. |
15 | * |
16 | * Copyright (c) 2007 Vlad Khorsun <hvlad@users.sourceforge.net> |
17 | * and all contributors signed below. |
18 | * |
19 | * All Rights Reserved. |
20 | * Contributor(s): ______________________________________. |
21 | */ |
22 | |
23 | #ifndef EXTDS_H |
24 | #define EXTDS_H |
25 | |
26 | #include "../../common/classes/fb_string.h" |
27 | #include "../../common/classes/array.h" |
28 | #include "../../common/classes/ClumpletWriter.h" |
29 | #include "../../common/classes/locks.h" |
30 | #include "../../common/utils_proto.h" |
31 | |
32 | |
33 | namespace Jrd |
34 | { |
35 | class jrd_tra; |
36 | class thread_db; |
37 | class ValueListNode; |
38 | } |
39 | |
40 | namespace EDS { |
41 | |
42 | class Manager; |
43 | class Provider; |
44 | class Connection; |
45 | class Transaction; |
46 | class Statement; |
47 | class Blob; |
48 | |
49 | enum TraModes {traReadCommited, traReadCommitedRecVersions, traConcurrency, traConsistency}; |
50 | enum TraScope {traAutonomous = 1, traCommon, traTwoPhase}; |
51 | |
52 | // Known built-in provider's names |
53 | extern const char* FIREBIRD_PROVIDER_NAME; |
54 | extern const char* INTERNAL_PROVIDER_NAME; |
55 | |
56 | |
57 | // Manage providers |
58 | class Manager : public Firebird::PermanentStorage |
59 | { |
60 | public: |
61 | explicit Manager(Firebird::MemoryPool& pool); |
62 | ~Manager(); |
63 | |
64 | static void addProvider(Provider* provider); |
65 | static Provider* getProvider(const Firebird::string& prvName); |
66 | static Connection* getConnection(Jrd::thread_db* tdbb, |
67 | const Firebird::string& dataSource, const Firebird::string& user, |
68 | const Firebird::string& pwd, const Firebird::string& role, TraScope tra_scope); |
69 | |
70 | // Notify providers when some jrd attachment is about to be released |
71 | static void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att); |
72 | static int shutdown(); |
73 | |
74 | private: |
75 | static Firebird::GlobalPtr<Manager> manager; |
76 | static Firebird::Mutex m_mutex; |
77 | static Provider* m_providers; |
78 | static volatile bool m_initialized; |
79 | }; |
80 | |
81 | |
82 | // manages connections\connection pool |
83 | |
84 | class Provider : public Firebird::GlobalStorage |
85 | { |
86 | friend class Manager; |
87 | friend class EngineCallbackGuard; |
88 | |
89 | public: |
90 | explicit Provider(const char* prvName); |
91 | virtual ~Provider(); |
92 | |
93 | // return existing or create new Connection |
94 | virtual Connection* getConnection(Jrd::thread_db* tdbb, const Firebird::string& dbName, |
95 | const Firebird::string& user, const Firebird::string& pwd, const Firebird::string& role, |
96 | TraScope tra_scope); |
97 | |
98 | // Connection gets unused, release it into pool or delete it completely |
99 | virtual void releaseConnection(Jrd::thread_db* tdbb, Connection& conn, bool inPool = true); |
100 | |
101 | // Notify provider when some jrd attachment is about to be released |
102 | virtual void jrdAttachmentEnd(Jrd::thread_db* tdbb, Jrd::Attachment* att) = 0; |
103 | |
104 | // cancel execution of every connection |
105 | void cancelConnections(); |
106 | |
107 | const Firebird::string& getName() const { return m_name; } |
108 | |
109 | virtual void initialize() = 0; |
110 | |
111 | // Provider properties |
112 | int getFlags() const { return m_flags; } |
113 | |
114 | // Interprete status and put error description into passed string |
115 | virtual void getRemoteError(const ISC_STATUS* status, Firebird::string& err) const = 0; |
116 | |
117 | static const Firebird::string* generate(const Provider* item) |
118 | { |
119 | return &item->m_name; |
120 | } |
121 | |
122 | protected: |
123 | void clearConnections(Jrd::thread_db* tdbb); |
124 | virtual Connection* doCreateConnection() = 0; |
125 | |
126 | // Protection against simultaneous attach database calls. Not sure we still |
127 | // need it, but i believe it will not harm |
128 | Firebird::Mutex m_mutex; |
129 | |
130 | Firebird::string m_name; |
131 | Provider* m_next; |
132 | |
133 | Firebird::Array<Connection*> m_connections; |
134 | int m_flags; |
135 | }; |
136 | |
137 | // Provider flags |
138 | const int prvMultyStmts = 0x0001; // supports many active statements per connection |
139 | const int prvMultyTrans = 0x0002; // supports many active transactions per connection |
140 | const int prvNamedParams = 0x0004; // supports named parameters |
141 | const int prvTrustedAuth = 0x0008; // supports trusted authentication |
142 | |
143 | |
144 | class Connection : public Firebird::PermanentStorage |
145 | { |
146 | protected: |
147 | friend class EngineCallbackGuard; |
148 | friend class Provider; |
149 | |
150 | explicit Connection(Provider& prov); |
151 | virtual ~Connection(); |
152 | |
153 | public: |
154 | static void deleteConnection(Jrd::thread_db* tdbb, Connection* conn); |
155 | |
156 | Provider* getProvider() { return &m_provider; } |
157 | |
158 | virtual void attach(Jrd::thread_db* tdbb, const Firebird::string& dbName, |
159 | const Firebird::string& user, const Firebird::string& pwd, |
160 | const Firebird::string& role) = 0; |
161 | virtual void detach(Jrd::thread_db* tdbb); |
162 | |
163 | virtual bool cancelExecution() = 0; |
164 | |
165 | int getSqlDialect() const { return m_sqlDialect; } |
166 | |
167 | // Is this connections can be used by current needs ? Not every DBMS |
168 | // allows to use same connection in more than one transaction and\or |
169 | // to have more than on active statement at time. See also provider |
170 | // flags above. |
171 | virtual bool isAvailable(Jrd::thread_db* tdbb, TraScope traScope) const = 0; |
172 | |
173 | virtual bool isConnected() const = 0; |
174 | |
175 | virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::string& dbName, |
176 | const Firebird::string& user, const Firebird::string& pwd, |
177 | const Firebird::string& role) const; |
178 | |
179 | // Search for existing transaction of given scope, may return NULL. |
180 | Transaction* findTransaction(Jrd::thread_db* tdbb, TraScope traScope) const; |
181 | |
182 | const Firebird::string getDataSourceName() const |
183 | { |
184 | return m_provider.getName() + "::" + m_dbName; |
185 | } |
186 | |
187 | // Get error description from provider and put it with additional context |
188 | // info into locally raised exception |
189 | void raise(const ISC_STATUS* status, Jrd::thread_db* tdbb, const char* sWhere); |
190 | void raise(const Firebird::IStatus& status, Jrd::thread_db* tdbb, const char* sWhere); |
191 | |
192 | // will we wrap external errors into our ones (isc_eds_xxx) or pass them as is |
193 | bool getWrapErrors() const { return m_wrapErrors; } |
194 | void setWrapErrors(bool val) { m_wrapErrors = val; } |
195 | |
196 | // Transactions management within connection scope : put newly created |
197 | // transaction into m_transactions array and delete not needed transaction |
198 | // immediately (as we didn't pool transactions) |
199 | Transaction* createTransaction(); |
200 | void deleteTransaction(Transaction* tran); |
201 | |
202 | // Statements management within connection scope : put newly created |
203 | // statement into m_statements array, but don't delete freed statement |
204 | // immediately (as we did pooled statements). Instead keep it in |
205 | // m_freeStatements list for reuse later |
206 | Statement* createStatement(const Firebird::string& sql); |
207 | void releaseStatement(Jrd::thread_db* tdbb, Statement* stmt); |
208 | |
209 | virtual Blob* createBlob() = 0; |
210 | |
211 | protected: |
212 | void generateDPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& dpb, |
213 | const Firebird::string& user, const Firebird::string& pwd, |
214 | const Firebird::string& role) const; |
215 | |
216 | virtual Transaction* doCreateTransaction() = 0; |
217 | virtual Statement* doCreateStatement() = 0; |
218 | |
219 | void clearTransactions(Jrd::thread_db* tdbb); |
220 | void clearStatements(Jrd::thread_db* tdbb); |
221 | |
222 | virtual void doDetach(Jrd::thread_db* tdbb) = 0; |
223 | |
224 | // Protection against simultaneous ISC API calls for the same connection |
225 | Firebird::Mutex m_mutex; |
226 | |
227 | Provider& m_provider; |
228 | Firebird::string m_dbName; |
229 | Firebird::ClumpletWriter m_dpb; |
230 | |
231 | Firebird::Array<Transaction*> m_transactions; |
232 | Firebird::Array<Statement*> m_statements; |
233 | Statement* m_freeStatements; |
234 | |
235 | const Jrd::Attachment* m_boundAtt; |
236 | |
237 | static const int MAX_CACHED_STMTS = 16; |
238 | int m_used_stmts; |
239 | int m_free_stmts; |
240 | bool m_deleting; |
241 | int m_sqlDialect; // must be filled in attach call |
242 | bool m_wrapErrors; |
243 | }; |
244 | |
245 | |
246 | class Transaction : public Firebird::PermanentStorage |
247 | { |
248 | protected: |
249 | friend class Connection; |
250 | |
251 | // Create and delete only via parent Connection |
252 | explicit Transaction(Connection& conn); |
253 | virtual ~Transaction(); |
254 | |
255 | public: |
256 | |
257 | Provider* getProvider() { return &m_provider; } |
258 | |
259 | Connection* getConnection() { return &m_connection; } |
260 | |
261 | TraScope getScope() const { return m_scope; } |
262 | |
263 | virtual void start(Jrd::thread_db* tdbb, TraScope traScope, TraModes traMode, |
264 | bool readOnly, bool wait, int lockTimeout); |
265 | virtual void prepare(Jrd::thread_db* tdbb, int info_len, const char* info); |
266 | virtual void commit(Jrd::thread_db* tdbb, bool retain); |
267 | virtual void rollback(Jrd::thread_db* tdbb, bool retain); |
268 | |
269 | static Transaction* getTransaction(Jrd::thread_db* tdbb, |
270 | Connection* conn, TraScope tra_scope); |
271 | |
272 | // Notification about end of some jrd transaction. Bound external transaction |
273 | // (with traCommon scope) must be ended the same way as local jrd transaction |
274 | static void jrdTransactionEnd(Jrd::thread_db* tdbb, Jrd::jrd_tra* tran, |
275 | bool commit, bool retain, bool force); |
276 | |
277 | protected: |
278 | virtual void generateTPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& tpb, |
279 | TraModes traMode, bool readOnly, bool wait, int lockTimeout) const; |
280 | void detachFromJrdTran(); |
281 | |
282 | virtual void doStart(ISC_STATUS* status, Jrd::thread_db* tdbb, Firebird::ClumpletWriter& tpb) = 0; |
283 | virtual void doPrepare(ISC_STATUS* status, Jrd::thread_db* tdbb, int info_len, const char* info) = 0; |
284 | virtual void doCommit(ISC_STATUS* status, Jrd::thread_db* tdbb, bool retain) = 0; |
285 | virtual void doRollback(ISC_STATUS* status, Jrd::thread_db* tdbb, bool retain) = 0; |
286 | |
287 | Provider& m_provider; |
288 | Connection& m_connection; |
289 | TraScope m_scope; |
290 | Transaction* m_nextTran; // next common transaction |
291 | Jrd::jrd_tra* m_jrdTran; // parent JRD transaction |
292 | }; |
293 | |
294 | |
295 | typedef Firebird::Array<Firebird::MetaName*> ParamNames; |
296 | |
297 | class Statement : public Firebird::PermanentStorage |
298 | { |
299 | protected: |
300 | friend class Connection; |
301 | |
302 | // Create and delete only via parent Connection |
303 | explicit Statement(Connection& conn); |
304 | virtual ~Statement(); |
305 | |
306 | public: |
307 | static void deleteStatement(Jrd::thread_db* tdbb, Statement* stmt); |
308 | |
309 | Provider* getProvider() { return &m_provider; } |
310 | |
311 | Connection* getConnection() { return &m_connection; } |
312 | |
313 | Transaction* getTransaction() { return m_transaction; } |
314 | |
315 | void prepare(Jrd::thread_db* tdbb, Transaction* tran, const Firebird::string& sql, bool named); |
316 | void execute(Jrd::thread_db* tdbb, Transaction* tran, |
317 | const Firebird::MetaName* const* in_names, const Jrd::ValueListNode* in_params, |
318 | const Jrd::ValueListNode* out_params); |
319 | void open(Jrd::thread_db* tdbb, Transaction* tran, |
320 | const Firebird::MetaName* const* in_names, const Jrd::ValueListNode* in_params, bool singleton); |
321 | bool fetch(Jrd::thread_db* tdbb, const Jrd::ValueListNode* out_params); |
322 | void close(Jrd::thread_db* tdbb); |
323 | void deallocate(Jrd::thread_db* tdbb); |
324 | |
325 | const Firebird::string& getSql() const { return m_sql; } |
326 | |
327 | void setCallerPrivileges(bool use) { m_callerPrivileges = use; } |
328 | |
329 | bool isActive() const { return m_active; } |
330 | |
331 | bool isAllocated() const { return m_allocated; } |
332 | |
333 | bool isSelectable() const { return m_stmt_selectable; } |
334 | |
335 | unsigned int getInputs() const { return m_inputs; } |
336 | |
337 | unsigned int getOutputs() const { return m_outputs; } |
338 | |
339 | // Get error description from provider and put it with additional contex |
340 | // info into locally raised exception |
341 | void raise(ISC_STATUS* status, Jrd::thread_db* tdbb, const char* sWhere, |
342 | const Firebird::string* sQuery = NULL); |
343 | void raise(const Firebird::IStatus& status, Jrd::thread_db* tdbb, const char* sWhere, |
344 | const Firebird::string* sQuery = NULL); |
345 | |
346 | // Active statement must be bound to parent jrd request |
347 | void bindToRequest(Jrd::jrd_req* request, Statement** impure); |
348 | void unBindFromRequest(); |
349 | |
350 | protected: |
351 | virtual void doPrepare(Jrd::thread_db* tdbb, const Firebird::string& sql) = 0; |
352 | virtual void doExecute(Jrd::thread_db* tdbb) = 0; |
353 | virtual void doOpen(Jrd::thread_db* tdbb) = 0; |
354 | virtual bool doFetch(Jrd::thread_db* tdbb) = 0; |
355 | virtual void doClose(Jrd::thread_db* tdbb, bool drop) = 0; |
356 | |
357 | void setInParams(Jrd::thread_db* tdbb, const Firebird::MetaName* const* names, |
358 | const Jrd::ValueListNode* params); |
359 | virtual void getOutParams(Jrd::thread_db* tdbb, const Jrd::ValueListNode* params); |
360 | |
361 | virtual void doSetInParams(Jrd::thread_db* tdbb, unsigned int count, |
362 | const Firebird::MetaName* const* names, const NestConst<Jrd::ValueExprNode>* params); |
363 | |
364 | virtual void putExtBlob(Jrd::thread_db* tdbb, dsc& src, dsc& dst); |
365 | virtual void getExtBlob(Jrd::thread_db* tdbb, const dsc& src, dsc& dst); |
366 | |
367 | // Preprocess user sql string : replace parameter names by placeholders (?) |
368 | // and remember correspondence between logical parameter names and unnamed |
369 | // placeholders numbers. This is needed only if provider didn't support |
370 | // named parameters natively. |
371 | void preprocess(const Firebird::string& sql, Firebird::string& ret); |
372 | void clearNames(); |
373 | |
374 | |
375 | Provider &m_provider; |
376 | Connection &m_connection; |
377 | Transaction *m_transaction; |
378 | |
379 | Statement* m_nextFree; // next free statement |
380 | |
381 | Jrd::jrd_req* m_boundReq; |
382 | Statement** m_ReqImpure; |
383 | Statement* m_nextInReq; |
384 | Statement* m_prevInReq; |
385 | |
386 | Firebird::string m_sql; |
387 | |
388 | // passed in open() |
389 | bool m_singleton; |
390 | |
391 | // set in open() |
392 | bool m_active; |
393 | |
394 | // set in fetch() |
395 | bool m_fetched; |
396 | |
397 | // if statement executed in autonomous transaction, it must be rolled back, |
398 | // so track the error condition of a statement |
399 | bool m_error; |
400 | |
401 | // set in prepare() |
402 | bool m_allocated; |
403 | bool m_stmt_selectable; |
404 | unsigned int m_inputs; |
405 | unsigned int m_outputs; |
406 | |
407 | bool m_callerPrivileges; |
408 | Jrd::jrd_req* m_preparedByReq; |
409 | |
410 | // set in preprocess |
411 | ParamNames m_sqlParamNames; |
412 | ParamNames m_sqlParamsMap; |
413 | |
414 | // set in prepare() |
415 | Firebird::UCharBuffer m_in_buffer; |
416 | Firebird::UCharBuffer m_out_buffer; |
417 | Firebird::Array<dsc> m_inDescs; |
418 | Firebird::Array<dsc> m_outDescs; |
419 | }; |
420 | |
421 | |
422 | class Blob : public Firebird::PermanentStorage |
423 | { |
424 | friend class Connection; |
425 | protected: |
426 | explicit Blob(Connection& conn) : |
427 | PermanentStorage(conn.getProvider()->getPool()) |
428 | {} |
429 | |
430 | public: |
431 | virtual ~Blob() {} |
432 | |
433 | virtual void open(Jrd::thread_db* tdbb, Transaction& tran, const dsc& desc, |
434 | const Firebird::UCharBuffer* bpb) = 0; |
435 | virtual void create(Jrd::thread_db* tdbb, Transaction& tran, dsc& desc, |
436 | const Firebird::UCharBuffer* bpb) = 0; |
437 | virtual USHORT read(Jrd::thread_db* tdbb, UCHAR* buff, USHORT len) = 0; |
438 | virtual void write(Jrd::thread_db* tdbb, const UCHAR* buff, USHORT len) = 0; |
439 | virtual void close(Jrd::thread_db* tdbb) = 0; |
440 | virtual void cancel(Jrd::thread_db* tdbb) = 0; |
441 | }; |
442 | |
443 | |
444 | class EngineCallbackGuard |
445 | { |
446 | public: |
447 | EngineCallbackGuard(Jrd::thread_db* tdbb, Connection& conn, const char* from) |
448 | { |
449 | init(tdbb, conn, from); |
450 | } |
451 | |
452 | EngineCallbackGuard(Jrd::thread_db* tdbb, Transaction& tran, const char* from) |
453 | { |
454 | init(tdbb, *tran.getConnection(), from); |
455 | } |
456 | |
457 | EngineCallbackGuard(Jrd::thread_db* tdbb, Statement& stmt, const char* from) |
458 | { |
459 | init(tdbb, *stmt.getConnection(), from); |
460 | } |
461 | |
462 | ~EngineCallbackGuard(); |
463 | |
464 | private: |
465 | void init(Jrd::thread_db* tdbb, Connection& conn, const char* from); |
466 | |
467 | Jrd::thread_db* m_tdbb; |
468 | Firebird::Mutex* m_mutex; |
469 | Connection* m_saveConnection; |
470 | }; |
471 | |
472 | } // namespace EDS |
473 | |
474 | #endif // EXTDS_H |
475 | |