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) 2008 Vlad Khorsun <hvlad@users.sourceforge.net>
17 * and all contributors signed below.
18 *
19 * All Rights Reserved.
20 * Contributor(s): ______________________________________.
21 */
22
23#include "firebird.h"
24#include "fb_types.h"
25#include "../../include/fb_blk.h"
26
27#include "../align.h"
28#include "../exe.h"
29#include "../jrd.h"
30#include "../tra.h"
31#include "../common/dsc.h"
32#include "../../dsql/dsql.h"
33#include "../../dsql/sqlda_pub.h"
34#include "../common/classes/InternalMessageBuffer.h"
35
36#include "../blb_proto.h"
37#include "../evl_proto.h"
38#include "../exe_proto.h"
39#include "../mov_proto.h"
40#include "../mov_proto.h"
41#include "../PreparedStatement.h"
42#include "../Function.h"
43
44#include "InternalDS.h"
45
46using namespace Jrd;
47using namespace Firebird;
48
49namespace EDS {
50
51const char* INTERNAL_PROVIDER_NAME = "Internal";
52
53class RegisterInternalProvider
54{
55public:
56 RegisterInternalProvider()
57 {
58 InternalProvider* provider = new InternalProvider(INTERNAL_PROVIDER_NAME);
59 Manager::addProvider(provider);
60 }
61};
62
63static RegisterInternalProvider reg;
64
65// InternalProvider
66
67void InternalProvider::jrdAttachmentEnd(thread_db* tdbb, Jrd::Attachment* att)
68{
69 if (m_connections.getCount() == 0)
70 return;
71
72 Connection** ptr = m_connections.end();
73 Connection** begin = m_connections.begin();
74
75 for (ptr--; ptr >= begin; ptr--)
76 {
77 InternalConnection* conn = (InternalConnection*) *ptr;
78 if (conn->getJrdAtt() == att->att_interface)
79 releaseConnection(tdbb, *conn, false);
80 }
81}
82
83void InternalProvider::getRemoteError(const ISC_STATUS* status, string& err) const
84{
85 err = "";
86
87 char buff[1024];
88 const ISC_STATUS* p = status;
89 const ISC_STATUS* end = status + ISC_STATUS_LENGTH;
90
91 while (p < end)
92 {
93 const ISC_STATUS code = *p ? p[1] : 0;
94 if (!fb_interpret(buff, sizeof(buff), &p))
95 break;
96
97 string rem_err;
98 rem_err.printf("%lu : %s\n", code, buff);
99 err += rem_err;
100 }
101}
102
103Connection* InternalProvider::doCreateConnection()
104{
105 return new InternalConnection(*this);
106}
107
108
109// InternalConnection
110
111InternalConnection::~InternalConnection()
112{
113}
114
115// Status helper
116class IntStatus : public LocalStatus
117{
118public:
119 explicit IntStatus(ISC_STATUS *p)
120 : LocalStatus(), v(p)
121 {}
122
123 ~IntStatus()
124 {
125 if (v)
126 {
127 const ISC_STATUS *s = get();
128 fb_utils::copyStatus(v, ISC_STATUS_LENGTH, s, fb_utils::statusLength(s));
129 }
130 }
131
132private:
133 ISC_STATUS *v;
134};
135
136void InternalConnection::attach(thread_db* tdbb, const Firebird::string& dbName,
137 const Firebird::string& user, const Firebird::string& pwd,
138 const Firebird::string& role)
139{
140 fb_assert(!m_attachment);
141 Database* dbb = tdbb->getDatabase();
142 fb_assert(dbName.isEmpty() || dbName == dbb->dbb_database_name.c_str());
143
144 // Don't wrap raised errors. This is needed for backward compatibility.
145 setWrapErrors(false);
146
147 Jrd::Attachment* attachment = tdbb->getAttachment();
148 if ((user.isEmpty() || user == attachment->att_user->usr_user_name) &&
149 pwd.isEmpty() &&
150 (role.isEmpty() || role == attachment->att_user->usr_sql_role_name))
151 {
152 m_isCurrent = true;
153 m_attachment = attachment->att_interface;
154 }
155 else
156 {
157 m_isCurrent = false;
158 m_dbName = dbb->dbb_database_name.c_str();
159 generateDPB(tdbb, m_dpb, user, pwd, role);
160
161 LocalStatus status;
162 {
163 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
164 Firebird::RefPtr<JProvider> jInstance(JProvider::getInstance());
165 jInstance->setDbCryptCallback(&status, tdbb->getAttachment()->att_crypt_callback);
166 m_attachment = jInstance->attachDatabase(&status, m_dbName.c_str(),
167 m_dpb.getBufferLength(), m_dpb.getBuffer());
168 }
169
170 if (!status.isSuccess())
171 raise(status, tdbb, "JProvider::attach");
172 }
173
174 m_sqlDialect = (m_attachment->getHandle()->att_database->dbb_flags & DBB_DB_SQL_dialect_3) ?
175 SQL_DIALECT_V6 : SQL_DIALECT_V5;
176}
177
178void InternalConnection::doDetach(thread_db* tdbb)
179{
180 fb_assert(m_attachment);
181
182 if (m_isCurrent)
183 {
184 m_attachment = 0;
185 }
186 else
187 {
188 LocalStatus status;
189 JAttachment* att = m_attachment;
190 m_attachment = NULL;
191
192 { // scope
193 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
194 att->detach(&status);
195 }
196
197 if (status.get()[1] == isc_att_shutdown)
198 {
199 status.init();
200 }
201
202 if (!status.isSuccess())
203 {
204 m_attachment = att;
205 raise(status, tdbb, "JAttachment::detach");
206 }
207 }
208
209 fb_assert(!m_attachment);
210}
211
212bool InternalConnection::cancelExecution()
213{
214 if (m_isCurrent)
215 return true;
216
217 LocalStatus status;
218 m_attachment->cancelOperation(&status, fb_cancel_raise);
219 return (status.isSuccess());
220}
221
222// this internal connection instance is available for the current execution context if it
223// a) is current conenction and current thread's attachment is equal to
224// this attachment, or
225// b) is not current conenction
226bool InternalConnection::isAvailable(thread_db* tdbb, TraScope /*traScope*/) const
227{
228 return !m_isCurrent ||
229 (m_isCurrent && (tdbb->getAttachment() == m_attachment->getHandle()));
230}
231
232bool InternalConnection::isSameDatabase(thread_db* tdbb, const Firebird::string& dbName,
233 const Firebird::string& user, const Firebird::string& pwd,
234 const Firebird::string& role) const
235{
236 if (m_isCurrent)
237 {
238 const UserId* attUser = m_attachment->getHandle()->att_user;
239 return ((user.isEmpty() || user == attUser->usr_user_name) &&
240 pwd.isEmpty() &&
241 (role.isEmpty() || role == attUser->usr_sql_role_name));
242 }
243
244 return Connection::isSameDatabase(tdbb, dbName, user, pwd, role);
245}
246
247Transaction* InternalConnection::doCreateTransaction()
248{
249 return new InternalTransaction(*this);
250}
251
252Statement* InternalConnection::doCreateStatement()
253{
254 return new InternalStatement(*this);
255}
256
257Blob* InternalConnection::createBlob()
258{
259 return new InternalBlob(*this);
260}
261
262
263// InternalTransaction()
264
265void InternalTransaction::doStart(ISC_STATUS* status, thread_db* tdbb, ClumpletWriter& tpb)
266{
267 fb_assert(!m_transaction);
268
269 jrd_tra* localTran = tdbb->getTransaction();
270 fb_assert(localTran);
271
272 if (m_scope == traCommon && m_IntConnection.isCurrent())
273 m_transaction = localTran->getInterface();
274 else
275 {
276 JAttachment* att = m_IntConnection.getJrdAtt();
277
278 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
279 IntStatus s(status);
280 m_transaction = att->startTransaction(&s, tpb.getBufferLength(), tpb.getBuffer());
281
282 m_transaction->getHandle()->tra_callback_count = localTran->tra_callback_count;
283 }
284}
285
286void InternalTransaction::doPrepare(ISC_STATUS* /*status*/, thread_db* /*tdbb*/,
287 int /*info_len*/, const char* /*info*/)
288{
289 fb_assert(m_transaction);
290 fb_assert(false);
291}
292
293void InternalTransaction::doCommit(ISC_STATUS* status, thread_db* tdbb, bool retain)
294{
295 fb_assert(m_transaction);
296
297 if (m_scope == traCommon && m_IntConnection.isCurrent())
298 {
299 if (!retain) {
300 m_transaction = NULL;
301 }
302 }
303 else
304 {
305 IntStatus s(status);
306 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
307 if (retain)
308 m_transaction->commitRetaining(&s);
309 else
310 m_transaction->commit(&s);
311 }
312}
313
314void InternalTransaction::doRollback(ISC_STATUS* status, thread_db* tdbb, bool retain)
315{
316 fb_assert(m_transaction);
317
318 if (m_scope == traCommon && m_IntConnection.isCurrent())
319 {
320 if (!retain) {
321 m_transaction = NULL;
322 }
323 }
324 else
325 {
326 IntStatus s(status);
327 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
328 if (retain)
329 m_transaction->rollbackRetaining(&s);
330 else
331 m_transaction->rollback(&s);
332 }
333
334 if (status[1] == isc_att_shutdown && !retain)
335 {
336 m_transaction = NULL;
337 fb_utils::init_status(status);
338 }
339}
340
341
342// InternalStatement
343
344InternalStatement::InternalStatement(InternalConnection& conn) :
345 Statement(conn),
346 m_intConnection(conn),
347 m_intTransaction(0),
348 m_request(0),
349 m_cursor(0),
350 m_inMetadata(new MsgMetadata),
351 m_outMetadata(new MsgMetadata)
352{
353}
354
355InternalStatement::~InternalStatement()
356{
357}
358
359void InternalStatement::doPrepare(thread_db* tdbb, const string& sql)
360{
361 m_inMetadata->items.clear();
362 m_outMetadata->items.clear();
363
364 JAttachment* att = m_intConnection.getJrdAtt();
365 JTransaction* tran = getIntTransaction()->getJrdTran();
366
367 LocalStatus status;
368
369 if (m_request)
370 {
371 doClose(tdbb, true);
372 fb_assert(!m_allocated);
373 }
374
375 {
376 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
377
378 CallerName save_caller_name(tran->getHandle()->tra_caller_name);
379
380 if (m_callerPrivileges)
381 {
382 jrd_req* request = tdbb->getRequest();
383 JrdStatement* statement = request ? request->getStatement() : NULL;
384 CallerName callerName;
385 const Routine* routine;
386
387 if (statement && statement->parentStatement)
388 statement = statement->parentStatement;
389
390 if (statement && statement->triggerName.hasData())
391 tran->getHandle()->tra_caller_name = CallerName(obj_trigger, statement->triggerName);
392 else if (statement && (routine = statement->getRoutine()) &&
393 routine->getName().identifier.hasData())
394 {
395 if (routine->getName().package.isEmpty())
396 {
397 tran->getHandle()->tra_caller_name = CallerName(routine->getObjectType(),
398 routine->getName().identifier);
399 }
400 else
401 {
402 tran->getHandle()->tra_caller_name = CallerName(obj_package_header,
403 routine->getName().package);
404 }
405 }
406 else
407 tran->getHandle()->tra_caller_name = CallerName();
408 }
409
410 m_request = att->prepare(&status, tran, sql.length(), sql.c_str(), m_connection.getSqlDialect(), 0);
411 m_allocated = (m_request != NULL);
412
413 tran->getHandle()->tra_caller_name = save_caller_name;
414 }
415
416 if (!status.isSuccess())
417 raise(status, tdbb, "JAttachment::prepare", &sql);
418
419 const DsqlCompiledStatement* statement = m_request->getHandle()->getStatement();
420
421 if (statement->getSendMsg())
422 {
423 try
424 {
425 PreparedStatement::parseDsqlMessage(statement->getSendMsg(), m_inDescs,
426 m_inMetadata, m_in_buffer);
427 m_inputs = m_inMetadata->items.getCount();
428 }
429 catch (const Exception&)
430 {
431 raise(tdbb->tdbb_status_vector, tdbb, "parse input message", &sql);
432 }
433 }
434 else
435 m_inputs = 0;
436
437 if (statement->getReceiveMsg())
438 {
439 try
440 {
441 PreparedStatement::parseDsqlMessage(statement->getReceiveMsg(), m_outDescs,
442 m_outMetadata, m_out_buffer);
443 m_outputs = m_outMetadata->items.getCount();
444 }
445 catch (const Exception&)
446 {
447 raise(tdbb->tdbb_status_vector, tdbb, "parse output message", &sql);
448 }
449 }
450 else
451 m_outputs = 0;
452
453 m_stmt_selectable = false;
454
455 switch (statement->getType())
456 {
457 case DsqlCompiledStatement::TYPE_SELECT:
458 case DsqlCompiledStatement::TYPE_SELECT_UPD:
459 case DsqlCompiledStatement::TYPE_SELECT_BLOCK:
460 m_stmt_selectable = true;
461 break;
462
463 case DsqlCompiledStatement::TYPE_START_TRANS:
464 case DsqlCompiledStatement::TYPE_COMMIT:
465 case DsqlCompiledStatement::TYPE_ROLLBACK:
466 case DsqlCompiledStatement::TYPE_COMMIT_RETAIN:
467 case DsqlCompiledStatement::TYPE_ROLLBACK_RETAIN:
468 case DsqlCompiledStatement::TYPE_CREATE_DB:
469 status.set(Arg::Gds(isc_eds_expl_tran_ctrl).value());
470 raise(status, tdbb, "JAttachment::prepare", &sql);
471 break;
472
473 case DsqlCompiledStatement::TYPE_INSERT:
474 case DsqlCompiledStatement::TYPE_DELETE:
475 case DsqlCompiledStatement::TYPE_UPDATE:
476 case DsqlCompiledStatement::TYPE_UPDATE_CURSOR:
477 case DsqlCompiledStatement::TYPE_DELETE_CURSOR:
478 case DsqlCompiledStatement::TYPE_DDL:
479 case DsqlCompiledStatement::TYPE_EXEC_PROCEDURE:
480 case DsqlCompiledStatement::TYPE_SET_GENERATOR:
481 case DsqlCompiledStatement::TYPE_SAVEPOINT:
482 case DsqlCompiledStatement::TYPE_EXEC_BLOCK:
483 break;
484 }
485}
486
487
488void InternalStatement::doExecute(thread_db* tdbb)
489{
490 JTransaction* transaction = getIntTransaction()->getJrdTran();
491
492 LocalStatus status;
493 {
494 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
495
496 fb_assert(m_inMetadata->length == m_in_buffer.getCount());
497 InternalMessageBuffer inMsg(m_inMetadata, m_in_buffer.begin());
498 fb_assert(m_outMetadata->length == m_out_buffer.getCount());
499 InternalMessageBuffer outMsg(m_outMetadata, m_out_buffer.begin());
500
501 m_request->execute(&status, transaction,
502 inMsg.metadata, inMsg.buffer, outMsg.metadata, outMsg.buffer);
503 }
504
505 if (!status.isSuccess())
506 raise(status, tdbb, "JStatement::execute");
507}
508
509
510void InternalStatement::doOpen(thread_db* tdbb)
511{
512 JTransaction* transaction = getIntTransaction()->getJrdTran();
513
514 LocalStatus status;
515 {
516 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
517
518 if (m_cursor)
519 {
520 m_cursor->close(&status);
521 m_cursor = NULL;
522 }
523
524 fb_assert(m_inMetadata->length == m_in_buffer.getCount());
525 InternalMessageBuffer inMsg(m_inMetadata, m_in_buffer.begin());
526
527 m_cursor = m_request->openCursor(&status, transaction,
528 inMsg.metadata, inMsg.buffer, m_outMetadata);
529 }
530
531 if (!status.isSuccess())
532 raise(status, tdbb, "JStatement::open");
533}
534
535
536bool InternalStatement::doFetch(thread_db* tdbb)
537{
538 LocalStatus status;
539 bool res = true;
540
541 {
542 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
543
544 fb_assert(m_outMetadata->length == m_out_buffer.getCount());
545 fb_assert(m_cursor);
546 res = m_cursor->fetchNext(&status, m_out_buffer.begin());
547 }
548
549 if (!status.isSuccess())
550 raise(status, tdbb, "JResultSet::fetch");
551
552 return res;
553}
554
555
556void InternalStatement::doClose(thread_db* tdbb, bool drop)
557{
558 LocalStatus status;
559 {
560 EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
561
562 if (m_cursor)
563 m_cursor->close(&status);
564
565 m_cursor = NULL;
566 if (!status.isSuccess())
567 {
568 raise(status, tdbb, "JResultSet::close");
569 }
570
571 if (drop)
572 {
573 if (m_request)
574 m_request->free(&status);
575
576 m_allocated = false;
577 m_request = NULL;
578
579 if (!status.isSuccess())
580 {
581 raise(status, tdbb, "JStatement::free");
582 }
583 }
584 }
585}
586
587void InternalStatement::putExtBlob(thread_db* tdbb, dsc& src, dsc& dst)
588{
589 if (m_transaction->getScope() == traCommon)
590 MOV_move(tdbb, &src, &dst);
591 else
592 Statement::putExtBlob(tdbb, src, dst);
593}
594
595void InternalStatement::getExtBlob(thread_db* tdbb, const dsc& src, dsc& dst)
596{
597 fb_assert(dst.dsc_length == src.dsc_length);
598 fb_assert(dst.dsc_length == sizeof(bid));
599
600 if (m_transaction->getScope() == traCommon)
601 memcpy(dst.dsc_address, src.dsc_address, sizeof(bid));
602 else
603 Statement::getExtBlob(tdbb, src, dst);
604}
605
606
607
608// InternalBlob
609
610InternalBlob::InternalBlob(InternalConnection& conn) :
611 Blob(conn),
612 m_connection(conn),
613 m_blob(NULL)
614{
615 memset(&m_blob_id, 0, sizeof(m_blob_id));
616}
617
618InternalBlob::~InternalBlob()
619{
620 fb_assert(!m_blob);
621}
622
623void InternalBlob::open(thread_db* tdbb, Transaction& tran, const dsc& desc, const UCharBuffer* bpb)
624{
625 fb_assert(!m_blob);
626 fb_assert(sizeof(m_blob_id) == desc.dsc_length);
627
628 JAttachment* att = m_connection.getJrdAtt();
629 JTransaction* transaction = static_cast<InternalTransaction&>(tran).getJrdTran();
630 memcpy(&m_blob_id, desc.dsc_address, sizeof(m_blob_id));
631
632 LocalStatus status;
633 {
634 EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
635
636 USHORT bpb_len = bpb ? bpb->getCount() : 0;
637 const UCHAR* bpb_buff = bpb ? bpb->begin() : NULL;
638
639 m_blob = att->openBlob(&status, transaction, &m_blob_id, bpb_len, bpb_buff);
640 }
641
642 if (!status.isSuccess())
643 m_connection.raise(status, tdbb, "JAttachment::openBlob");
644
645 fb_assert(m_blob);
646}
647
648void InternalBlob::create(thread_db* tdbb, Transaction& tran, dsc& desc, const UCharBuffer* bpb)
649{
650 fb_assert(!m_blob);
651 fb_assert(sizeof(m_blob_id) == desc.dsc_length);
652
653 JAttachment* att = m_connection.getJrdAtt();
654 JTransaction* transaction = ((InternalTransaction&) tran).getJrdTran();
655 memset(&m_blob_id, 0, sizeof(m_blob_id));
656
657 LocalStatus status;
658 {
659 EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
660
661 const USHORT bpb_len = bpb ? bpb->getCount() : 0;
662 const UCHAR* bpb_buff = bpb ? bpb->begin() : NULL;
663
664 m_blob = att->createBlob(&status, transaction, &m_blob_id, bpb_len, bpb_buff);
665 memcpy(desc.dsc_address, &m_blob_id, sizeof(m_blob_id));
666 }
667
668 if (!status.isSuccess())
669 m_connection.raise(status, tdbb, "JAttachment::createBlob");
670
671 fb_assert(m_blob);
672}
673
674USHORT InternalBlob::read(thread_db* tdbb, UCHAR* buff, USHORT len)
675{
676 fb_assert(m_blob);
677
678 USHORT result = 0;
679 LocalStatus status;
680 {
681 EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
682 result = m_blob->getSegment(&status, len, buff);
683 }
684
685 switch (status.get()[1])
686 {
687 case isc_segstr_eof:
688 fb_assert(result == 0);
689 break;
690 case isc_segment:
691 case 0:
692 break;
693 default:
694 m_connection.raise(status, tdbb, "JBlob::getSegment");
695 }
696
697 return result;
698}
699
700void InternalBlob::write(thread_db* tdbb, const UCHAR* buff, USHORT len)
701{
702 fb_assert(m_blob);
703
704 LocalStatus status;
705 {
706 EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
707 m_blob->putSegment(&status, len, buff);
708 }
709
710 if (!status.isSuccess())
711 m_connection.raise(status, tdbb, "JBlob::putSegment");
712}
713
714void InternalBlob::close(thread_db* tdbb)
715{
716 fb_assert(m_blob);
717 LocalStatus status;
718 {
719 EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
720 m_blob->close(&status);
721 }
722
723 if (!status.isSuccess())
724 m_connection.raise(status, tdbb, "JBlob::close");
725
726 fb_assert(!m_blob);
727}
728
729void InternalBlob::cancel(thread_db* tdbb)
730{
731 if (!m_blob) {
732 return;
733 }
734
735 LocalStatus status;
736 {
737 EngineCallbackGuard guard(tdbb, m_connection, FB_FUNCTION);
738 m_blob->cancel(&status);
739 }
740
741 if (!status.isSuccess())
742 m_connection.raise(status, tdbb, "JBlob::cancel");
743
744 fb_assert(!m_blob);
745}
746
747
748} // namespace EDS
749