8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 20:43:04 +01:00
firebird-mirror/src/jrd/jrd.cpp

6404 lines
149 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: jrd.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: User visible entrypoints
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
2002-07-02 11:49:19 +02:00
*
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
2002-07-02 11:49:19 +02:00
* 2001.07.09 Sean Leyne - Restore default setting to Force Write = "On", for
* Windows NT platform, for new database files. This was changed
* with IB 6.0 to OFF and has introduced many reported database
* corruptions.
2002-10-30 07:40:58 +01:00
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
* Claudio Valderrama C.
* Adriano dos Santos Fernandes
2002-10-30 07:40:58 +01:00
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2004-04-29 00:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
#include <stdlib.h>
#include "../jrd/common.h"
2008-01-16 10:29:37 +01:00
#include "../jrd/ThreadStart.h"
#include "../jrd/os/thd_priority.h"
2001-05-23 15:26:42 +02:00
#include <stdarg.h>
#ifdef HAVE_UNISTD_H
2001-05-23 15:26:42 +02:00
#include <unistd.h>
#endif
2003-02-19 07:14:39 +01:00
#ifdef HAVE_PWD_H
#include <pwd.h>
2001-12-24 03:51:06 +01:00
#endif
2001-05-23 15:26:42 +02:00
#include <errno.h>
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/irq.h"
#include "../jrd/drq.h"
#include "../jrd/req.h"
#include "../jrd/tra.h"
#include "../jrd/blb.h"
#include "../jrd/lck.h"
#include "../jrd/nbak.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/scl.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/ods.h"
#include "../jrd/exe.h"
#include "../jrd/extds/ExtDS.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/val.h"
#include "../jrd/rse.h"
#include "../jrd/all.h"
#include "../jrd/fil.h"
#include "../jrd/intl.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/sbm.h"
#include "../jrd/svc.h"
#include "../jrd/sdw.h"
#include "../jrd/lls.h"
#include "../jrd/cch.h"
#include "../intl/charsets.h"
2003-12-22 11:00:59 +01:00
#include "../jrd/sort.h"
#include "../jrd/PreparedStatement.h"
2003-12-22 11:00:59 +01:00
2001-05-23 15:26:42 +02:00
#include "../jrd/blb_proto.h"
#include "../jrd/cch_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dbg_proto.h"
#include "../jrd/dyn_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/ext_proto.h"
#include "../jrd/fun_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/inf_proto.h"
#include "../jrd/ini_proto.h"
#include "../jrd/intl_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/par_proto.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/scl_proto.h"
#include "../jrd/sdw_proto.h"
#include "../jrd/shut_proto.h"
#include "../jrd/sort_proto.h"
#include "../jrd/thread_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/tra_proto.h"
#include "../jrd/val_proto.h"
2007-01-06 07:52:43 +01:00
#include "../jrd/vio_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/file_params.h"
#include "../jrd/event_proto.h"
#include "../jrd/why_proto.h"
#include "../jrd/flags.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/Database.h"
#include "../common/config/config.h"
#include "../common/config/dir_list.h"
#include "../jrd/plugin_manager.h"
#include "../jrd/db_alias.h"
2005-05-28 00:45:31 +02:00
#include "../jrd/IntlManager.h"
2004-05-03 01:06:37 +02:00
#include "../common/classes/fb_tls.h"
#include "../common/classes/ClumpletReader.h"
#include "../common/classes/RefMutex.h"
#include "../common/utils_proto.h"
#include "../jrd/DebugInterface.h"
2008-02-28 14:48:16 +01:00
#include "../dsql/dsql.h"
#include "../dsql/dsql_proto.h"
using namespace Jrd;
using namespace Firebird;
2004-05-07 00:11:24 +02:00
const SSHORT WAIT_PERIOD = -1;
2001-05-23 15:26:42 +02:00
#ifdef SUPPORT_RAW_DEVICES
#define unlink PIO_unlink
#endif
2008-01-16 10:29:37 +01:00
#ifdef DEV_BUILD
int debug;
#endif
2001-05-23 15:26:42 +02:00
namespace
{
2008-01-16 10:29:37 +01:00
Database* databases = NULL;
GlobalPtr<Mutex> databases_mutex;
bool engineShuttingDown = false;
2008-01-16 10:29:37 +01:00
class EngineStartup
{
public:
static void init()
{
IbUtil::initialize();
2008-01-16 10:29:37 +01:00
IntlManager::initialize();
PluginManager::load_engine_plugins();
}
2008-01-16 10:29:37 +01:00
static void cleanup()
{
}
};
InitMutex<EngineStartup> engineStartup;
inline void validateHandle(thread_db* tdbb, Attachment* const attachment)
{
if (!attachment->checkHandle() ||
!attachment->att_database->checkHandle())
{
status_exception::raise(Arg::Gds(isc_bad_db_handle));
}
tdbb->setAttachment(attachment);
tdbb->setDatabase(attachment->att_database);
}
inline void validateHandle(thread_db* tdbb, jrd_tra* const transaction)
{
if (!transaction->checkHandle())
status_exception::raise(Arg::Gds(isc_bad_trans_handle));
validateHandle(tdbb, transaction->tra_attachment);
tdbb->setTransaction(transaction);
}
inline void validateHandle(thread_db* tdbb, jrd_req* const request)
{
if (!request->checkHandle())
status_exception::raise(Arg::Gds(isc_bad_req_handle));
validateHandle(tdbb, request->req_attachment);
}
inline void validateHandle(thread_db* tdbb, dsql_req* const statement)
2008-02-28 14:48:16 +01:00
{
if (!statement->checkHandle())
status_exception::raise(Arg::Gds(isc_bad_req_handle));
2008-02-28 14:48:16 +01:00
validateHandle(tdbb, statement->req_dbb->dbb_attachment);
}
inline void validateHandle(thread_db* tdbb, blb* blob)
{
if (!blob->checkHandle())
status_exception::raise(Arg::Gds(isc_bad_segstr_handle));
validateHandle(tdbb, blob->blb_transaction);
validateHandle(tdbb, blob->blb_attachment);
}
inline void validateHandle(Service* service)
{
if (!service->checkHandle())
status_exception::raise(Arg::Gds(isc_bad_svc_handle));
}
class DatabaseContextHolder : public Database::SyncGuard, public Jrd::ContextPoolHolder
{
public:
explicit DatabaseContextHolder(thread_db* arg, bool lockAtt = true)
: Database::SyncGuard(arg->getDatabase()),
Jrd::ContextPoolHolder(arg, arg->getDatabase()->dbb_permanent),
tdbb(arg)
{
Attachment *attachment = tdbb->getAttachment();
if (lockAtt && attachment)
{
attLocked = attachment->att_mutex.tryEnter();
if (!attLocked || engineShuttingDown)
status_exception::raise(Arg::Gds(isc_att_handle_busy));
}
else
attLocked = false;
Database* dbb = tdbb->getDatabase();
++dbb->dbb_use_count;
}
~DatabaseContextHolder()
{
Database* dbb = tdbb->getDatabase();
if (dbb->checkHandle())
{
--dbb->dbb_use_count;
}
Attachment* attachment = tdbb->getAttachment();
if (attLocked && attachment)
attachment->att_mutex.leave();
}
private:
// copying is prohibited
DatabaseContextHolder(const DatabaseContextHolder&);
DatabaseContextHolder& operator=(const DatabaseContextHolder&);
thread_db* tdbb;
bool attLocked;
};
2008-07-07 15:33:28 +02:00
void validateAccess(const Attachment* attachment)
{
if (!attachment->locksmith())
{
ERR_post(Arg::Gds(isc_adm_task_denied));
}
}
2008-01-16 10:29:37 +01:00
} // anonymous
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
#include <windows.h>
2006-07-21 03:35:17 +02:00
// these should stop a most annoying warning
2001-05-23 15:26:42 +02:00
#undef TEXT
#define TEXT SCHAR
#endif // WIN_NT
2004-04-19 17:29:29 +02:00
void Jrd::Trigger::compile(thread_db* tdbb)
2002-10-24 13:16:59 +02:00
{
if (!request /*&& !compile_in_progress*/)
2002-10-24 13:16:59 +02:00
{
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
if (request)
{
return;
}
compile_in_progress = true;
// Allocate statement memory pool
2008-01-29 11:11:52 +01:00
MemoryPool* new_pool = dbb->createPool();
// Trigger request is not compiled yet. Lets do it now
USHORT par_flags = (USHORT)
(flags & TRG_ignore_perm) ? csb_ignore_perm : 0;
if (type & 1)
par_flags |= csb_pre_trigger;
else
par_flags |= csb_post_trigger;
2006-11-03 10:42:42 +01:00
CompilerScratch* csb = NULL;
2002-09-26 20:13:02 +02:00
try {
Jrd::ContextPoolHolder context(tdbb, new_pool);
2006-11-03 10:42:42 +01:00
csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
csb->csb_g_flags |= par_flags;
if (!dbg_blob_id.isEmpty())
DBG_parse_debug_info(tdbb, &dbg_blob_id, csb->csb_dbg_info);
PAR_blr(tdbb, relation, blr.begin(), NULL, &csb, &request, (relation ? true : false),
par_flags);
delete csb;
2002-10-24 13:16:59 +02:00
}
catch (const Exception&)
{
compile_in_progress = false;
2006-11-03 10:42:42 +01:00
if (csb) {
delete csb;
csb = NULL;
}
if (request) {
2004-11-24 10:22:07 +01:00
CMP_release(tdbb, request);
request = NULL;
}
else {
2008-01-29 11:11:52 +01:00
dbb->deletePool(new_pool);
2002-10-24 13:16:59 +02:00
}
2002-09-26 20:13:02 +02:00
throw;
}
2008-12-05 02:20:14 +01:00
request->req_trg_name = name;
if (sys_trigger)
2004-04-19 17:29:29 +02:00
{
2002-10-24 13:16:59 +02:00
request->req_flags |= req_sys_trigger;
2004-04-19 17:29:29 +02:00
}
if (flags & TRG_ignore_perm)
2004-04-19 17:29:29 +02:00
{
2002-10-24 13:16:59 +02:00
request->req_flags |= req_ignore_perm;
2004-04-19 17:29:29 +02:00
}
2002-10-24 13:16:59 +02:00
compile_in_progress = false;
}
}
2004-04-19 17:29:29 +02:00
void Jrd::Trigger::release(thread_db* tdbb)
2002-10-24 13:16:59 +02:00
{
2008-01-29 02:36:41 +01:00
if (blr.getCount() == 0 || //sys_trigger
!request || CMP_clone_is_active(request))
{
return; // FALSE;
2002-10-24 13:16:59 +02:00
}
2008-12-05 02:20:14 +01:00
CMP_release(tdbb, request);
2002-10-24 13:16:59 +02:00
request = NULL;
return; // TRUE;
}
2006-07-21 03:35:17 +02:00
// Option block for database parameter block
2001-05-23 15:26:42 +02:00
class DatabaseOptions
2001-05-23 15:26:42 +02:00
{
public:
2001-05-23 15:26:42 +02:00
USHORT dpb_wal_action;
SLONG dpb_sweep_interval;
ULONG dpb_page_buffers;
bool dpb_set_page_buffers;
ULONG dpb_buffers;
2001-05-23 15:26:42 +02:00
USHORT dpb_verify;
USHORT dpb_sweep;
USHORT dpb_dbkey_scope;
USHORT dpb_page_size;
bool dpb_activate_shadow;
bool dpb_delete_shadow;
bool dpb_no_garbage;
2001-05-23 15:26:42 +02:00
USHORT dpb_shutdown;
SSHORT dpb_shutdown_delay;
USHORT dpb_online;
bool dpb_force_write;
bool dpb_set_force_write;
bool dpb_no_reserve;
bool dpb_set_no_reserve;
2001-05-23 15:26:42 +02:00
SSHORT dpb_interp;
bool dpb_single_user;
bool dpb_overwrite;
bool dpb_sec_attach;
bool dpb_disable_wal;
bool dpb_gsec_attach;
2001-05-23 15:26:42 +02:00
SLONG dpb_connect_timeout;
SLONG dpb_dummy_packet_interval;
bool dpb_db_readonly;
bool dpb_set_db_readonly;
bool dpb_gfix_attach;
bool dpb_gstat_attach;
2001-05-23 15:26:42 +02:00
USHORT dpb_sql_dialect;
USHORT dpb_set_db_sql_dialect;
SLONG dpb_remote_pid;
bool dpb_no_db_triggers;
2007-10-24 09:03:37 +02:00
bool dpb_gbak_attach;
2008-01-16 10:29:37 +01:00
bool dpb_trusted_role;
bool dpb_utf8_filename;
ULONG dpb_flags; // to OR'd with dbb_flags
2008-01-16 10:29:37 +01:00
2008-07-06 18:42:52 +02:00
// here begin compound objects
2008-12-05 02:20:14 +01:00
// for constructor to work properly dpb_sys_user_name
2008-07-06 18:42:52 +02:00
// MUST be FIRST
string dpb_sys_user_name;
string dpb_user_name;
string dpb_password;
string dpb_password_enc;
string dpb_role_name;
string dpb_journal;
string dpb_key;
PathName dpb_lc_messages;
string dpb_lc_ctype;
PathName dpb_working_directory;
string dpb_set_db_charset;
string dpb_network_protocol;
string dpb_remote_address;
string dpb_trusted_login;
PathName dpb_remote_process;
PathName dpb_org_filename;
public:
DatabaseOptions()
{
2008-12-05 02:20:14 +01:00
memset(this, 0,
reinterpret_cast<char*>(&this->dpb_sys_user_name) -
reinterpret_cast<char*>(this));
}
void get(const UCHAR*, USHORT, bool&);
2004-05-24 19:31:47 +02:00
};
static void cancel_attachments();
static void check_database(thread_db* tdbb);
static void check_transaction(thread_db*, jrd_tra*);
static void commit(thread_db*, jrd_tra*, const bool);
2008-01-16 10:29:37 +01:00
static bool drop_files(const jrd_file*);
static void find_intl_charset(thread_db*, Attachment*, const DatabaseOptions*);
static jrd_tra* find_transaction(thread_db*, ISC_STATUS);
static void init_database_locks(thread_db*);
2008-01-16 10:29:37 +01:00
static ISC_STATUS handle_error(ISC_STATUS*, ISC_STATUS);
static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction);
static void verify_request_synchronization(jrd_req*& request, SSHORT level);
static unsigned int purge_transactions(thread_db*, Attachment*, const bool, const ULONG);
namespace {
enum vdnResult {vdnFail, vdnOk, vdnSecurity};
}
static vdnResult verify_database_name(const PathName&, ISC_STATUS*);
2008-12-05 02:20:14 +01:00
static ISC_STATUS unwindAttach(const Exception& ex,
ISC_STATUS* userStatus,
thread_db* tdbb,
Attachment* attachment,
2008-01-16 10:29:37 +01:00
Database* dbb);
#ifdef WIN_NT
static void ExtractDriveLetter(const TEXT*, ULONG*);
#endif
2001-05-23 15:26:42 +02:00
static Database* init(thread_db*, const PathName&, bool);
static void prepare(thread_db*, jrd_tra*, USHORT, const UCHAR*);
2008-08-31 03:10:41 +02:00
static void release_attachment(thread_db*, Attachment*, ISC_STATUS* = NULL);
2007-06-15 11:28:56 +02:00
static void detachLocksFromAttachment(Attachment*);
static void rollback(thread_db*, jrd_tra*, const bool);
2004-03-07 08:58:55 +01:00
static void shutdown_database(Database*, const bool);
static void strip_quotes(string&);
static void purge_attachment(thread_db*, ISC_STATUS*, Attachment*, const bool);
static void getUserInfo(UserId&, const DatabaseOptions&);
static bool shutdown_dbb(thread_db*, Database*);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM);
2001-05-23 15:26:42 +02:00
2008-05-05 04:22:53 +02:00
static void cancel_attachments()
{
engineShuttingDown = true;
MutexLockGuard guard(databases_mutex);
2008-05-05 04:22:53 +02:00
for (Database* dbb = databases; dbb; dbb = dbb->dbb_next)
{
if ( !(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use | DBB_security_db)) )
{
Database::SyncGuard dsGuard(dbb);
2008-05-05 04:22:53 +02:00
Attachment* lockedAtt = NULL;
2008-12-05 02:20:14 +01:00
Attachment* att = dbb->dbb_attachments;
2008-05-05 04:22:53 +02:00
while (att)
{
// Try to cancel attachment and lock it. Handle case when attachment
// deleted while waiting for lock.
while (true)
{
2008-12-05 02:20:14 +01:00
if (att->att_mutex.tryEnter() || (att->att_flags & ATT_purge_error))
{
lockedAtt = att;
break;
}
2008-12-05 02:20:14 +01:00
{
const bool cancel_disable = (att->att_flags & ATT_cancel_disable);
Database::Checkout dcoHolder(dbb);
if (!cancel_disable)
{
ISC_STATUS_ARRAY status;
jrd8_cancel_operation(status, &att, fb_cancel_enable);
jrd8_cancel_operation(status, &att, fb_cancel_raise);
}
THREAD_YIELD();
}
// check if attachment still exist
if (lockedAtt && lockedAtt->att_next != att) {
break;
}
if (dbb->dbb_attachments != att) {
break;
}
}
att = lockedAtt ? lockedAtt->att_next : dbb->dbb_attachments;
}
}
2008-05-05 04:22:53 +02:00
}
}
2008-05-05 04:22:53 +02:00
2001-05-23 15:26:42 +02:00
//____________________________________________________________
//
// check whether we need to perform an autocommit;
// do it here to prevent committing every record update
// in a statement
//
static void check_autocommit(jrd_req* request, thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
2006-07-21 03:35:17 +02:00
// dimitr: we should ignore autocommit for requests
// created by EXECUTE STATEMENT
2008-12-05 02:20:14 +01:00
// AP: also do nothing if request is cancelled and
// transaction is already missing
if ((!request->req_transaction) || (request->req_transaction->tra_callback_count > 0))
return;
2001-05-23 15:26:42 +02:00
if (request->req_transaction->tra_flags & TRA_perform_autocommit)
{
if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers) &&
!(request->req_transaction->tra_flags & TRA_prepared))
{
// run ON TRANSACTION COMMIT triggers
run_commit_triggers(tdbb, request->req_transaction);
}
2001-05-23 15:26:42 +02:00
request->req_transaction->tra_flags &= ~TRA_perform_autocommit;
TRA_commit(tdbb, request->req_transaction, true);
2001-05-23 15:26:42 +02:00
}
}
const int SWEEP_INTERVAL = 20000;
2001-05-23 15:26:42 +02:00
const char DBL_QUOTE = '\042';
const char SINGLE_QUOTE = '\'';
2001-05-23 15:26:42 +02:00
#define GDS_ATTACH_DATABASE jrd8_attach_database
#define GDS_BLOB_INFO jrd8_blob_info
#define GDS_CANCEL_BLOB jrd8_cancel_blob
#define GDS_CANCEL_EVENTS jrd8_cancel_events
#define FB_CANCEL_OPERATION jrd8_cancel_operation
2001-05-23 15:26:42 +02:00
#define GDS_CLOSE_BLOB jrd8_close_blob
#define GDS_COMMIT jrd8_commit_transaction
#define GDS_COMMIT_RETAINING jrd8_commit_retaining
#define GDS_COMPILE jrd8_compile_request
#define GDS_CREATE_BLOB2 jrd8_create_blob2
#define GDS_CREATE_DATABASE jrd8_create_database
#define GDS_DATABASE_INFO jrd8_database_info
#define GDS_DDL jrd8_ddl
#define GDS_DETACH jrd8_detach_database
#define GDS_DROP_DATABASE jrd8_drop_database
#define GDS_GET_SEGMENT jrd8_get_segment
#define GDS_GET_SLICE jrd8_get_slice
#define GDS_OPEN_BLOB2 jrd8_open_blob2
#define GDS_PREPARE jrd8_prepare_transaction
#define GDS_PUT_SEGMENT jrd8_put_segment
#define GDS_PUT_SLICE jrd8_put_slice
#define GDS_QUE_EVENTS jrd8_que_events
#define GDS_RECONNECT jrd8_reconnect_transaction
#define GDS_RECEIVE jrd8_receive
#define GDS_RELEASE_REQUEST jrd8_release_request
#define GDS_REQUEST_INFO jrd8_request_info
#define GDS_ROLLBACK jrd8_rollback_transaction
#define GDS_ROLLBACK_RETAINING jrd8_rollback_retaining
#define GDS_SEEK_BLOB jrd8_seek_blob
#define GDS_SEND jrd8_send
#define GDS_SERVICE_ATTACH jrd8_service_attach
#define GDS_SERVICE_DETACH jrd8_service_detach
#define GDS_SERVICE_QUERY jrd8_service_query
#define GDS_SERVICE_START jrd8_service_start
#define GDS_START_AND_SEND jrd8_start_and_send
#define GDS_START jrd8_start_request
#define GDS_START_MULTIPLE jrd8_start_multiple
#define GDS_START_TRANSACTION jrd8_start_transaction
#define GDS_TRANSACT_REQUEST jrd8_transact_request
#define GDS_TRANSACTION_INFO jrd8_transaction_info
#define GDS_UNWIND jrd8_unwind_request
#define GDS_SHUTDOWN jrd8_shutdown_all
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
#define GDS_DSQL_ALLOCATE jrd8_allocate_statement
#define GDS_DSQL_EXECUTE jrd8_execute
#define GDS_DSQL_EXECUTE_IMMEDIATE jrd8_execute_immediate
#define GDS_DSQL_FETCH jrd8_fetch
#define GDS_DSQL_FREE jrd8_free_statement
#define GDS_DSQL_INSERT jrd8_insert
#define GDS_DSQL_PREPARE jrd8_prepare
#define GDS_DSQL_SET_CURSOR jrd8_set_cursor
#define GDS_DSQL_SQL_INFO jrd8_sql_info
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// External hook definitions
2001-05-23 15:26:42 +02:00
/* dimitr: just uncomment the following line to use this feature.
Requires support from the PIO modules. Only Win32 is 100% ready
for this so far. Note that the database encryption code in the
PIO layer seems to be incompatible with the SUPERSERVER_V2 code.
2003.02.09 */
//#define ISC_DATABASE_ENCRYPTION
static const char* CRYPT_IMAGE = "fbcrypt";
static const char* ENCRYPT = "encrypt";
static const char* DECRYPT = "decrypt";
2001-05-23 15:26:42 +02:00
2006-07-01 10:50:21 +02:00
void JRD_print_pools(const char* filename)
{
FILE* out = fopen(filename, "w");
if (out)
{
ALL_print_memory_pool_info(out, databases);
fclose(out);
}
}
2004-10-28 07:23:16 +02:00
ISC_STATUS GDS_ATTACH_DATABASE(ISC_STATUS* user_status,
const TEXT* filename,
Attachment** handle,
SSHORT dpb_length,
const UCHAR* dpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ a t t a c h _ d a t a b a s e
*
**************************************
*
* Functional description
* Attach a moldy, grungy, old database
* sullied by user data.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
if (*handle)
{
2008-01-16 10:29:37 +01:00
return handle_error(user_status, isc_bad_db_handle);
2001-05-23 15:26:42 +02:00
}
UserId userId;
DatabaseOptions options;
bool invalid_client_SQL_dialect = false;
SecurityDatabase::InitHolder siHolder;
try
{
// Process database parameter block
options.get(dpb, dpb_length, invalid_client_SQL_dialect);
2008-11-14 15:15:37 +01:00
// Check for correct credentials supplied
getUserInfo(userId, options);
}
catch (const DelayFailedLogin& ex)
{
ex.sleep();
return ex.stuff_exception(user_status);
}
catch (const Exception& e)
{
e.stuff_exception(user_status);
return user_status[1];
}
PathName file_name = options.dpb_org_filename.hasData() ?
2008-08-28 17:39:51 +02:00
options.dpb_org_filename : filename;
if (options.dpb_utf8_filename)
ISC_utf8ToSystem(file_name);
else
{
ISC_systemToUtf8(file_name);
ISC_unescape(file_name);
ISC_utf8ToSystem(file_name);
}
PathName expanded_name;
// Resolve given alias name
const bool is_alias = ResolveDatabaseAlias(file_name, expanded_name);
if (is_alias)
{
ISC_systemToUtf8(expanded_name);
ISC_unescape(expanded_name);
ISC_utf8ToSystem(expanded_name);
ISC_expand_filename(expanded_name, false);
}
else
{
expanded_name = filename;
if (!options.dpb_utf8_filename)
ISC_systemToUtf8(expanded_name);
ISC_unescape(expanded_name);
ISC_utf8ToSystem(expanded_name);
}
2006-07-21 03:35:17 +02:00
// Check database against conf file.
const vdnResult vdn = verify_database_name(expanded_name, user_status);
if (!is_alias && vdn == vdnFail)
{
return user_status[1];
}
2001-05-23 15:26:42 +02:00
Database* dbb = NULL;
MutexEnsureUnlock guardDatabases(databases_mutex);
guardDatabases.enter();
try
{
// Unless we're already attached, do some initialization
dbb = init(tdbb, expanded_name, true);
}
catch (const Exception& ex)
{
ex.stuff_exception(user_status);
2001-05-23 15:26:42 +02:00
return user_status[1];
}
fb_assert(dbb);
tdbb->setDatabase(dbb);
DatabaseContextHolder dbbHolder(tdbb);
2001-05-23 15:26:42 +02:00
dbb->dbb_flags |= DBB_being_opened;
2006-07-21 03:35:17 +02:00
// Initialize special error handling
2001-05-23 15:26:42 +02:00
2008-01-16 10:29:37 +01:00
ISC_STATUS* const status = user_status;
Attachment* attachment = NULL;
2001-05-23 15:26:42 +02:00
bool initing_security = false;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
#ifndef NO_NFS
2006-07-21 03:35:17 +02:00
// Don't check nfs if single user
2001-05-23 15:26:42 +02:00
if (!options.dpb_single_user)
#endif
{
2006-07-21 03:35:17 +02:00
// Check to see if the database is truly local or if it just looks
// that way
2008-12-05 02:20:14 +01:00
if (ISC_check_if_remote(expanded_name, true)) {
ERR_post(Arg::Gds(isc_unavailable));
2003-04-06 11:08:58 +02:00
}
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
/* If database to be opened is security database, then only
gsec or SecurityDatabase may open it. This protects from use
of old gsec to write wrong password hashes into it. */
if (vdn == vdnSecurity && !options.dpb_gsec_attach && !options.dpb_sec_attach)
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("direct") <<
Arg::Str("security database") <<
Arg::Str(file_name));
}
2006-07-21 03:35:17 +02:00
// Worry about encryption key
2001-05-23 15:26:42 +02:00
if (dbb->dbb_decrypt) {
2008-12-05 02:20:14 +01:00
if (dbb->dbb_filename.hasData() &&
(dbb->dbb_encrypt_key.hasData() || options.dpb_key.hasData()))
{
if ((dbb->dbb_encrypt_key.hasData() && options.dpb_key.isEmpty()) ||
(dbb->dbb_encrypt_key.empty() && options.dpb_key.hasData()) ||
(dbb->dbb_encrypt_key != options.dpb_key))
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("encryption") <<
Arg::Str("database") <<
Arg::Str(file_name));
}
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
else if (options.dpb_key.hasData())
{
dbb->dbb_encrypt_key = options.dpb_key;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
}
attachment = Attachment::create(dbb);
tdbb->setAttachment(attachment);
attachment->att_filename = is_alias ? file_name : expanded_name;
attachment->att_network_protocol = options.dpb_network_protocol;
attachment->att_remote_address = options.dpb_remote_address;
attachment->att_remote_pid = options.dpb_remote_pid;
attachment->att_remote_process = options.dpb_remote_process;
2001-05-23 15:26:42 +02:00
attachment->att_next = dbb->dbb_attachments;
2006-09-15 04:14:46 +02:00
2001-05-23 15:26:42 +02:00
dbb->dbb_attachments = attachment;
dbb->dbb_flags &= ~DBB_being_opened;
dbb->dbb_sys_trans->tra_attachment = attachment;
attachment->att_charset = options.dpb_interp;
if (options.dpb_lc_messages.hasData()) {
attachment->att_lc_messages = options.dpb_lc_messages;
2001-05-23 15:26:42 +02:00
}
if (options.dpb_no_garbage)
attachment->att_flags |= ATT_no_cleanup;
if (options.dpb_gbak_attach)
2001-05-23 15:26:42 +02:00
attachment->att_flags |= ATT_gbak_attachment;
if (options.dpb_gstat_attach)
attachment->att_flags |= ATT_gstat_attachment;
if (options.dpb_gfix_attach)
attachment->att_flags |= ATT_gfix_attachment;
if (options.dpb_working_directory.hasData()) {
attachment->att_working_directory = options.dpb_working_directory;
2001-05-23 15:26:42 +02:00
}
2006-07-21 03:35:17 +02:00
// If we're a not a secondary attachment, initialize some stuff
2001-05-23 15:26:42 +02:00
bool first = false;
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
LCK_init(tdbb, LCK_OWNER_attachment); // For the attachment
2001-05-23 15:26:42 +02:00
attachment->att_flags |= ATT_lck_init_done;
2008-02-10 18:26:35 +01:00
if (dbb->dbb_filename.empty())
2001-12-24 03:51:06 +01:00
{
#if defined(DEV_BUILD) && defined(SUPERSERVER)
// make sure we do not reopen same DB twice
2008-02-10 18:26:35 +01:00
for (Database* d = databases; d; d = d->dbb_next)
{
if (d->dbb_filename == expanded_name)
{
fatal_exception::raise(("Attempt to reopen " + expanded_name).c_str());
}
}
#endif
first = true;
dbb->dbb_filename = expanded_name;
dbb->dbb_flags |= options.dpb_flags;
// NS: Use alias as database ID only if accessing database using file name is not possible.
//
// This way we:
// 1. Ensure uniqueness of ID even in presence of multiple processes
// 2. Make sure that ID value can be used to connect back to database
//
2008-12-05 02:20:14 +01:00
if (is_alias && vdn == vdnFail)
dbb->dbb_database_name = file_name;
else
dbb->dbb_database_name = expanded_name;
2006-07-21 03:35:17 +02:00
// Extra LCK_init() done to keep the lock table until the
// database is shutdown() after the last detach.
2001-05-23 15:26:42 +02:00
LCK_init(tdbb, LCK_OWNER_database);
dbb->dbb_flags |= DBB_lck_init_done;
2006-07-19 08:19:56 +02:00
INI_init(tdbb);
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
pageSpace->file = PIO_open(dbb, expanded_name, file_name, false);
// Initialize locks
init_database_locks(tdbb);
SHUT_init(tdbb);
PAG_header_init(tdbb);
INI_init2(tdbb);
PAG_init(tdbb);
if (options.dpb_set_page_buffers) {
#ifdef SUPERSERVER
// Here we do not let anyone except SYSDBA (like DBO) to change dbb_page_buffers,
// cause other flags is UserId can be set only when DB is opened.
// No idea how to test for other cases before init is complete.
if (userId.locksmith())
#endif
dbb->dbb_page_buffers = options.dpb_page_buffers;
2001-05-23 15:26:42 +02:00
}
CCH_init(tdbb, options.dpb_buffers);
// Initialize backup difference subsystem. This must be done before WAL and shadowing
// is enabled because nbackup it is a lower level subsystem
dbb->dbb_backup_manager = FB_NEW(*dbb->dbb_permanent) BackupManager(tdbb, dbb, nbak_state_unknown);
2007-03-09 09:55:07 +01:00
PAG_init2(tdbb, 0);
PAG_header(tdbb, false);
2006-05-22 00:07:35 +02:00
2006-07-21 03:35:17 +02:00
// initialize shadowing as soon as the database is ready for it
// but before any real work is done
SDW_init(tdbb, options.dpb_activate_shadow, options.dpb_delete_shadow);
2001-05-23 15:26:42 +02:00
}
else
{
2008-02-12 00:32:15 +01:00
if ((dbb->dbb_flags & options.dpb_flags) != options.dpb_flags)
{
// looks like someone tries to attach incompatibly
status_exception::raise(Arg::Gds(isc_bad_dpb_content));
}
}
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Attachments to a ReadOnly database need NOT do garbage collection
2008-02-28 14:48:16 +01:00
if (dbb->dbb_flags & DBB_read_only) {
attachment->att_flags |= ATT_no_cleanup;
2001-12-24 03:51:06 +01:00
}
2001-07-12 07:46:06 +02:00
2008-02-28 14:48:16 +01:00
if (options.dpb_disable_wal) {
ERR_post(Arg::Gds(isc_lock_timeout) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(file_name));
2001-05-23 15:26:42 +02:00
}
if (options.dpb_buffers && !dbb->dbb_page_buffers) {
2006-04-29 07:42:43 +02:00
CCH_expand(tdbb, options.dpb_buffers);
2001-05-23 15:26:42 +02:00
}
if (!options.dpb_verify && CCH_exclusive(tdbb, LCK_PW, LCK_NO_WAIT))
{
TRA_cleanup(tdbb);
}
initing_security = true;
2001-05-23 15:26:42 +02:00
if (invalid_client_SQL_dialect)
{
ERR_post(Arg::Gds(isc_inv_client_dialect_specified) << Arg::Num(options.dpb_sql_dialect) <<
Arg::Gds(isc_valid_client_dialects) << Arg::Str("1, 2 or 3"));
2001-05-23 15:26:42 +02:00
}
if (userId.usr_sql_role_name.hasData())
2001-05-23 15:26:42 +02:00
{
switch (options.dpb_sql_dialect)
{
case 0:
{
2006-07-21 03:35:17 +02:00
// V6 Client --> V6 Server, dummy client SQL dialect 0 was passed
// It means that client SQL dialect was not set by user
// and takes DB SQL dialect as client SQL dialect
2008-11-14 15:15:37 +01:00
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_10_0)
2003-12-03 09:19:24 +01:00
{
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) {
2006-07-21 03:35:17 +02:00
// DB created in IB V6.0 by client SQL dialect 3
2001-05-23 15:26:42 +02:00
options.dpb_sql_dialect = SQL_DIALECT_V6;
2003-12-03 09:19:24 +01:00
}
else {
2006-07-21 03:35:17 +02:00
// old DB was gbaked in IB V6.0
2001-05-23 15:26:42 +02:00
options.dpb_sql_dialect = SQL_DIALECT_V5;
2003-12-03 09:19:24 +01:00
}
}
else {
2001-05-23 15:26:42 +02:00
options.dpb_sql_dialect = SQL_DIALECT_V5;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
}
break;
case 99:
2006-07-21 03:35:17 +02:00
// V5 Client --> V6 Server, old client has no concept of dialect
2001-05-23 15:26:42 +02:00
options.dpb_sql_dialect = SQL_DIALECT_V5;
break;
default:
2006-07-21 03:35:17 +02:00
// V6 Client --> V6 Server, but client SQL dialect was set
// by user and was passed.
2001-05-23 15:26:42 +02:00
break;
}
switch (options.dpb_sql_dialect)
{
case SQL_DIALECT_V5:
{
strip_quotes(userId.usr_sql_role_name);
userId.usr_sql_role_name.upper();
2001-05-23 15:26:42 +02:00
}
break;
case SQL_DIALECT_V6_TRANSITION:
case SQL_DIALECT_V6:
{
2008-12-05 02:20:14 +01:00
if (userId.usr_sql_role_name.hasData() &&
(userId.usr_sql_role_name[0] == DBL_QUOTE ||
userId.usr_sql_role_name[0] == SINGLE_QUOTE))
2001-05-23 15:26:42 +02:00
{
const char end_quote = userId.usr_sql_role_name[0];
2006-07-21 03:35:17 +02:00
// remove the delimited quotes and escape quote
// from ROLE name
userId.usr_sql_role_name.erase(0, 1);
2008-12-05 02:20:14 +01:00
for (string::iterator p = userId.usr_sql_role_name.begin();
p < userId.usr_sql_role_name.end(); ++p)
2001-05-23 15:26:42 +02:00
{
if (*p == end_quote)
2001-05-23 15:26:42 +02:00
{
if (++p < userId.usr_sql_role_name.end() &&
*p == end_quote)
2001-05-23 15:26:42 +02:00
{
// skip the escape quote here
userId.usr_sql_role_name.erase(p--);
2001-05-23 15:26:42 +02:00
}
else
{
// delimited done
2008-12-05 02:20:14 +01:00
userId.usr_sql_role_name.erase(--p,
userId.usr_sql_role_name.end());
2001-05-23 15:26:42 +02:00
}
}
}
}
else
{
userId.usr_sql_role_name.upper();
2001-05-23 15:26:42 +02:00
}
}
break;
default:
break;
}
}
options.dpb_sql_dialect = 0;
SCL_init(tdbb, false, userId);
2001-05-23 15:26:42 +02:00
initing_security = false;
2001-05-23 15:26:42 +02:00
if (options.dpb_shutdown)
2001-12-24 03:51:06 +01:00
{
if (!SHUT_database(tdbb, options.dpb_shutdown,
options.dpb_shutdown_delay))
{
if (user_status[1] != FB_SUCCESS)
ERR_punt();
else
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("shutdown or online") <<
Arg::Str("database") <<
Arg::Str(file_name));
}
}
if (options.dpb_online)
{
2008-12-05 02:20:14 +01:00
if (!SHUT_online(tdbb, options.dpb_online))
{
if (user_status[1] != FB_SUCCESS)
2001-05-23 15:26:42 +02:00
ERR_punt();
else
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("shutdown or online") <<
Arg::Str("database") <<
Arg::Str(file_name));
2001-05-23 15:26:42 +02:00
}
}
#ifdef SUPERSERVER
/* Check if another attachment has or is requesting exclusive database access.
2001-05-23 15:26:42 +02:00
If this is an implicit attachment for the security (password) database, don't
try to get exclusive attachment to avoid a deadlock condition which happens
2001-05-23 15:26:42 +02:00
when a client tries to connect to the security database itself. */
if (!options.dpb_sec_attach) {
bool attachment_succeeded = true;
if (dbb->dbb_ast_flags & DBB_shutdown_single)
attachment_succeeded = CCH_exclusive_attachment(tdbb, LCK_none, -1);
else
CCH_exclusive_attachment(tdbb, LCK_none, LCK_WAIT);
if (attachment->att_flags & ATT_shutdown) {
if (dbb->dbb_ast_flags & DBB_shutdown) {
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(file_name));
}
else {
ERR_post(Arg::Gds(isc_att_shutdown));
}
}
if (!attachment_succeeded) {
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(file_name));
}
2001-05-23 15:26:42 +02:00
}
#endif
2006-07-21 03:35:17 +02:00
// If database is shutdown then kick 'em out.
2001-05-23 15:26:42 +02:00
if (dbb->dbb_ast_flags & (DBB_shut_attach | DBB_shut_tran))
2001-12-24 03:51:06 +01:00
{
ERR_post(Arg::Gds(isc_shutinprog) << Arg::Str(file_name));
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
if (dbb->dbb_ast_flags & DBB_shutdown) {
// Allow only SYSDBA/owner to access database that is shut down
bool allow_access = attachment->locksmith();
// Handle special shutdown modes
if (allow_access) {
if (dbb->dbb_ast_flags & DBB_shutdown_full) {
// Full shutdown. Deny access always
allow_access = false;
}
else if (dbb->dbb_ast_flags & DBB_shutdown_single) {
// Single user maintenance. Allow access only if we were able to take exclusive lock
// Note that logic below this exclusive lock differs for SS and CS builds:
// - CS keeps PW database lock from releasing in AST in single-user maintenance mode
// - for SS this code effectively checks that no other attachments are present
// at call point, ATT_exclusive bit is released just before this procedure exits
// Things are done this way to handle return to online mode nicely.
allow_access = CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD);
}
}
if (!allow_access) {
// Note we throw exception here when entering full-shutdown mode
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(file_name));
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Figure out what character set & collation this attachment prefers
2001-05-23 15:26:42 +02:00
find_intl_charset(tdbb, attachment, &options);
2001-12-24 03:51:06 +01:00
if (options.dpb_verify)
{
if (!CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD)) {
ERR_post(Arg::Gds(isc_bad_dpb_content) << Arg::Gds(isc_cant_validate));
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
#ifdef GARBAGE_THREAD
2006-07-21 03:35:17 +02:00
// Can't allow garbage collection during database validation.
2001-05-23 15:26:42 +02:00
VIO_fini(tdbb);
#endif
if (!VAL_validate(tdbb, options.dpb_verify)) {
ERR_punt();
}
}
if (options.dpb_journal.hasData()) {
ERR_post(Arg::Gds(isc_bad_dpb_content) << Arg::Gds(isc_cant_start_journal));
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (options.dpb_wal_action)
{
2005-05-02 11:10:06 +02:00
// No WAL anymore. We deleted it.
ERR_post(Arg::Gds(isc_no_wal));
2001-05-23 15:26:42 +02:00
}
/*
* if the attachment is through gbak and this attachment is not by owner
* or sysdba then return error. This has been added here to allow for the
2001-05-23 15:26:42 +02:00
* GBAK security feature of only allowing the owner or sysdba to backup a
* database. smistry 10/5/98
*/
if (((attachment->att_flags & ATT_gbak_attachment) ||
(attachment->att_flags & ATT_gfix_attachment) ||
(attachment->att_flags & ATT_gstat_attachment)) &&
!attachment->locksmith())
{
ERR_post(Arg::Gds(isc_adm_task_denied));
2001-05-23 15:26:42 +02:00
}
if (((attachment->att_flags & ATT_gfix_attachment) ||
(attachment->att_flags & ATT_gstat_attachment)))
{
options.dpb_no_db_triggers = true;
}
if (options.dpb_no_db_triggers)
{
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
attachment->att_flags |= ATT_no_db_triggers;
}
if (options.dpb_set_db_sql_dialect) {
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
PAG_set_db_SQL_dialect(tdbb, options.dpb_set_db_sql_dialect);
2001-05-23 15:26:42 +02:00
}
if (options.dpb_sweep_interval != -1) {
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
PAG_sweep_interval(tdbb, options.dpb_sweep_interval);
2001-05-23 15:26:42 +02:00
dbb->dbb_sweep_interval = options.dpb_sweep_interval;
}
if (options.dpb_set_force_write) {
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
PAG_set_force_write(tdbb, options.dpb_force_write);
2001-05-23 15:26:42 +02:00
}
if (options.dpb_set_no_reserve) {
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
PAG_set_no_reserve(tdbb, options.dpb_no_reserve);
2001-05-23 15:26:42 +02:00
}
if (options.dpb_set_page_buffers) {
#ifdef SUPERSERVER
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
#else
if (attachment->locksmith())
#endif
PAG_set_page_buffers(tdbb, options.dpb_page_buffers);
2001-05-23 15:26:42 +02:00
}
if (options.dpb_set_db_readonly) {
2008-07-07 15:33:28 +02:00
validateAccess(attachment);
if (!CCH_exclusive(tdbb, LCK_EX, WAIT_PERIOD)) {
ERR_post(Arg::Gds(isc_lock_timeout) <<
2008-12-05 02:20:14 +01:00
Arg::Gds(isc_obj_in_use) << Arg::Str(file_name));
}
PAG_set_db_readonly(tdbb, options.dpb_db_readonly);
}
2001-07-12 07:46:06 +02:00
2006-07-24 17:56:50 +02:00
PAG_attachment_id(tdbb);
2001-05-23 15:26:42 +02:00
#ifdef GARBAGE_THREAD
VIO_init(tdbb);
#endif
CCH_release_exclusive(tdbb);
2006-07-21 03:35:17 +02:00
// if there was an error, the status vector is all set
2001-05-23 15:26:42 +02:00
guardDatabases.leave();
2003-11-08 17:40:17 +01:00
if (options.dpb_sweep & isc_dpb_records)
2001-12-24 03:51:06 +01:00
{
if (!(TRA_sweep(tdbb, 0))) {
2001-05-23 15:26:42 +02:00
ERR_punt();
}
}
if (options.dpb_dbkey_scope) {
2001-05-23 15:26:42 +02:00
attachment->att_dbkey_trans = TRA_start(tdbb, 0, 0);
}
// Recover database after crash during backup difference file merge
2006-07-21 03:35:17 +02:00
dbb->dbb_backup_manager->end_backup(tdbb, true); // true = do recovery
if (!(attachment->att_flags & ATT_no_db_triggers))
{
jrd_tra* transaction = NULL;
try
{
// start a transaction to execute ON CONNECT triggers
transaction = TRA_start(tdbb, 0, NULL);
// load all database triggers
MET_load_db_triggers(tdbb, DB_TRIGGER_CONNECT);
MET_load_db_triggers(tdbb, DB_TRIGGER_DISCONNECT);
MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_START);
MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_COMMIT);
MET_load_db_triggers(tdbb, DB_TRIGGER_TRANS_ROLLBACK);
// run ON CONNECT triggers
EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_connect);
// and commit the transaction
TRA_commit(tdbb, transaction, false);
}
catch (const Exception&)
{
if (!(dbb->dbb_flags & DBB_bugcheck) && transaction)
TRA_rollback(tdbb, transaction, false, false);
throw;
}
}
// guardDatabases.leave();
2008-12-05 02:20:14 +01:00
*handle = attachment;
attachment->att_mutex.leave();
2001-12-24 03:51:06 +01:00
} // try
catch (const Exception& ex)
2001-12-24 03:51:06 +01:00
{
2008-01-16 10:29:37 +01:00
return unwindAttach(ex, user_status, tdbb, attachment, dbb);
2001-12-24 03:51:06 +01:00
}
siHolder.clear();
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_BLOB_INFO(ISC_STATUS* user_status,
blb** blob_handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ b l o b _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try {
blb* blob = *blob_handle;
validateHandle(tdbb, blob);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
INF_blob_info(blob, items, item_length, buffer, buffer_length);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_CANCEL_BLOB(ISC_STATUS * user_status, blb** blob_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c a n c e l _ b l o b
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try {
blb* blob = *blob_handle;
validateHandle(tdbb, blob);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
BLB_cancel(tdbb, blob);
*blob_handle = NULL;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_CANCEL_EVENTS(ISC_STATUS* user_status,
Attachment** handle,
SLONG* id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c a n c e l _ e v e n t s
*
**************************************
*
* Functional description
* Cancel an outstanding event.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
validateHandle(tdbb, *handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
EVENT_cancel(*id);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS FB_CANCEL_OPERATION(ISC_STATUS* user_status,
Attachment** handle,
USHORT option)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c a n c e l _ o p e r a t i o n
*
**************************************
*
* Functional description
* Try to cancel an operation.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
Attachment* attachment = *handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb, false);
2001-05-23 15:26:42 +02:00
switch (option) {
case fb_cancel_disable:
attachment->att_flags |= ATT_cancel_disable;
attachment->att_flags &= ~ATT_cancel_raise;
2001-05-23 15:26:42 +02:00
break;
case fb_cancel_enable:
if (attachment->att_flags & ATT_cancel_disable)
{
// avoid leaving ATT_cancel_raise set when cleaning ATT_cancel_disable
// to avoid unexpected CANCEL (though it should not be set, but...)
attachment->att_flags &= ~(ATT_cancel_disable | ATT_cancel_raise);
}
break;
2001-05-23 15:26:42 +02:00
case fb_cancel_raise:
if (!(attachment->att_flags & ATT_cancel_disable))
{
attachment->att_flags |= ATT_cancel_raise;
attachment->cancelExternalConnection(tdbb);
}
break;
2001-05-23 15:26:42 +02:00
default:
fb_assert(false);
}
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_CLOSE_BLOB(ISC_STATUS * user_status, blb** blob_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c l o s e _ b l o b
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
blb* blob = *blob_handle;
validateHandle(tdbb, blob);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
BLB_close(tdbb, blob);
*blob_handle = NULL;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_COMMIT(ISC_STATUS * user_status, jrd_tra** tra_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c o m m i t
*
**************************************
*
* Functional description
* Commit a transaction.
*
**************************************/
ThreadContextHolder tdbb(user_status);
try
{
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
JRD_commit_transaction(tdbb, tra_handle);
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_COMMIT_RETAINING(ISC_STATUS * user_status, jrd_tra** tra_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c o m m i t _ r e t a i n i n g
*
**************************************
*
* Functional description
* Commit a transaction.
*
**************************************/
ThreadContextHolder tdbb(user_status);
try
{
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
JRD_commit_retaining(tdbb, tra_handle);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_COMPILE(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_req** req_handle,
SSHORT blr_length,
const SCHAR* blr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c o m p i l e
*
**************************************
*
* Functional description
*
**************************************/
ThreadContextHolder tdbb(user_status);
try
{
Attachment* attachment = *db_handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
JRD_compile(tdbb, attachment, req_handle,
blr_length, reinterpret_cast<const UCHAR*>(blr),
0, NULL, 0, NULL);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_CREATE_BLOB2(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
blb** blob_handle,
bid* blob_id,
USHORT bpb_length,
const UCHAR* bpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c r e a t e _ b l o b
*
**************************************
*
* Functional description
* Create a new blob.
2001-05-23 15:26:42 +02:00
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
if (*blob_handle)
status_exception::raise(Arg::Gds(isc_bad_segstr_handle));
validateHandle(tdbb, *db_handle);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
jrd_tra* transaction = find_transaction(tdbb, isc_segstr_wrong_db);
blb* blob = BLB_create2(tdbb, transaction, blob_id, bpb_length, bpb);
*blob_handle = blob;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_CREATE_DATABASE(ISC_STATUS* user_status,
const TEXT* filename,
Attachment** handle,
USHORT dpb_length,
const UCHAR* dpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ c r e a t e _ d a t a b a s e
*
**************************************
*
* Functional description
* Create a nice, squeeky clean database, uncorrupted by user data.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
if (*handle)
{
2008-01-16 10:29:37 +01:00
return handle_error(user_status, isc_bad_db_handle);
}
2001-05-23 15:26:42 +02:00
UserId userId;
DatabaseOptions options;
SecurityDatabase::InitHolder siHolder;
try {
// Process database parameter block
bool invalid_client_SQL_dialect = false;
options.get(dpb, dpb_length, invalid_client_SQL_dialect);
if (!invalid_client_SQL_dialect && options.dpb_sql_dialect == 99) {
options.dpb_sql_dialect = 0;
}
2008-02-10 18:26:35 +01:00
// Check for correct credentials supplied
getUserInfo(userId, options);
}
catch (const DelayFailedLogin& ex)
{
ex.sleep();
return ex.stuff_exception(user_status);
}
catch (const Exception& e)
{
e.stuff_exception(user_status);
return user_status[1];
}
PathName file_name = options.dpb_org_filename.hasData() ? options.dpb_org_filename : filename;
if (options.dpb_utf8_filename)
ISC_utf8ToSystem(file_name);
else
{
ISC_systemToUtf8(file_name);
ISC_unescape(file_name);
ISC_utf8ToSystem(file_name);
}
PathName expanded_name;
// Resolve given alias name
const bool is_alias = ResolveDatabaseAlias(file_name, expanded_name);
if (is_alias)
{
ISC_systemToUtf8(expanded_name);
ISC_unescape(expanded_name);
ISC_utf8ToSystem(expanded_name);
ISC_expand_filename(expanded_name, false);
}
else
{
expanded_name = filename;
if (!options.dpb_utf8_filename)
ISC_systemToUtf8(expanded_name);
ISC_unescape(expanded_name);
ISC_utf8ToSystem(expanded_name);
}
2006-07-21 03:35:17 +02:00
// Check database against conf file.
const vdnResult vdn = verify_database_name(expanded_name, user_status);
if (!is_alias && vdn == vdnFail)
{
return user_status[1];
2008-12-05 02:20:14 +01:00
}
Database* dbb = NULL;
MutexEnsureUnlock guardDatabases(databases_mutex);
guardDatabases.enter();
try
{
dbb = init(tdbb, expanded_name, false);
}
catch (const Exception& ex)
{
ex.stuff_exception(user_status);
2001-05-23 15:26:42 +02:00
return user_status[1];
}
fb_assert(dbb);
tdbb->setDatabase(dbb);
DatabaseContextHolder dbbHolder(tdbb);
dbb->dbb_flags |= (DBB_being_opened | options.dpb_flags);
2001-05-23 15:26:42 +02:00
2008-01-16 10:29:37 +01:00
ISC_STATUS* const status = user_status;
Attachment* attachment = NULL;
bool initing_security = false;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
#ifndef NO_NFS
2006-07-21 03:35:17 +02:00
// Don't check nfs if single user
2001-05-23 15:26:42 +02:00
if (!options.dpb_single_user)
#endif
{
2006-07-21 03:35:17 +02:00
// Check to see if the database is truly local or if it just looks
// that way
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
if (ISC_check_if_remote(expanded_name, true))
{
ERR_post(Arg::Gds(isc_unavailable));
}
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
if (options.dpb_key.hasData())
{
dbb->dbb_encrypt_key = options.dpb_key;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
attachment = Attachment::create(dbb);
tdbb->setAttachment(attachment);
attachment->att_filename = is_alias ? file_name : expanded_name;
attachment->att_network_protocol = options.dpb_network_protocol;
attachment->att_remote_address = options.dpb_remote_address;
attachment->att_remote_pid = options.dpb_remote_pid;
attachment->att_remote_process = options.dpb_remote_process;
2001-05-23 15:26:42 +02:00
attachment->att_next = dbb->dbb_attachments;
2006-09-15 04:14:46 +02:00
2001-05-23 15:26:42 +02:00
dbb->dbb_attachments = attachment;
dbb->dbb_flags &= ~DBB_being_opened;
dbb->dbb_sys_trans->tra_attachment = attachment;
if (options.dpb_working_directory.hasData()) {
attachment->att_working_directory = options.dpb_working_directory;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
if (options.dpb_gbak_attach) {
2001-05-23 15:26:42 +02:00
attachment->att_flags |= ATT_gbak_attachment;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
if (options.dpb_no_db_triggers)
attachment->att_flags |= ATT_no_db_triggers;
2001-05-23 15:26:42 +02:00
switch (options.dpb_sql_dialect) {
case 0:
2008-12-05 02:20:14 +01:00
// This can be issued by QLI, GDEF and old BDE clients.
// In this case assume dialect 1
2001-05-23 15:26:42 +02:00
options.dpb_sql_dialect = SQL_DIALECT_V5;
case SQL_DIALECT_V5:
break;
case SQL_DIALECT_V6:
dbb->dbb_flags |= DBB_DB_SQL_dialect_3;
break;
default:
ERR_post(Arg::Gds(isc_database_create_failed) << Arg::Str(expanded_name) <<
Arg::Gds(isc_inv_dialect_specified) << Arg::Num(options.dpb_sql_dialect) <<
Arg::Gds(isc_valid_db_dialects) << Arg::Str("1 and 3"));
2001-05-23 15:26:42 +02:00
break;
}
attachment->att_charset = options.dpb_interp;
if (options.dpb_lc_messages.hasData()) {
attachment->att_lc_messages = options.dpb_lc_messages;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
2003-12-03 09:19:24 +01:00
if (!options.dpb_page_size) {
2001-05-23 15:26:42 +02:00
options.dpb_page_size = DEFAULT_PAGE_SIZE;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
USHORT page_size = MIN_NEW_PAGE_SIZE;
for (; page_size < MAX_PAGE_SIZE; page_size <<= 1)
2003-12-03 09:19:24 +01:00
{
2003-09-13 14:03:11 +02:00
if (options.dpb_page_size < page_size << 1)
2001-05-23 15:26:42 +02:00
break;
2003-12-03 09:19:24 +01:00
}
2001-05-23 15:26:42 +02:00
dbb->dbb_page_size =
(page_size > MAX_PAGE_SIZE) ? MAX_PAGE_SIZE : page_size;
2006-07-21 03:35:17 +02:00
LCK_init(tdbb, LCK_OWNER_attachment); // For the attachment
2001-05-23 15:26:42 +02:00
attachment->att_flags |= ATT_lck_init_done;
2006-07-21 03:35:17 +02:00
// Extra LCK_init() done to keep the lock table until the
// database is shutdown() after the last detach.
2001-05-23 15:26:42 +02:00
LCK_init(tdbb, LCK_OWNER_database);
dbb->dbb_flags |= DBB_lck_init_done;
2006-07-19 08:19:56 +02:00
INI_init(tdbb);
PAG_init(tdbb);
initing_security = true;
2001-05-23 15:26:42 +02:00
SCL_init(tdbb, true, userId);
2001-05-23 15:26:42 +02:00
initing_security = false;
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
try
{
// try to create with overwrite = false
2007-03-09 08:59:05 +01:00
pageSpace->file = PIO_create(dbb, expanded_name, false, false, false);
}
catch (status_exception)
{
if (options.dpb_overwrite)
{
if (GDS_ATTACH_DATABASE(user_status, filename, handle, dpb_length, dpb) == isc_adm_task_denied)
{
throw;
}
bool allow_overwrite = false;
if (*handle)
{
allow_overwrite = (*handle)->att_user->locksmith();
GDS_DETACH(user_status, handle);
}
else
{
// clear status after failed attach
fb_utils::init_status(user_status);
allow_overwrite = true;
}
if (allow_overwrite)
{
// file is a database and the user (SYSDBA or owner) has right to overwrite
pageSpace->file = PIO_create(dbb, expanded_name, options.dpb_overwrite, false, false);
}
else
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("overwrite") <<
Arg::Str("database") <<
Arg::Str(expanded_name));
}
}
else
throw;
}
const jrd_file* const first_dbb_file = pageSpace->file;
// Initialize locks
init_database_locks(tdbb);
2006-05-22 00:07:35 +02:00
2001-05-23 15:26:42 +02:00
if (options.dpb_set_page_buffers)
dbb->dbb_page_buffers = options.dpb_page_buffers;
CCH_init(tdbb, options.dpb_buffers);
// Initialize backup difference subsystem. This must be done before WAL and shadowing
// is enabled because nbackup it is a lower level subsystem
2008-12-05 02:20:14 +01:00
dbb->dbb_backup_manager = FB_NEW(*dbb->dbb_permanent) BackupManager(tdbb, dbb, nbak_state_normal);
dbb->dbb_backup_manager->dbCreating = true;
PAG_format_header(tdbb);
INI_init2(tdbb);
PAG_format_log(tdbb);
2006-05-22 00:07:35 +02:00
PAG_format_pip(tdbb, *pageSpace);
2001-05-23 15:26:42 +02:00
if (options.dpb_set_page_buffers)
PAG_set_page_buffers(tdbb, options.dpb_page_buffers);
2001-05-23 15:26:42 +02:00
if (options.dpb_set_no_reserve)
PAG_set_no_reserve(tdbb, options.dpb_no_reserve);
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
INI_format(attachment->att_user->usr_user_name.c_str(),
options.dpb_set_db_charset.c_str());
2001-05-23 15:26:42 +02:00
// There is no point to move database online at database creation since it is online by default.
// We do not allow to create database that is fully shut down.
if (options.dpb_online || (options.dpb_shutdown & isc_dpb_shut_mode_mask) == isc_dpb_shut_full)
ERR_post(Arg::Gds(isc_bad_shutdown_mode) << Arg::Str(file_name));
2008-12-05 02:20:14 +01:00
if (options.dpb_shutdown) {
if (!SHUT_database(tdbb, options.dpb_shutdown,
options.dpb_shutdown_delay))
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("shutdown or online") <<
Arg::Str("database") <<
Arg::Str(file_name));
2001-05-23 15:26:42 +02:00
}
}
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
if (options.dpb_sweep_interval != -1) {
PAG_sweep_interval(tdbb, options.dpb_sweep_interval);
2001-05-23 15:26:42 +02:00
dbb->dbb_sweep_interval = options.dpb_sweep_interval;
}
if (options.dpb_set_force_write)
PAG_set_force_write(tdbb, options.dpb_force_write);
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// initialize shadowing semaphore as soon as the database is ready for it
// but before any real work is done
2001-05-23 15:26:42 +02:00
SDW_init(tdbb, options.dpb_activate_shadow, options.dpb_delete_shadow);
2001-05-23 15:26:42 +02:00
#ifdef GARBAGE_THREAD
VIO_init(tdbb);
#endif
if (options.dpb_set_db_readonly) {
if (!CCH_exclusive (tdbb, LCK_EX, WAIT_PERIOD))
ERR_post(Arg::Gds(isc_lock_timeout) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(file_name));
2008-12-05 02:20:14 +01:00
PAG_set_db_readonly(tdbb, options.dpb_db_readonly);
}
2006-07-24 17:56:50 +02:00
PAG_attachment_id(tdbb);
CCH_release_exclusive(tdbb);
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Figure out what character set & collation this attachment prefers
2001-05-23 15:26:42 +02:00
find_intl_charset(tdbb, attachment, &options);
2002-07-02 11:49:19 +02:00
#ifdef WIN_NT
dbb->dbb_filename.assign(first_dbb_file->fil_string);
2002-07-02 11:49:19 +02:00
#else
dbb->dbb_filename = expanded_name;
2002-07-02 11:49:19 +02:00
#endif
2001-05-23 15:26:42 +02:00
// NS: Use alias as database ID only if accessing database using file name is not possible.
//
// This way we:
// 1. Ensure uniqueness of ID even in presence of multiple processes
// 2. Make sure that ID value can be used to connect back to database
//
if (is_alias && vdn == vdnFail)
dbb->dbb_database_name = file_name;
else
dbb->dbb_database_name = dbb->dbb_filename;
CCH_flush(tdbb, FLUSH_FINI, 0);
2001-05-23 15:26:42 +02:00
dbb->dbb_backup_manager->dbCreating = false;
guardDatabases.leave();
*handle = attachment;
attachment->att_mutex.leave();
2001-12-24 03:51:06 +01:00
} // try
catch (const Exception& ex)
2001-12-24 03:51:06 +01:00
{
2008-01-16 10:29:37 +01:00
return unwindAttach(ex, user_status, tdbb, attachment, dbb);
2001-12-24 03:51:06 +01:00
}
siHolder.clear();
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_DATABASE_INFO(ISC_STATUS* user_status,
Attachment** handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ d a t a b a s e _ i n f o
*
**************************************
*
* Functional description
* Provide information on database object.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
Attachment* attachment = *handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
INF_database_info(items, item_length, buffer, buffer_length);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_DDL(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
USHORT ddl_length,
const SCHAR* ddl)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ d d l
*
**************************************
*
* Functional description
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
try {
Attachment* attachment = *db_handle;
validateHandle(tdbb, attachment);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
2001-12-24 03:51:06 +01:00
jrd_tra* transaction = find_transaction(tdbb, isc_segstr_wrong_db);
2001-12-24 03:51:06 +01:00
JRD_ddl(tdbb, attachment, transaction, ddl_length, reinterpret_cast<const UCHAR*>(ddl));
}
catch (const Exception& ex) {
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_DETACH(ISC_STATUS* user_status, Attachment** handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ d e t a c h
*
**************************************
*
* Functional description
* Close down a database.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
2008-01-16 10:29:37 +01:00
{
{ // scope
MutexLockGuard guard(databases_mutex);
2001-05-23 15:26:42 +02:00
Attachment* attachment = *handle;
validateHandle(tdbb, attachment);
2008-02-10 18:26:35 +01:00
{ // holder scope
DatabaseContextHolder dbbHolder(tdbb);
2001-05-23 15:26:42 +02:00
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
// if this is the last attachment, mark dbb as not in use
if (dbb->dbb_attachments == attachment &&
!attachment->att_next &&
!(dbb->dbb_flags & DBB_being_opened))
{
dbb->dbb_flags |= DBB_not_in_use;
}
2001-05-23 15:26:42 +02:00
try
{
// Purge attachment, don't rollback open transactions
attachment->att_flags |= ATT_cancel_disable;
purge_attachment(tdbb, user_status, attachment, false);
}
catch (const Exception&)
{
dbb->dbb_flags &= ~DBB_not_in_use;
throw;
}
}
}
2001-12-24 03:51:06 +01:00
*handle = NULL;
SecurityDatabase::shutdown();
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-12-24 03:51:06 +01:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_DROP_DATABASE(ISC_STATUS* user_status, Attachment** handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i s c _ d r o p _ d a t a b a s e
*
**************************************
*
* Functional description
* Close down and purge a database.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
MutexLockGuard guard(databases_mutex);
2001-05-23 15:26:42 +02:00
Attachment* attachment = *handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
2001-05-23 15:26:42 +02:00
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
const PathName& file_name = attachment->att_filename;
if (!attachment->locksmith())
2008-01-29 02:36:41 +01:00
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("drop") <<
Arg::Str("database") <<
Arg::Str(file_name));
2008-01-29 02:36:41 +01:00
}
2003-01-18 17:31:23 +01:00
2008-01-29 02:36:41 +01:00
if (attachment->att_flags & ATT_shutdown)
{
if (dbb->dbb_ast_flags & DBB_shutdown)
{
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(file_name));
}
else
{
ERR_post(Arg::Gds(isc_att_shutdown));
}
}
2001-05-23 15:26:42 +02:00
if (!CCH_exclusive(tdbb, LCK_PW, WAIT_PERIOD))
2008-01-29 02:36:41 +01:00
{
ERR_post(Arg::Gds(isc_lock_timeout) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(file_name));
2008-01-29 02:36:41 +01:00
}
// Check if same process has more attachments
2001-05-23 15:26:42 +02:00
if (dbb->dbb_attachments && dbb->dbb_attachments->att_next) {
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str("DATABASE"));
}
2001-05-23 15:26:42 +02:00
// Forced release of all transactions
purge_transactions(tdbb, attachment, true, attachment->att_flags);
attachment->att_flags |= ATT_cancel_disable;
2001-05-23 15:26:42 +02:00
// Here we have database locked in exclusive mode.
// Just mark the header page with an 0 ods version so that no other
// process can attach to this database once we release our exclusive
// lock and start dropping files.
2001-05-23 15:26:42 +02:00
WIN window(HEADER_PAGE_NUMBER);
Ods::header_page* header = (Ods::header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
header->hdr_ods_version = 0;
CCH_RELEASE(tdbb, &window);
2001-12-24 03:51:06 +01:00
2006-07-21 03:35:17 +02:00
// This point on database is useless
// mark the dbb unusable
2001-05-23 15:26:42 +02:00
dbb->dbb_flags |= DBB_not_in_use;
*handle = NULL;
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
const jrd_file* file = pageSpace->file;
const Shadow* shadow = dbb->dbb_shadow;
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Unlink attachment from database
release_attachment(tdbb, attachment); // normal release, no need to process status vector
2001-05-23 15:26:42 +02:00
shutdown_database(dbb, false);
2001-05-23 15:26:42 +02:00
// drop the files here
bool err = drop_files(file);
2008-12-05 02:20:14 +01:00
for (; shadow; shadow = shadow->sdw_next)
{
err = err || drop_files(shadow->sdw_file);
}
2001-05-23 15:26:42 +02:00
tdbb->setDatabase(NULL);
Database::destroy(dbb);
2001-05-23 15:26:42 +02:00
if (err) {
ERR_build_status(user_status, Arg::Gds(isc_drdb_completed_with_errs));
}
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-12-24 03:51:06 +01:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
2008-08-31 03:10:41 +02:00
ISC_STATUS GDS_GET_SEGMENT(ISC_STATUS* user_status,
blb** blob_handle,
2008-08-31 03:10:41 +02:00
USHORT* length,
USHORT buffer_length,
2008-08-31 03:10:41 +02:00
UCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ g e t _ s e g m e n t
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
blb* blob = *blob_handle;
validateHandle(tdbb, blob);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
*length = BLB_get_segment(tdbb, blob, buffer, buffer_length);
user_status[0] = isc_arg_gds;
if (blob->blb_flags & BLB_eof) {
user_status[1] = isc_segstr_eof;
}
else if (blob->blb_fragment_size) {
user_status[1] = isc_segment;
}
user_status[2] = isc_arg_end;
2001-05-23 15:26:42 +02:00
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
2003-12-03 09:19:24 +01:00
ISC_STATUS GDS_GET_SLICE(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
ISC_QUAD* array_id,
USHORT sdl_length,
2003-12-03 09:19:24 +01:00
const UCHAR* sdl,
USHORT param_length,
2003-12-03 09:19:24 +01:00
const UCHAR* param,
SLONG slice_length,
2003-12-03 09:19:24 +01:00
UCHAR* slice,
SLONG* return_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ g e t _ s l i c e
*
**************************************
*
* Functional description
* Snatch a slice of an array.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
validateHandle(tdbb, *db_handle);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
jrd_tra* transaction = find_transaction(tdbb, isc_segstr_wrong_db);
2008-12-05 02:20:14 +01:00
if (!array_id->gds_quad_low && !array_id->gds_quad_high) {
MOVE_CLEAR(slice, slice_length);
*return_length = 0;
}
2007-11-01 18:19:54 +01:00
else {
*return_length = BLB_get_slice(tdbb,
2001-05-23 15:26:42 +02:00
transaction,
reinterpret_cast<bid*>(array_id),
2001-05-23 15:26:42 +02:00
sdl,
param_length,
2007-11-01 18:19:54 +01:00
param,
2001-05-23 15:26:42 +02:00
slice_length, slice);
2007-11-01 18:19:54 +01:00
}
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_OPEN_BLOB2(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
blb** blob_handle,
bid* blob_id,
USHORT bpb_length,
const UCHAR* bpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ o p e n _ b l o b 2
*
**************************************
*
* Functional description
* Open an existing blob.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
if (*blob_handle)
status_exception::raise(Arg::Gds(isc_bad_segstr_handle));
validateHandle(tdbb, *db_handle);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
jrd_tra* transaction = find_transaction(tdbb, isc_segstr_wrong_db);
blb* blob = BLB_open2(tdbb, transaction, blob_id, bpb_length, bpb, true);
*blob_handle = blob;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_PREPARE(ISC_STATUS * user_status,
jrd_tra** tra_handle,
USHORT length,
const UCHAR* msg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ p r e p a r e
*
**************************************
*
* Functional description
* Prepare a transaction for commit. First phase of a two
* phase commit.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_tra* transaction = *tra_handle;
validateHandle(tdbb, transaction);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
prepare(tdbb, transaction, length, msg);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_PUT_SEGMENT(ISC_STATUS* user_status,
blb** blob_handle,
USHORT buffer_length,
const UCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ p u t _ s e g m e n t
*
**************************************
*
* Functional description
* Abort a partially completed blob.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
blb* blob = *blob_handle;
validateHandle(tdbb, blob);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
BLB_put_segment(tdbb, blob, buffer, buffer_length);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
2003-12-03 09:19:24 +01:00
ISC_STATUS GDS_PUT_SLICE(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
ISC_QUAD* array_id,
USHORT sdl_length,
2003-12-03 09:19:24 +01:00
const UCHAR* sdl,
USHORT param_length,
2003-12-03 09:19:24 +01:00
const UCHAR* param,
SLONG slice_length,
2003-12-03 09:19:24 +01:00
UCHAR* slice)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ p u t _ s l i c e
*
**************************************
*
* Functional description
* Snatch a slice of an array.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
validateHandle(tdbb, *db_handle);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
jrd_tra* transaction = find_transaction(tdbb, isc_segstr_wrong_db);
BLB_put_slice(tdbb,
2001-05-23 15:26:42 +02:00
transaction,
reinterpret_cast<bid*>(array_id),
2001-05-23 15:26:42 +02:00
sdl,
param_length,
reinterpret_cast<const SLONG*>(param), slice_length, slice);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_QUE_EVENTS(ISC_STATUS* user_status,
Attachment** handle,
SLONG* id,
SSHORT length,
const UCHAR* items,
2003-12-22 11:00:59 +01:00
FPTR_EVENT_CALLBACK ast,
void* arg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ q u e _ e v e n t s
*
**************************************
*
* Functional description
* Que a request for event notification.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
Attachment* attachment = *handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
Database* dbb = tdbb->getDatabase();
Lock* lock = dbb->dbb_lock;
if (!attachment->att_event_session &&
!(attachment->att_event_session = EVENT_create_session(user_status)))
{
return user_status[1];
}
2008-12-05 02:20:14 +01:00
*id = EVENT_que(user_status,
attachment->att_event_session,
lock->lck_length,
(const TEXT*) &lock->lck_key, length, items, ast, arg);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_RECEIVE(ISC_STATUS * user_status,
jrd_req** req_handle,
USHORT msg_type,
USHORT msg_length,
SCHAR * msg,
SSHORT level
2001-05-23 15:26:42 +02:00
#ifdef SCROLLABLE_CURSORS
, USHORT direction,
ULONG offset
2001-05-23 15:26:42 +02:00
#endif
)
{
/**************************************
*
* g d s _ $ r e c e i v e
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
check_transaction(tdbb, request->req_transaction);
JRD_receive(tdbb, request, msg_type, msg_length,
reinterpret_cast<UCHAR*>(msg), level
2006-01-28 05:12:42 +01:00
#ifdef SCROLLABLE_CURSORS
, direction, offset
2006-01-28 05:12:42 +01:00
#endif
);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_RECONNECT(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
SSHORT length,
const UCHAR* id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ r e c o n n e c t
*
**************************************
*
* Functional description
* Connect to a transaction in limbo.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
if (*tra_handle)
status_exception::raise(Arg::Gds(isc_bad_trans_handle));
Attachment* attachment = *db_handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
jrd_tra* transaction = TRA_reconnect(tdbb, id, length);
*tra_handle = transaction;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_RELEASE_REQUEST(ISC_STATUS * user_status, jrd_req** req_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ r e l e a s e _ r e q u e s t
*
**************************************
*
* Functional description
* Release a request.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
CMP_release(tdbb, request);
*req_handle = NULL;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_REQUEST_INFO(ISC_STATUS* user_status,
jrd_req** req_handle,
SSHORT level,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ r e q u e s t _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
JRD_request_info(tdbb, request, level, item_length, items, buffer_length, buffer);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_ROLLBACK_RETAINING(ISC_STATUS * user_status,
jrd_tra** tra_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i s c _ r o l l b a c k _ r e t a i n i n g
*
**************************************
*
* Functional description
* Abort a transaction but keep the environment valid
*
**************************************/
ThreadContextHolder tdbb(user_status);
try
{
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
JRD_rollback_retaining(tdbb, tra_handle);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_ROLLBACK(ISC_STATUS * user_status, jrd_tra** tra_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ r o l l b a c k
*
**************************************
*
* Functional description
* Abort a transaction.
*
**************************************/
ThreadContextHolder tdbb(user_status);
try
{
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
JRD_rollback_transaction(tdbb, tra_handle);
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_SEEK_BLOB(ISC_STATUS * user_status,
blb** blob_handle,
SSHORT mode,
SLONG offset,
SLONG * result)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s e e k _ b l o b
*
**************************************
*
* Functional description
* Seek a stream blob.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
blb* blob = *blob_handle;
validateHandle(tdbb, blob);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
*result = BLB_lseek(blob, mode, offset);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_SEND(ISC_STATUS * user_status,
jrd_req** req_handle,
USHORT msg_type,
USHORT msg_length,
SCHAR * msg,
SSHORT level)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s e n d
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
check_transaction(tdbb, request->req_transaction);
verify_request_synchronization(request, level);
2008-12-05 02:20:14 +01:00
EXE_send(tdbb, request, msg_type, msg_length,
2003-10-07 12:43:20 +02:00
reinterpret_cast<UCHAR*>(msg));
2008-12-05 02:20:14 +01:00
check_autocommit(request, tdbb);
2008-12-05 02:20:14 +01:00
if (request->req_flags & req_warning)
request->req_flags &= ~req_warning;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
2003-12-03 09:19:24 +01:00
ISC_STATUS GDS_SERVICE_ATTACH(ISC_STATUS* user_status,
const TEXT* service_name,
Service** svc_handle,
USHORT spb_length,
const SCHAR* spb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s e r v i c e _ a t t a c h
*
**************************************
*
* Functional description
* Connect to a Firebird service.
2001-05-23 15:26:42 +02:00
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
if (*svc_handle)
status_exception::raise(Arg::Gds(isc_bad_svc_handle));
2001-05-23 15:26:42 +02:00
*svc_handle = new Service(service_name, spb_length, reinterpret_cast<const UCHAR*>(spb));
}
catch (const DelayFailedLogin& ex)
2008-01-16 10:29:37 +01:00
{
ex.sleep();
stuff_exception(user_status, ex);
2008-01-16 10:29:37 +01:00
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_SERVICE_DETACH(ISC_STATUS* user_status, Service** svc_handle)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s e r v i c e _ d e t a c h
*
**************************************
*
* Functional description
* Close down a service.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
Service* service = *svc_handle;
validateHandle(service);
service->detach();
*svc_handle = NULL;
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_SERVICE_QUERY(ISC_STATUS* user_status,
Service** svc_handle,
ULONG* reserved,
USHORT send_item_length,
const SCHAR* send_items,
USHORT recv_item_length,
const SCHAR* recv_items,
USHORT buffer_length,
SCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s e r v i c e _ q u e r y
*
**************************************
*
* Functional description
* Provide information on service object.
*
* NOTE: The parameter RESERVED must not be used
* for any purpose as there are networking issues
* involved (as with any handle that goes over the
* network). This parameter will be implemented at
2001-05-23 15:26:42 +02:00
* a later date.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
Service* service = *svc_handle;
validateHandle(service);
if (service->getVersion() == isc_spb_version1) {
service->query(send_item_length, send_items, recv_item_length,
recv_items, buffer_length, buffer);
}
else {
2006-07-21 03:35:17 +02:00
// For SVC_query2, we are going to completly dismantle user_status (since at this point it is
// meaningless anyway). The status vector returned by this function can hold information about
// the call to query the service manager and/or a service thread that may have been running.
service->query2(tdbb, send_item_length, send_items,
recv_item_length, recv_items, buffer_length, buffer);
2008-12-05 02:20:14 +01:00
// If there is a status vector from a service thread, copy it into the thread status
int len, warning;
PARSE_STATUS(service->getStatus(), len, warning);
if (len) {
memcpy(tdbb->tdbb_status_vector, service->getStatus(), sizeof(ISC_STATUS) * len);
2006-07-21 03:35:17 +02:00
// Empty out the service status vector
memset(service->getStatus(), 0, sizeof(ISC_STATUS_ARRAY));
}
2001-05-23 15:26:42 +02:00
}
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_SERVICE_START(ISC_STATUS* user_status,
Service** svc_handle,
ULONG* reserved,
USHORT spb_length,
const SCHAR* spb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ s e r v i c e _ s t a r t
*
**************************************
*
* Functional description
* Start the specified service
*
* NOTE: The parameter RESERVED must not be used
* for any purpose as there are networking issues
* involved (as with any handle that goes over the
* network). This parameter will be implemented at
2001-05-23 15:26:42 +02:00
* a later date.
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
Service* service = *svc_handle;
validateHandle(service);
service->start(spb_length, reinterpret_cast<const UCHAR*>(spb));
2008-12-05 02:20:14 +01:00
if (service->getStatus()[1]) {
memcpy(tdbb->tdbb_status_vector, service->getStatus(), sizeof(ISC_STATUS_ARRAY));
}
2001-05-23 15:26:42 +02:00
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
2003-12-03 09:19:24 +01:00
ISC_STATUS GDS_START_AND_SEND(ISC_STATUS* user_status,
jrd_req** req_handle,
jrd_tra** tra_handle,
USHORT msg_type,
USHORT msg_length,
2003-12-03 09:19:24 +01:00
SCHAR* msg,
SSHORT level)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s t a r t _ a n d _ s e n d
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
check_transaction(tdbb, request->req_transaction);
jrd_tra* transaction = find_transaction(tdbb, isc_req_wrong_db);
2008-12-05 02:20:14 +01:00
JRD_start_and_send(tdbb, request, transaction, msg_type, msg_length, msg, level);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_START(ISC_STATUS * user_status,
jrd_req** req_handle,
jrd_tra** tra_handle,
SSHORT level)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s t a r t
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
check_transaction(tdbb, request->req_transaction);
jrd_tra* transaction = find_transaction(tdbb, isc_req_wrong_db);
JRD_start(tdbb, request, transaction, level);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
int GDS_SHUTDOWN(unsigned int timeout)
{
/**************************************
*
* G D S _ S H U T D O W N
*
**************************************
*
* Functional description
* Rollback every transaction, release
* every attachment, and shutdown every
* database.
*
**************************************/
ISC_STATUS_ARRAY status;
ThreadContextHolder tdbb(status);
try
{
ULONG attach_count, database_count;
JRD_num_attachments(NULL, 0, JRD_info_none, &attach_count, &database_count);
if (attach_count > 0)
{
gds__log("Shutting down the server with %d active connection(s) to %d database(s)",
attach_count, database_count);
}
if (timeout)
{
Semaphore shutdown_semaphore;
2008-06-06 17:55:36 +02:00
ThreadStart::start(shutdown_thread, &shutdown_semaphore, THREAD_medium, 0);
if (!shutdown_semaphore.tryEnter(0, timeout))
{
status_exception::raise(Arg::Gds(isc_shutdown_timeout));
}
}
else
{
shutdown_thread(NULL);
}
}
catch (const Exception& ex)
{
stuff_exception(status, ex);
gds__log_status(0, status);
}
return 0;
}
ISC_STATUS GDS_START_MULTIPLE(ISC_STATUS * user_status,
jrd_tra** tra_handle,
USHORT count,
TEB * vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s t a r t _ m u l t i p l e
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
JRD_start_multiple(tdbb, tra_handle, count, vector);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_START_TRANSACTION(ISC_STATUS * user_status,
jrd_tra** tra_handle,
SSHORT count,
...)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ s t a r t _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
ThreadContextHolder tdbb(user_status);
try
{
if (count < 1 || USHORT(count) > MAX_DB_PER_TRANS)
{
status_exception::raise(Arg::Gds(isc_max_db_per_trans_allowed) <<
Arg::Num(MAX_DB_PER_TRANS));
}
HalfStaticArray<TEB, 16> tebs;
tebs.grow(count);
va_list ptr;
va_start(ptr, count);
for (TEB* teb_iter = tebs.begin(); teb_iter < tebs.end(); teb_iter++) {
teb_iter->teb_database = va_arg(ptr, Attachment**);
teb_iter->teb_tpb_length = va_arg(ptr, int);
teb_iter->teb_tpb = va_arg(ptr, UCHAR*);
}
va_end(ptr);
2001-05-23 15:26:42 +02:00
JRD_start_multiple(tdbb, tra_handle, count, tebs.begin());
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_TRANSACT_REQUEST(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
USHORT blr_length,
2003-12-03 09:19:24 +01:00
const SCHAR* blr,
USHORT in_msg_length,
2008-03-30 11:42:24 +02:00
const SCHAR* in_msg,
USHORT out_msg_length,
SCHAR* out_msg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i s c _ t r a n s a c t _ r e q u e s t
*
**************************************
*
* Functional description
* Execute a procedure.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
try
{
Attachment* attachment = *db_handle;
validateHandle(tdbb, attachment);
validateHandle(tdbb, *tra_handle);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
2001-05-23 15:26:42 +02:00
2008-01-29 11:11:52 +01:00
Database* dbb = tdbb->getDatabase();
jrd_tra* transaction = find_transaction(tdbb, isc_req_wrong_db);
2001-05-23 15:26:42 +02:00
jrd_nod* in_message = NULL;
jrd_nod* out_message = NULL;
2008-01-29 11:11:52 +01:00
jrd_req* request = NULL;
MemoryPool* new_pool = dbb->createPool();
try
{
Jrd::ContextPoolHolder context(tdbb, new_pool);
2001-05-23 15:26:42 +02:00
CompilerScratch* csb = PAR_parse(tdbb, reinterpret_cast<const UCHAR*>(blr), FALSE);
request = CMP_make_request(tdbb, csb, false);
CMP_verify_access(tdbb, request);
2001-05-23 15:26:42 +02:00
jrd_nod* node;
for (size_t i = 0; i < csb->csb_rpt.getCount(); i++)
{
if ( (node = csb->csb_rpt[i].csb_message) )
{
2008-12-05 02:20:14 +01:00
if ((int) (IPTR) node->nod_arg[e_msg_number] == 0)
{
in_message = node;
}
2008-12-05 02:20:14 +01:00
else if ((int) (IPTR) node->nod_arg[e_msg_number] == 1)
{
out_message = node;
}
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
catch (const Exception&)
2008-01-29 11:11:52 +01:00
{
if (request)
CMP_release(tdbb, request);
else
dbb->deletePool(new_pool);
throw;
}
2001-05-23 15:26:42 +02:00
request->req_attachment = attachment;
2001-05-23 15:26:42 +02:00
USHORT len;
if (in_msg_length)
{
if (in_message) {
const Format* format = (Format*) in_message->nod_arg[e_msg_format];
len = format->fmt_length;
}
else {
len = 0;
}
if (in_msg_length != len)
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_port_len) << Arg::Num(in_msg_length) <<
Arg::Num(len));
}
memcpy((SCHAR*) request + in_message->nod_impure, in_msg, in_msg_length);
}
EXE_start(tdbb, request, transaction);
if (out_message) {
const Format* format = (Format*) out_message->nod_arg[e_msg_format];
2001-05-23 15:26:42 +02:00
len = format->fmt_length;
}
2001-12-24 03:51:06 +01:00
else {
2001-05-23 15:26:42 +02:00
len = 0;
2001-12-24 03:51:06 +01:00
}
if (out_msg_length != len) {
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_port_len) << Arg::Num(out_msg_length) <<
Arg::Num(len));
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
if (out_msg_length) {
memcpy(out_msg, (SCHAR*) request + out_message->nod_impure,
out_msg_length);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
check_autocommit(request, tdbb);
2001-12-24 03:51:06 +01:00
CMP_release(tdbb, request);
}
catch (const Exception& ex)
2001-12-24 03:51:06 +01:00
{
stuff_exception(user_status, ex);
}
2001-12-24 03:51:06 +01:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_TRANSACTION_INFO(ISC_STATUS* user_status,
jrd_tra** tra_handle,
SSHORT item_length,
const SCHAR* items,
SSHORT buffer_length,
SCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ t r a n s a c t i o n _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_tra* transaction = *tra_handle;
validateHandle(tdbb, transaction);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
INF_transaction_info(transaction, items, item_length, buffer,
2001-05-23 15:26:42 +02:00
buffer_length);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
return user_status[1];
2001-05-23 15:26:42 +02:00
}
ISC_STATUS GDS_UNWIND(ISC_STATUS * user_status,
jrd_req** req_handle,
SSHORT level)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g d s _ $ u n w i n d
*
**************************************
*
* Functional description
* Unwind a running request. This is potentially nasty since it can
* be called asynchronously.
*
**************************************/
2008-01-16 10:29:37 +01:00
ThreadContextHolder tdbb(user_status);
2001-05-23 15:26:42 +02:00
try
{
jrd_req* request = *req_handle;
validateHandle(tdbb, request);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
2001-05-23 15:26:42 +02:00
JRD_unwind_request(tdbb, request, level);
2001-12-24 03:51:06 +01:00
}
catch (const Exception& ex)
2001-05-23 15:26:42 +02:00
{
stuff_exception(user_status, ex);
2001-05-23 15:26:42 +02:00
}
return user_status[1];
2001-05-23 15:26:42 +02:00
}
2008-02-28 14:48:16 +01:00
ISC_STATUS GDS_DSQL_ALLOCATE(ISC_STATUS* user_status,
Attachment** db_handle,
dsql_req** stmt_handle)
{
ThreadContextHolder tdbb(user_status);
try
{
if (*stmt_handle)
status_exception::raise(Arg::Gds(isc_bad_req_handle));
2008-02-28 14:48:16 +01:00
Attachment* const attachment = *db_handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
*stmt_handle = DSQL_allocate_statement(tdbb, attachment);
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_EXECUTE(ISC_STATUS* user_status,
jrd_tra** tra_handle,
dsql_req** stmt_handle,
USHORT in_blr_length, const SCHAR* in_blr,
USHORT in_msg_type, USHORT in_msg_length, const SCHAR* in_msg,
USHORT out_blr_length, SCHAR* out_blr,
USHORT out_msg_type, USHORT out_msg_length, SCHAR* out_msg)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
if (*tra_handle)
validateHandle(tdbb, *tra_handle);
2008-02-28 14:48:16 +01:00
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_execute(tdbb, tra_handle, statement,
in_blr_length, reinterpret_cast<const UCHAR*>(in_blr),
in_msg_type, in_msg_length, reinterpret_cast<const UCHAR*>(in_msg),
out_blr_length, reinterpret_cast<UCHAR*>(out_blr),
out_msg_type, out_msg_length, reinterpret_cast<UCHAR*>(out_msg));
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_EXECUTE_IMMEDIATE(ISC_STATUS* user_status,
Attachment** db_handle,
jrd_tra** tra_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT in_blr_length, const SCHAR* in_blr,
USHORT in_msg_type, USHORT in_msg_length, const SCHAR* in_msg,
USHORT out_blr_length, SCHAR* out_blr,
USHORT out_msg_type, USHORT out_msg_length, SCHAR* out_msg)
{
ThreadContextHolder tdbb(user_status);
try
{
Attachment* const attachment = *db_handle;
validateHandle(tdbb, attachment);
if (*tra_handle)
validateHandle(tdbb, *tra_handle);
2008-02-28 14:48:16 +01:00
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_execute_immediate(tdbb, attachment, tra_handle,
length, string, dialect,
in_blr_length, reinterpret_cast<const UCHAR*>(in_blr),
in_msg_type, in_msg_length, reinterpret_cast<const UCHAR*>(in_msg),
out_blr_length, reinterpret_cast<UCHAR*>(out_blr),
out_msg_type, out_msg_length, reinterpret_cast<UCHAR*>(out_msg));
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_FETCH(ISC_STATUS* user_status,
dsql_req** stmt_handle,
USHORT blr_length, const SCHAR* blr,
USHORT msg_type, USHORT msg_length, SCHAR* dsql_msg_buf
#ifdef SCROLLABLE_CURSORS
, USHORT direction, SLONG offset
#endif
)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
return DSQL_fetch(tdbb, statement,
blr_length, reinterpret_cast<const UCHAR*>(blr),
msg_type, msg_length, reinterpret_cast<UCHAR*>(dsql_msg_buf)
#ifdef SCROLLABLE_CURSORS
, direction, offset
#endif
);
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_FREE(ISC_STATUS* user_status,
dsql_req** stmt_handle,
USHORT option)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_free_statement(tdbb, statement, option);
if (option & DSQL_drop)
*stmt_handle = NULL;
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_INSERT(ISC_STATUS* user_status,
dsql_req** stmt_handle,
USHORT blr_length, const SCHAR* blr,
USHORT msg_type, USHORT msg_length, const SCHAR* dsql_msg_buf)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_insert(tdbb, statement,
blr_length, reinterpret_cast<const UCHAR*>(blr),
msg_type, msg_length, reinterpret_cast<const UCHAR*>(dsql_msg_buf));
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_PREPARE(ISC_STATUS* user_status,
jrd_tra** tra_handle,
dsql_req** stmt_handle,
USHORT length, const TEXT* string, USHORT dialect,
USHORT item_length, const SCHAR* items,
USHORT buffer_length, SCHAR* buffer)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
if (*tra_handle)
validateHandle(tdbb, *tra_handle);
2008-02-28 14:48:16 +01:00
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_prepare(tdbb, *tra_handle, stmt_handle,
2008-02-28 14:48:16 +01:00
length, string, dialect,
item_length, reinterpret_cast<const UCHAR*>(items),
buffer_length, reinterpret_cast<UCHAR*>(buffer));
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_SET_CURSOR(ISC_STATUS* user_status,
dsql_req** stmt_handle,
const TEXT* cursor,
USHORT type)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_set_cursor(tdbb, statement, cursor, type);
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
ISC_STATUS GDS_DSQL_SQL_INFO(ISC_STATUS* user_status,
dsql_req** stmt_handle,
USHORT item_length, const SCHAR* items,
USHORT info_length, SCHAR* info)
{
ThreadContextHolder tdbb(user_status);
try
{
dsql_req* const statement = *stmt_handle;
validateHandle(tdbb, statement);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
DSQL_sql_info(tdbb, statement,
item_length, reinterpret_cast<const UCHAR*>(items),
info_length, reinterpret_cast<UCHAR*>(info));
}
catch (const Exception& ex)
2008-02-28 14:48:16 +01:00
{
stuff_exception(user_status, ex);
2008-02-28 14:48:16 +01:00
}
return user_status[1];
}
2001-05-23 15:26:42 +02:00
#ifdef DEBUG_PROCS
void JRD_print_procedure_info(thread_db* tdbb, const char* mesg)
2001-05-23 15:26:42 +02:00
{
/*****************************************************
*
* J R D _ p r i n t _ p r o c e d u r e _ i n f o
*
*****************************************************
*
* Functional description
* print name , use_count of all procedures in
2001-05-23 15:26:42 +02:00
* cache
*
******************************************************/
TEXT fname[MAXPATHLEN];
gds__prefix(fname, "proc_info.log");
2004-04-29 00:43:34 +02:00
FILE* fptr = fopen(fname, "a+");
if (!fptr) {
gds__log("Failed to open %s\n", fname);
2001-05-23 15:26:42 +02:00
return;
}
if (mesg)
2004-04-29 00:43:34 +02:00
fputs(mesg, fptr);
fprintf(fptr,
2001-05-23 15:26:42 +02:00
"Prc Name , prc id , flags , Use Count , Alter Count\n");
vec<jrd_prc*>* procedures = tdbb->getDatabase()->dbb_procedures;
if (procedures) {
vec<jrd_prc*>::iterator ptr, end;
2001-12-24 03:51:06 +01:00
for (ptr = procedures->begin(), end = procedures->end();
2006-02-10 04:28:43 +01:00
ptr < end; ++ptr)
{
const jrd_prc* procedure = *ptr;
if (procedure)
2004-04-29 00:43:34 +02:00
fprintf(fptr, "%s , %d, %X, %d, %d\n",
(procedure->prc_name->hasData()) ?
procedure->prc_name->c_str() : "NULL",
procedure->prc_id,
procedure->prc_flags, procedure->prc_use_count,
0); // procedure->prc_alter_count
}
2001-05-23 15:26:42 +02:00
}
else
2004-04-29 00:43:34 +02:00
fprintf(fptr, "No Cached Procedures\n");
2001-05-23 15:26:42 +02:00
2004-04-29 00:43:34 +02:00
fclose(fptr);
2001-05-23 15:26:42 +02:00
}
2006-07-21 03:35:17 +02:00
#endif // DEBUG_PROCS
2001-05-23 15:26:42 +02:00
bool JRD_reschedule(thread_db* tdbb, SLONG quantum, bool punt)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* J R D _ r e s c h e d u l e
*
**************************************
*
* Functional description
* Somebody has kindly offered to relinquish
* control so that somebody else may run.
*
**************************************/
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
if (dbb->dbb_sync->hasContention())
{
Database::Checkout dcoHolder(dbb);
2004-05-15 02:58:46 +02:00
THREAD_YIELD();
2001-05-23 15:26:42 +02:00
}
// Test various flags and unwind/throw if required.
// But do that only if we're not in the verb cleanup state,
// which should never be interrupted.
2001-05-23 15:26:42 +02:00
if (!(tdbb->tdbb_flags & TDBB_verb_cleanup))
{
// If database has been shutdown then get out
2001-05-23 15:26:42 +02:00
Attachment* attachment = tdbb->getAttachment();
jrd_tra* transaction = tdbb->getTransaction();
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
if (attachment)
{
if (dbb->dbb_ast_flags & DBB_shutdown &&
attachment->att_flags & ATT_shutdown)
{
const PathName& file_name = attachment->att_filename;
2008-08-31 03:10:41 +02:00
if (punt)
{
CCH_unwind(tdbb, false);
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(file_name));
}
2008-08-31 03:10:41 +02:00
else
{
ERR_build_status(tdbb->tdbb_status_vector,
Arg::Gds(isc_shutdown) << Arg::Str(file_name));
return true;
}
}
else if (attachment->att_flags & ATT_shutdown &&
!(tdbb->tdbb_flags & TDBB_shutdown_manager))
{
2008-08-31 03:10:41 +02:00
if (punt)
{
CCH_unwind(tdbb, false);
ERR_post(Arg::Gds(isc_att_shutdown));
2001-05-23 15:26:42 +02:00
}
2008-08-31 03:10:41 +02:00
else
{
ERR_build_status(tdbb->tdbb_status_vector,
Arg::Gds(isc_att_shutdown));
return true;
2001-05-23 15:26:42 +02:00
}
}
// If a cancel has been raised, defer its acknowledgement
// when executing in the context of an internal request or
// the system transaction.
if ((attachment->att_flags & ATT_cancel_raise) &&
!(attachment->att_flags & ATT_cancel_disable))
{
if ((!request ||
!(request->req_flags & (req_internal | req_sys_trigger))) &&
(!transaction || !(transaction->tra_flags & TRA_system)))
{
attachment->att_flags &= ~ATT_cancel_raise;
2008-08-31 03:10:41 +02:00
if (punt)
{
CCH_unwind(tdbb, false);
ERR_post(Arg::Gds(isc_cancelled));
}
2008-08-31 03:10:41 +02:00
else
{
ERR_build_status(tdbb->tdbb_status_vector, Arg::Gds(isc_cancelled));
return true;
}
}
}
2001-05-23 15:26:42 +02:00
}
// Handle request cancellation
if (transaction && (transaction->tra_flags & TRA_cancel_request))
{
transaction->tra_flags &= ~TRA_cancel_request;
tdbb->tdbb_flags |= TDBB_sys_error;
if (punt) {
CCH_unwind(tdbb, false);
ERR_post(Arg::Gds(isc_cancelled));
}
else {
ERR_build_status(tdbb->tdbb_status_vector, Arg::Gds(isc_cancelled));
return true;
}
}
}
// Enable signal handler for the monitoring stuff
if (dbb->dbb_ast_flags & DBB_monitor_off) {
dbb->dbb_ast_flags &= ~DBB_monitor_off;
LCK_lock(tdbb, dbb->dbb_monitor_lock, LCK_SR, LCK_WAIT);
2001-05-23 15:26:42 +02:00
}
tdbb->tdbb_quantum = (tdbb->tdbb_quantum <= 0) ?
#ifdef SUPERSERVER
2008-12-05 02:20:14 +01:00
(quantum ? quantum : (ThreadPriorityScheduler::boosted() ?
Config::getPriorityBoost() : 1) * QUANTUM) :
#else
(quantum ? quantum : QUANTUM) :
#endif
tdbb->tdbb_quantum;
return false;
2001-05-23 15:26:42 +02:00
}
void jrd_vtof(const char* string, char* field, SSHORT length)
{
/**************************************
*
* j r d _ v t o f
*
**************************************
*
* Functional description
* Move a null terminated string to a fixed length
* field.
* If the length of the string pointed to by 'field'
* is less than 'length', this function pads the
* destination string with space upto 'length' bytes.
*
* The call is primarily generated by the preprocessor.
*
* This is the same code as gds__vtof but is used internally.
*
**************************************/
while (*string) {
*field++ = *string++;
if (--length <= 0) {
return;
}
}
if (length) {
memset(field, ' ', length);
2001-05-23 15:26:42 +02:00
}
}
static void check_database(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c h e c k _ d a t a b a s e
*
**************************************
*
* Functional description
* Check an attachment for validity.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
Attachment* attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
const Attachment* attach = dbb->dbb_attachments;
while (attach && attach != attachment)
attach = attach->att_next;
if (!attach)
status_exception::raise(Arg::Gds(isc_bad_db_handle));
if (dbb->dbb_flags & DBB_bugcheck)
2008-01-16 10:29:37 +01:00
{
static const char string[] = "can't continue after bugcheck";
status_exception::raise(Arg::Gds(isc_bug_check) << Arg::Str(string));
2001-05-23 15:26:42 +02:00
}
if (attachment->att_flags & ATT_shutdown ||
((dbb->dbb_ast_flags & DBB_shutdown) &&
((dbb->dbb_ast_flags & DBB_shutdown_full) ||
!attachment->locksmith())))
{
if (dbb->dbb_ast_flags & DBB_shutdown)
{
const PathName& filename = attachment->att_filename;
status_exception::raise(Arg::Gds(isc_shutdown) << Arg::Str(filename));
}
else
{
status_exception::raise(Arg::Gds(isc_att_shutdown));
}
2001-05-23 15:26:42 +02:00
}
if ((attachment->att_flags & ATT_cancel_raise) &&
!(attachment->att_flags & ATT_cancel_disable))
{
2001-05-23 15:26:42 +02:00
attachment->att_flags &= ~ATT_cancel_raise;
status_exception::raise(Arg::Gds(isc_cancelled));
2001-05-23 15:26:42 +02:00
}
// Enable signal handler for the monitoring stuff
if (dbb->dbb_ast_flags & DBB_monitor_off)
{
dbb->dbb_ast_flags &= ~DBB_monitor_off;
LCK_lock(tdbb, dbb->dbb_monitor_lock, LCK_SR, LCK_WAIT);
}
2001-05-23 15:26:42 +02:00
}
static void check_transaction(thread_db* tdbb, jrd_tra* transaction)
{
/**************************************
*
* c h e c k _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Check transaction for not being interrupted
* in the meantime.
*
**************************************/
SET_TDBB(tdbb);
if (transaction && (transaction->tra_flags & TRA_cancel_request))
{
transaction->tra_flags &= ~TRA_cancel_request;
tdbb->tdbb_flags |= TDBB_sys_error;
status_exception::raise(Arg::Gds(isc_cancelled));
}
}
static void commit(thread_db* tdbb,
jrd_tra* transaction,
const bool retaining_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o m m i t
*
**************************************
*
* Functional description
* Commit a transaction.
*
**************************************/
if (transaction->tra_sibling && !(transaction->tra_flags & TRA_prepared))
2001-12-24 03:51:06 +01:00
{
prepare(tdbb, transaction, 0, NULL);
}
2001-05-23 15:26:42 +02:00
const Attachment* const attachment = tdbb->getAttachment();
2001-12-24 03:51:06 +01:00
if (!(attachment->att_flags & ATT_no_db_triggers) &&
!(transaction->tra_flags & TRA_prepared))
{
// run ON TRANSACTION COMMIT triggers
run_commit_triggers(tdbb, transaction);
2001-12-24 03:51:06 +01:00
}
jrd_tra* next = transaction;
2001-05-23 15:26:42 +02:00
while ( (transaction = next) )
{
next = transaction->tra_sibling;
validateHandle(tdbb, transaction->tra_attachment);
tdbb->setTransaction(transaction);
check_database(tdbb);
TRA_commit(tdbb, transaction, retaining_flag);
}
}
static bool drop_files(const jrd_file* file)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d r o p _ f i l e s
*
**************************************
*
* Functional description
* drop a linked list of files
*
**************************************/
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status;
2001-05-23 15:26:42 +02:00
status[1] = FB_SUCCESS;
2001-05-23 15:26:42 +02:00
for (; file; file = file->fil_next)
{
if (unlink(file->fil_string))
{
2008-12-05 02:20:14 +01:00
ERR_build_status(status, Arg::Gds(isc_io_error) << Arg::Str("unlink") <<
Arg::Str(file->fil_string) <<
Arg::Gds(isc_io_delete_err) << SYS_ERR(errno));
Database* dbb = GET_DBB();
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
gds__log_status(pageSpace->file->fil_string, status);
2001-05-23 15:26:42 +02:00
}
}
return status[1] ? true : false;
2001-05-23 15:26:42 +02:00
}
static jrd_tra* find_transaction(thread_db* tdbb, ISC_STATUS error_code)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i n d _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Find the element of a possible multiple database transaction
* that corresponds to the current database.
*
**************************************/
SET_TDBB(tdbb);
const Attachment* const attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
for (jrd_tra* transaction = tdbb->getTransaction(); transaction;
transaction = transaction->tra_sibling)
2008-01-16 10:29:37 +01:00
{
if (transaction->tra_attachment == attachment)
2008-01-16 10:29:37 +01:00
{
2001-05-23 15:26:42 +02:00
return transaction;
}
2008-01-16 10:29:37 +01:00
}
2001-05-23 15:26:42 +02:00
status_exception::raise(Arg::Gds(error_code));
return NULL; // Added to remove compiler warnings
2001-05-23 15:26:42 +02:00
}
static void find_intl_charset(thread_db* tdbb, Attachment* attachment, const DatabaseOptions* options)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i n d _ i n t l _ c h a r s e t
*
**************************************
*
* Functional description
* Attachment has declared it's prefered character set
* as part of LC_CTYPE, passed over with the attachment
* block. Now let's resolve that to an internal subtype id.
*
**************************************/
SET_TDBB(tdbb);
if (options->dpb_lc_ctype.isEmpty()) {
2006-07-21 03:35:17 +02:00
// No declaration of character set, act like 3.x Interbase
attachment->att_charset = DEFAULT_ATTACHMENT_CHARSET;
2001-05-23 15:26:42 +02:00
return;
}
2006-04-05 18:34:18 +02:00
USHORT id;
2001-05-23 15:26:42 +02:00
2004-11-27 07:51:58 +01:00
const UCHAR* lc_ctype =
reinterpret_cast<const UCHAR*>(options->dpb_lc_ctype.c_str());
2008-12-05 02:20:14 +01:00
2005-05-28 00:45:31 +02:00
if (MET_get_char_coll_subtype(tdbb, &id, lc_ctype, options->dpb_lc_ctype.length()) &&
INTL_defined_type(tdbb, id & 0xFF) &&
2005-05-28 00:45:31 +02:00
((id & 0xFF) != CS_BINARY))
2001-05-23 15:26:42 +02:00
{
2005-05-28 00:45:31 +02:00
attachment->att_charset = id & 0xFF;
2001-05-23 15:26:42 +02:00
}
else
{
2006-07-21 03:35:17 +02:00
// Report an error - we can't do what user has requested
ERR_post(Arg::Gds(isc_bad_dpb_content) <<
Arg::Gds(isc_charset_not_found) << Arg::Str(options->dpb_lc_ctype));
2001-05-23 15:26:42 +02:00
}
}
void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_client_SQL_dialect)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-01-16 10:29:37 +01:00
* D a t a b a s e O p t i o n s : : g e t
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Parse database parameter block picking up options and things.
*
**************************************/
SSHORT num_old_files = 0;
2008-01-16 10:29:37 +01:00
ULONG page_cache_size = Config::getDefaultDbCachePages();
if (page_cache_size < MIN_PAGE_BUFFERS)
page_cache_size = MIN_PAGE_BUFFERS;
if (page_cache_size > MAX_PAGE_BUFFERS)
page_cache_size = MAX_PAGE_BUFFERS;
dpb_buffers = page_cache_size;
dpb_sweep_interval = -1;
dpb_overwrite = false;
dpb_sql_dialect = 99;
invalid_client_SQL_dialect = false;
2001-05-23 15:26:42 +02:00
if (dpb_length == 0)
{
return;
}
if (dpb == NULL)
{
ERR_post(Arg::Gds(isc_bad_dpb_form));
}
ClumpletReader rdr(ClumpletReader::Tagged, dpb, dpb_length);
2001-05-23 15:26:42 +02:00
if (rdr.getBufferTag() != isc_dpb_version1)
{
ERR_post(Arg::Gds(isc_bad_dpb_form) <<
Arg::Gds(isc_wrodpbver));
}
2001-05-23 15:26:42 +02:00
for (; !(rdr.isEof()); rdr.moveNext())
2001-05-23 15:26:42 +02:00
{
switch (rdr.getClumpTag())
2001-05-23 15:26:42 +02:00
{
case isc_dpb_working_directory:
rdr.getPath(dpb_working_directory);
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_set_page_buffers:
dpb_page_buffers = rdr.getInt();
if (dpb_page_buffers &&
(dpb_page_buffers < MIN_PAGE_BUFFERS ||
dpb_page_buffers > MAX_PAGE_BUFFERS))
2001-05-23 15:26:42 +02:00
{
ERR_post(Arg::Gds(isc_bad_dpb_content));
2001-05-23 15:26:42 +02:00
}
dpb_set_page_buffers = true;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_num_buffers:
dpb_buffers = rdr.getInt();
if (dpb_buffers < 10)
{
ERR_post(Arg::Gds(isc_bad_dpb_content));
}
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_page_size:
dpb_page_size = (USHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_debug:
rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_sweep:
dpb_sweep = (USHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_sweep_interval:
dpb_sweep_interval = rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_verify:
dpb_verify = (USHORT) rdr.getInt();
if (dpb_verify & isc_dpb_ignore)
dpb_flags |= DBB_damaged;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_trace:
rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_damaged:
if (rdr.getInt() & 1)
dpb_flags |= DBB_damaged;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_enable_journal:
rdr.getString(dpb_journal);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_wal_backup_dir:
// ignore, skip
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_drop_walfile:
dpb_wal_action = (USHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_old_dump_id:
case isc_dpb_online_dump:
case isc_dpb_old_file_size:
case isc_dpb_old_num_files:
case isc_dpb_old_start_page:
case isc_dpb_old_start_seqno:
case isc_dpb_old_start_file:
// ignore, skip
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_old_file:
//if (num_old_files >= MAX_OLD_FILES) complain here, for now.
ERR_post(Arg::Gds(isc_num_old_files));
// following code is never executed now !
2001-05-23 15:26:42 +02:00
num_old_files++;
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_wal_chkptlen:
case isc_dpb_wal_numbufs:
case isc_dpb_wal_bufsize:
case isc_dpb_wal_grp_cmt_wait:
// ignore, skip
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_dbkey_scope:
dpb_dbkey_scope = (USHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_sys_user_name:
rdr.getString(dpb_sys_user_name);
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_sql_role_name:
2008-01-16 10:29:37 +01:00
if (! dpb_trusted_role)
{
rdr.getString(dpb_role_name);
}
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_user_name:
rdr.getString(dpb_user_name);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_password:
rdr.getString(dpb_password);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_password_enc:
rdr.getString(dpb_password_enc);
2001-05-23 15:26:42 +02:00
break;
2006-12-08 19:38:15 +01:00
case isc_dpb_trusted_auth:
rdr.getString(dpb_trusted_login);
break;
2008-01-16 10:29:37 +01:00
case isc_dpb_trusted_role:
dpb_trusted_role = true;
rdr.getString(dpb_role_name);
break;
2006-12-08 19:38:15 +01:00
2003-11-08 17:40:17 +01:00
case isc_dpb_encrypt_key:
2001-05-23 15:26:42 +02:00
#ifdef ISC_DATABASE_ENCRYPTION
rdr.getString(dpb_key);
2001-05-23 15:26:42 +02:00
#else
2006-07-21 03:35:17 +02:00
// Just in case there WAS a customer using this unsupported
// feature - post an error when they try to access it in 4.0
ERR_post(Arg::Gds(isc_uns_ext) <<
Arg::Gds(isc_random) << Arg::Str("Encryption not supported"));
2001-05-23 15:26:42 +02:00
#endif
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_no_garbage_collect:
dpb_no_garbage = true;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_activate_shadow:
dpb_activate_shadow = true;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_delete_shadow:
dpb_delete_shadow = true;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_force_write:
dpb_set_force_write = true;
dpb_force_write = rdr.getInt() != 0;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_begin_log:
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_quit_log:
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_no_reserve:
dpb_set_no_reserve = true;
dpb_no_reserve = rdr.getInt() != 0;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_interp:
dpb_interp = (SSHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_lc_messages:
rdr.getPath(dpb_lc_messages);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_lc_ctype:
rdr.getString(dpb_lc_ctype);
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_shutdown:
dpb_shutdown = (USHORT) rdr.getInt();
// Enforce default
if ((dpb_shutdown & isc_dpb_shut_mode_mask) == isc_dpb_shut_default)
dpb_shutdown |= isc_dpb_shut_multi;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_shutdown_delay:
dpb_shutdown_delay = (SSHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_online:
dpb_online = (USHORT) rdr.getInt();
// Enforce default
if ((dpb_online & isc_dpb_shut_mode_mask) == isc_dpb_shut_default)
{
dpb_online |= isc_dpb_shut_normal;
}
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_reserved:
{
string single;
rdr.getString(single);
if (single == "YES")
{
dpb_single_user = true;
}
}
break;
2001-05-23 15:26:42 +02:00
case isc_dpb_overwrite:
dpb_overwrite = rdr.getInt() != 0;
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_sec_attach:
dpb_sec_attach = rdr.getInt() != 0;
dpb_buffers = 50;
dpb_flags |= DBB_security_db;
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_gbak_attach:
{
string gbakStr;
rdr.getString(gbakStr);
dpb_gbak_attach = gbakStr.hasData();
}
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_gstat_attach:
dpb_gstat_attach = true;
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_gfix_attach:
dpb_gfix_attach = true;
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_gsec_attach:
dpb_gsec_attach = rdr.getBoolean();
break;
2001-05-23 15:26:42 +02:00
case isc_dpb_disable_wal:
dpb_disable_wal = true;
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_connect_timeout:
dpb_connect_timeout = rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
2003-11-08 17:40:17 +01:00
case isc_dpb_dummy_packet_interval:
dpb_dummy_packet_interval = rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_sql_dialect:
dpb_sql_dialect = (USHORT) rdr.getInt();
if (dpb_sql_dialect > SQL_DIALECT_V6)
invalid_client_SQL_dialect = true;
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_set_db_sql_dialect:
dpb_set_db_sql_dialect = (USHORT) rdr.getInt();
2001-05-23 15:26:42 +02:00
break;
case isc_dpb_set_db_readonly:
dpb_set_db_readonly = true;
dpb_db_readonly = rdr.getInt() != 0;
break;
case isc_dpb_set_db_charset:
rdr.getString(dpb_set_db_charset);
break;
2001-07-12 07:46:06 +02:00
case isc_dpb_address_path:
{
ClumpletReader address_stack(ClumpletReader::UnTagged,
rdr.getBytes(), rdr.getClumpLength());
while (!address_stack.isEof()) {
if (address_stack.getClumpTag() != isc_dpb_address) {
address_stack.moveNext();
continue;
}
ClumpletReader address(ClumpletReader::UnTagged,
address_stack.getBytes(), address_stack.getClumpLength());
while (!address.isEof()) {
switch (address.getClumpTag()) {
case isc_dpb_addr_protocol:
address.getString(dpb_network_protocol);
break;
case isc_dpb_addr_endpoint:
address.getString(dpb_remote_address);
break;
default:
break;
}
address.moveNext();
}
break;
}
}
break;
case isc_dpb_process_id:
dpb_remote_pid = rdr.getInt();
break;
case isc_dpb_process_name:
rdr.getPath(dpb_remote_process);
break;
case isc_dpb_no_db_triggers:
dpb_no_db_triggers = rdr.getInt() != 0;
break;
case isc_dpb_org_filename:
rdr.getPath(dpb_org_filename);
break;
case isc_dpb_utf8_filename:
dpb_utf8_filename = true;
break;
2001-05-23 15:26:42 +02:00
default:
break;
2001-05-23 15:26:42 +02:00
}
}
if (! rdr.isEof())
2002-07-02 11:49:19 +02:00
{
ERR_post(Arg::Gds(isc_bad_dpb_form));
2002-07-02 11:49:19 +02:00
}
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:29:37 +01:00
static ISC_STATUS handle_error(ISC_STATUS* user_status, ISC_STATUS code)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* h a n d l e _ e r r o r
*
**************************************
*
* Functional description
* An invalid handle has been passed in. If there is a user status
* vector, make it reflect the error. If not, emulate the routine
* "error" and abort.
*
**************************************/
ERR_build_status(user_status, Arg::Gds(code));
2001-05-23 15:26:42 +02:00
return code;
}
static Database* init(thread_db* tdbb,
const PathName& expanded_filename,
bool attach_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n i t
*
**************************************
*
* Functional description
* Initialize for database access. First call from both CREATE and
* OPEN.
* Upon entry mutex databases_mutex must be locked.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
2007-02-26 02:01:17 +01:00
// Initialize standard random generator.
// MSVC (at least since version 7) have per-thread random seed.
2007-03-01 01:44:14 +01:00
// As we don't know who uses per-thread seed, this should work for both cases.
2007-02-26 02:01:17 +01:00
static bool first_rand = true;
static int first_rand_value = rand();
if (first_rand || (rand() == first_rand_value))
srand(time(NULL));
first_rand = false;
2008-01-16 10:29:37 +01:00
engineStartup.init();
2007-02-26 03:13:42 +01:00
Database* dbb = NULL;
2001-05-23 15:26:42 +02:00
// Check to see if the database is already actively attached
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER
for (dbb = databases; dbb; dbb = dbb->dbb_next)
{
if (!(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use)) &&
(dbb->dbb_filename == expanded_filename))
2001-05-23 15:26:42 +02:00
{
2008-12-05 02:20:14 +01:00
if (attach_flag)
return dbb;
2008-02-14 08:31:25 +01:00
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str("DATABASE"));
2001-05-23 15:26:42 +02:00
}
}
#endif
2001-12-24 03:51:06 +01:00
dbb = Database::create();
tdbb->setDatabase(dbb);
2001-12-24 03:51:06 +01:00
dbb->dbb_bufferpool = dbb->createPool();
// provide context pool for the rest stuff
Jrd::ContextPoolHolder context(tdbb, dbb->dbb_permanent);
2001-05-23 15:26:42 +02:00
dbb->dbb_next = databases;
databases = dbb;
2001-05-23 15:26:42 +02:00
dbb->dbb_flags |= DBB_exclusive;
dbb->dbb_sweep_interval = SWEEP_INTERVAL;
2001-05-23 15:26:42 +02:00
dbb->dbb_monitoring_id = fb_utils::genUniqueId();
// set a garbage collection policy
if ((dbb->dbb_flags & (DBB_gc_cooperative | DBB_gc_background)) == 0)
{
string gc_policy = Config::getGCPolicy();
gc_policy.lower();
if (gc_policy == GCPolicyCooperative) {
dbb->dbb_flags |= DBB_gc_cooperative;
}
else if (gc_policy == GCPolicyBackground) {
dbb->dbb_flags |= DBB_gc_background;
}
else if (gc_policy == GCPolicyCombined) {
dbb->dbb_flags |= DBB_gc_cooperative | DBB_gc_background;
}
else // config value is invalid, use default
{
if (GCPolicyDefault == GCPolicyCooperative) {
dbb->dbb_flags |= DBB_gc_cooperative;
}
else if (GCPolicyDefault == GCPolicyBackground) {
dbb->dbb_flags |= DBB_gc_background;
}
else if (GCPolicyDefault == GCPolicyCombined) {
dbb->dbb_flags |= DBB_gc_cooperative | DBB_gc_background;
}
2008-12-05 02:20:14 +01:00
else
fb_assert(false);
}
}
// Initialize the lock manager
dbb->dbb_lock_mgr = LockManager::create(expanded_filename);
2001-05-23 15:26:42 +02:00
// Initialize a number of subsystems
2001-05-23 15:26:42 +02:00
TRA_init(dbb);
2001-05-23 15:26:42 +02:00
// Lookup some external "hooks"
2001-05-23 15:26:42 +02:00
PluginManager::Plugin crypt_lib =
PluginManager::enginePluginManager().findPlugin(CRYPT_IMAGE);
if (crypt_lib) {
string encrypt_entrypoint(ENCRYPT);
string decrypt_entrypoint(DECRYPT);
dbb->dbb_encrypt =
(Database::crypt_routine) crypt_lib.lookupSymbol(encrypt_entrypoint);
dbb->dbb_decrypt =
(Database::crypt_routine) crypt_lib.lookupSymbol(decrypt_entrypoint);
}
2001-05-23 15:26:42 +02:00
INTL_init(tdbb);
2004-03-07 08:58:55 +01:00
return dbb;
2001-05-23 15:26:42 +02:00
}
static void init_database_locks(thread_db* tdbb)
2006-07-19 08:19:56 +02:00
{
/**************************************
*
* i n i t _ d a t a b a s e _ l o c k s
*
**************************************
*
* Functional description
* Initialize database locks.
2006-07-19 08:19:56 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
2006-07-19 08:19:56 +02:00
// Main database lock
PageSpace* const pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
fb_assert(pageSpace && pageSpace->file);
UCharBuffer file_id;
PIO_get_unique_file_id(pageSpace->file, file_id);
size_t key_length = file_id.getCount();
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, key_length) Lock;
dbb->dbb_lock = lock;
lock->lck_type = LCK_database;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_object = dbb;
lock->lck_length = key_length;
lock->lck_dbb = dbb;
lock->lck_ast = CCH_down_grade_dbb;
memcpy(lock->lck_key.lck_string, file_id.begin(), key_length);
// Try to get an exclusive lock on database.
// If this fails, insist on at least a shared lock.
dbb->dbb_flags |= DBB_exclusive;
if (!LCK_lock(tdbb, lock, LCK_EX, LCK_NO_WAIT))
{
// Clean status vector from lock manager error code
fb_utils::init_status(tdbb->tdbb_status_vector);
dbb->dbb_flags &= ~DBB_exclusive;
while (!LCK_lock(tdbb, lock, LCK_SW, -1))
{
fb_utils::init_status(tdbb->tdbb_status_vector);
// If we are in a single-threaded maintenance mode then clean up and stop waiting
SCHAR spare_memory[MIN_PAGE_SIZE * 2];
SCHAR* header_page_buffer = (SCHAR*) FB_ALIGN((IPTR) spare_memory, MIN_PAGE_SIZE);
2008-07-09 14:34:43 +02:00
Ods::header_page* const header_page = reinterpret_cast<Ods::header_page*>(header_page_buffer);
PIO_header(dbb, header_page_buffer, MIN_PAGE_SIZE);
if ((header_page->hdr_flags & Ods::hdr_shutdown_mask) == Ods::hdr_shutdown_single)
{
ERR_post(Arg::Gds(isc_shutdown) << Arg::Str(pageSpace->file->fil_string));
}
}
}
2006-07-19 08:19:56 +02:00
// Lock shared by all dbb owners, used to signal other processes
// to dump their monitoring data and synchronize operations
lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock();
dbb->dbb_monitor_lock = lock;
lock->lck_type = LCK_monitor;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_parent = dbb->dbb_lock;
lock->lck_length = sizeof(SLONG);
lock->lck_dbb = dbb;
lock->lck_object = dbb;
lock->lck_ast = DatabaseSnapshot::blockingAst;
LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT);
2006-07-19 08:19:56 +02:00
}
2001-05-23 15:26:42 +02:00
2006-07-20 05:14:15 +02:00
static void prepare(thread_db* tdbb,
jrd_tra* transaction,
USHORT length,
const UCHAR* msg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r e p a r e
*
**************************************
*
* Functional description
* Attempt to prepare a transaction.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
if (!(transaction->tra_flags & TRA_prepared))
{
// run ON TRANSACTION COMMIT triggers
run_commit_triggers(tdbb, transaction);
}
2001-05-23 15:26:42 +02:00
for (; transaction; transaction = transaction->tra_sibling) {
validateHandle(tdbb, transaction->tra_attachment);
tdbb->setTransaction(transaction);
check_database(tdbb);
2001-05-23 15:26:42 +02:00
TRA_prepare(tdbb, transaction, length, msg);
}
}
static void release_attachment(thread_db* tdbb, Attachment* attachment, ISC_STATUS* s)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e l e a s e _ a t t a c h m e n t
*
**************************************
*
* Functional description
* Disconnect attachment block from database block.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
if (!attachment)
2001-05-23 15:26:42 +02:00
return;
if (attachment->att_strings_buffer && (attachment->att_strings_buffer != ((StringsBuffer*)(~0))))
{
if (! s)
{
// if no user vector passed for save operation, this is not error return
// let's save warning strings if present
s = tdbb->tdbb_status_vector;
}
StringsBuffer::makeEnginePermanentVector(s);
delete attachment->att_strings_buffer; // attachment will be released in the end of this function,
// keep that in sync please
attachment->att_strings_buffer = (StringsBuffer*)(~0);
}
2006-05-22 00:07:35 +02:00
#ifdef SUPERSERVER
2007-01-27 16:40:12 +01:00
if (dbb->dbb_relations)
2006-05-22 00:07:35 +02:00
{
2007-01-27 16:40:12 +01:00
vec<jrd_rel*>& rels = *dbb->dbb_relations;
for (size_t i = 1; i < rels.count(); i++)
2006-05-22 00:07:35 +02:00
{
2007-01-27 16:40:12 +01:00
jrd_rel* relation = rels[i];
if (relation && (relation->rel_flags & REL_temp_conn) &&
!(relation->rel_flags & (REL_deleted | REL_deleting)) )
{
relation->delPages(tdbb);
}
2006-05-22 00:07:35 +02:00
}
}
#endif
2001-05-23 15:26:42 +02:00
if (attachment->att_event_session)
EVENT_delete_session(attachment->att_event_session);
if (attachment->att_id_lock)
LCK_release(tdbb, attachment->att_id_lock);
2006-05-22 00:07:35 +02:00
#ifndef SUPERSERVER
if (attachment->att_temp_pg_lock)
LCK_release(tdbb, attachment->att_temp_pg_lock);
2008-01-16 10:29:37 +01:00
2008-12-05 02:20:14 +01:00
for (bool getResult = attachment->att_dsql_cache.getFirst(); getResult;
getResult = attachment->att_dsql_cache.getNext())
2008-01-16 10:29:37 +01:00
{
LCK_release(tdbb, attachment->att_dsql_cache.current()->second.lock);
2008-01-16 10:29:37 +01:00
}
2006-05-22 00:07:35 +02:00
#endif
for (vcl** vector = attachment->att_counts;
2001-05-23 15:26:42 +02:00
vector < attachment->att_counts + DBB_max_count; ++vector)
{
2001-05-23 15:26:42 +02:00
if (*vector)
2005-01-25 07:33:07 +01:00
{
2001-12-24 03:51:06 +01:00
delete *vector;
2005-01-25 07:33:07 +01:00
*vector = 0;
}
}
2001-05-23 15:26:42 +02:00
// Release any validation error vector allocated
2001-05-23 15:26:42 +02:00
if (attachment->att_val_errors) {
2001-12-24 03:51:06 +01:00
delete attachment->att_val_errors;
attachment->att_val_errors = NULL;
2001-05-23 15:26:42 +02:00
}
2007-06-15 11:28:56 +02:00
detachLocksFromAttachment(attachment);
2001-05-23 15:26:42 +02:00
if (attachment->att_flags & ATT_lck_init_done) {
2006-07-21 03:35:17 +02:00
LCK_fini(tdbb, LCK_OWNER_attachment); // For the attachment
attachment->att_flags &= ~ATT_lck_init_done;
}
2001-05-23 15:26:42 +02:00
delete attachment->att_compatibility_table;
2001-05-23 15:26:42 +02:00
2008-02-28 14:48:16 +01:00
if (attachment->att_dsql_instance) {
MemoryPool* const pool = &attachment->att_dsql_instance->dbb_pool;
delete attachment->att_dsql_instance;
dbb->deletePool(pool);
}
2006-07-21 03:35:17 +02:00
// remove the attachment block from the dbb linked list
2001-05-23 15:26:42 +02:00
for (Attachment** ptr = &dbb->dbb_attachments; *ptr; ptr = &(*ptr)->att_next) {
2001-05-23 15:26:42 +02:00
if (*ptr == attachment) {
*ptr = attachment->att_next;
break;
}
}
// CMP_release() advances the pointer before the deallocation.
jrd_req* request;
while ( (request = attachment->att_requests) ) {
CMP_release(tdbb, request);
}
SCL_release_all(attachment->att_security_classes);
delete attachment->att_user;
Attachment::destroy(attachment); // string were re-saved in the beginning of this function,
// keep that in sync please
tdbb->setAttachment(NULL);
2001-05-23 15:26:42 +02:00
}
2007-06-15 11:28:56 +02:00
static void detachLocksFromAttachment(Attachment* attachment)
{
/**************************************
*
* d e t a c h L o c k s F r o m A t t a c h m e n t
*
**************************************
*
* Functional description
* Bug #7781, need to null out the attachment pointer of all locks which
* were hung off this attachment block, to ensure that the attachment
* block doesn't get dereferenced after it is released
*
**************************************/
Lock* long_lock = attachment->att_long_locks;
while (long_lock) {
Lock* next = long_lock->lck_next;
long_lock->lck_attachment = NULL;
long_lock->lck_next = NULL;
long_lock->lck_prior = NULL;
long_lock = next;
}
attachment->att_long_locks = NULL;
}
Attachment::Attachment(MemoryPool* pool, Database* dbb)
: att_pool(pool),
att_memory_stats(&dbb->dbb_memory_stats),
2008-12-05 02:20:14 +01:00
att_database(dbb),
att_lock_owner_id(Database::getLockOwnerId()),
att_lc_messages(*pool),
2008-12-05 02:20:14 +01:00
att_working_directory(*pool),
att_filename(*pool),
att_timestamp(TimeStamp::getCurrentTimeStamp()),
att_context_vars(*pool),
att_network_protocol(*pool),
att_remote_address(*pool),
att_remote_process(*pool),
att_dsql_cache(*pool),
att_udf_pointers(*pool),
att_strings_buffer(NULL),
att_ext_connection(NULL)
2008-02-28 14:48:16 +01:00
{
att_mutex.enter();
2008-02-28 14:48:16 +01:00
}
2008-03-17 03:00:16 +01:00
Attachment::~Attachment()
{
// For normal attachments that happens release_attachment(),
2008-12-05 02:20:14 +01:00
// but for special ones like GC should be done also in dtor -
2008-03-17 03:00:16 +01:00
// they do not (and should not) call release_attachment().
2008-12-05 02:20:14 +01:00
// It's no danger calling detachLocksFromAttachment()
2008-03-17 03:00:16 +01:00
// once more here because it nulls att_long_locks.
// AP 2007
detachLocksFromAttachment(this);
if (att_strings_buffer != ((StringsBuffer*)(~0)))
{
delete att_strings_buffer;
}
att_mutex.leave();
2008-03-17 03:00:16 +01:00
}
PreparedStatement* Attachment::prepareStatement(thread_db* tdbb, MemoryPool& pool,
jrd_tra* transaction, const string& text)
2008-03-17 03:00:16 +01:00
{
2008-03-31 03:51:18 +02:00
return FB_NEW(pool) PreparedStatement(tdbb, pool, this, transaction, text);
2008-03-17 03:00:16 +01:00
}
void Attachment::cancelExternalConnection(thread_db* tdbb)
{
if (att_ext_connection) {
att_ext_connection->cancelExecution(tdbb);
}
}
2008-03-17 03:00:16 +01:00
static void rollback(thread_db* tdbb,
jrd_tra* transaction,
const bool retaining_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r o l l b a c k
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Abort a transaction.
2001-05-23 15:26:42 +02:00
*
**************************************/
ISC_STATUS_ARRAY user_status = {0};
ISC_STATUS_ARRAY local_status = {0};
ISC_STATUS* const orig_status = tdbb->tdbb_status_vector;
2001-05-23 15:26:42 +02:00
try
2001-05-23 15:26:42 +02:00
{
jrd_tra* next = transaction;
2001-05-23 15:26:42 +02:00
while ( (transaction = next) )
{
next = transaction->tra_sibling;
2001-05-23 15:26:42 +02:00
try
{
validateHandle(tdbb, transaction->tra_attachment);
check_database(tdbb);
2006-11-10 09:12:07 +01:00
const Database* const dbb = tdbb->getDatabase();
const Attachment* const attachment = tdbb->getAttachment();
if (!(attachment->att_flags & ATT_no_db_triggers))
2006-11-10 09:12:07 +01:00
{
try
{
ISC_STATUS_ARRAY temp_status = {0};
tdbb->tdbb_status_vector = temp_status;
// run ON TRANSACTION ROLLBACK triggers
2008-02-03 03:32:39 +01:00
EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_rollback);
}
catch (const Exception&)
{
if (dbb->dbb_flags & DBB_bugcheck)
throw;
}
}
2001-12-24 03:51:06 +01:00
tdbb->tdbb_status_vector = user_status;
tdbb->setTransaction(transaction);
TRA_rollback(tdbb, transaction, retaining_flag, false);
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
tdbb->tdbb_status_vector = local_status;
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
catch (const Exception& ex)
{
stuff_exception(user_status, ex);
}
2001-05-23 15:26:42 +02:00
2008-03-08 22:20:26 +01:00
tdbb->tdbb_status_vector = orig_status;
if (user_status[1] != FB_SUCCESS)
status_exception::raise(user_status);
2001-05-23 15:26:42 +02:00
}
2004-03-07 08:58:55 +01:00
static void shutdown_database(Database* dbb, const bool release_pools)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s h u t d o w n _ d a t a b a s e
*
**************************************
*
* Functional description
* Shutdown physical database environment.
* NOTE: This routine assumes that upon entry,
* mutex databases_mutex will be locked.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Shutdown file and/or remote connection
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
2006-07-21 03:35:17 +02:00
TRA_header_write(tdbb, dbb, 0L); // Update transaction info on header page.
2001-05-23 15:26:42 +02:00
#endif
#ifdef GARBAGE_THREAD
VIO_fini(tdbb);
#endif
CMP_fini(tdbb);
CCH_fini(tdbb);
DatabaseSnapshot::cleanup(tdbb);
if (dbb->dbb_backup_manager)
dbb->dbb_backup_manager->shutdown(tdbb);
2001-05-23 15:26:42 +02:00
if (dbb->dbb_monitor_lock)
LCK_release(tdbb, dbb->dbb_monitor_lock);
2001-05-23 15:26:42 +02:00
if (dbb->dbb_shadow_lock)
LCK_release(tdbb, dbb->dbb_shadow_lock);
if (dbb->dbb_retaining_lock)
LCK_release(tdbb, dbb->dbb_retaining_lock);
if (dbb->dbb_sh_counter_lock)
LCK_release(tdbb, dbb->dbb_sh_counter_lock);
// temporal measure to avoid unstable state of lock file -
// this is anyway called in ~Database()
dbb->destroyIntlObjects();
2006-07-21 03:35:17 +02:00
// Shut down any extern relations
2001-05-23 15:26:42 +02:00
if (dbb->dbb_relations)
{
vec<jrd_rel*>* vector = dbb->dbb_relations;
vec<jrd_rel*>::iterator ptr = vector->begin(), end = vector->end();
2001-05-23 15:26:42 +02:00
while (ptr < end)
2001-05-23 15:26:42 +02:00
{
jrd_rel* relation = *ptr++;
2007-10-26 12:53:47 +02:00
if (relation)
2001-05-23 15:26:42 +02:00
{
2007-10-26 12:53:47 +02:00
if (relation->rel_file)
{
EXT_fini(relation, false);
}
2007-10-26 12:53:47 +02:00
for (IndexBlock* index_block = relation->rel_index_blocks; index_block;
index_block = index_block->idb_next)
{
if (index_block->idb_lock)
LCK_release(tdbb, index_block->idb_lock);
}
2001-05-23 15:26:42 +02:00
}
}
}
2007-10-26 12:53:47 +02:00
if (dbb->dbb_lock)
LCK_release(tdbb, dbb->dbb_lock);
2004-03-07 08:58:55 +01:00
Database** d_ptr; // Intentionally left outside loop (HP/UX compiler)
for (d_ptr = &databases; *d_ptr; d_ptr = &(*d_ptr)->dbb_next) {
2001-05-23 15:26:42 +02:00
if (*d_ptr == dbb) {
*d_ptr = dbb->dbb_next;
break;
}
}
if (dbb->dbb_flags & DBB_lck_init_done) {
dbb->dbb_page_manager.releaseLocks();
2006-07-21 03:35:17 +02:00
LCK_fini(tdbb, LCK_OWNER_database); // For the database
2001-05-23 15:26:42 +02:00
dbb->dbb_flags &= ~DBB_lck_init_done;
}
if (release_pools) {
tdbb->setDatabase(NULL);
Database::destroy(dbb);
2001-05-23 15:26:42 +02:00
}
}
static void strip_quotes(string& out)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s t r i p _ q u o t e s
*
**************************************
*
* Functional description
* Get rid of quotes around strings
*
**************************************/
2008-12-05 02:20:14 +01:00
if (out.isEmpty())
{
2001-05-23 15:26:42 +02:00
return;
}
if (out[0] == DBL_QUOTE || out[0] == SINGLE_QUOTE)
{
// Skip any initial quote
2004-11-27 07:51:58 +01:00
const char quote = out[0];
out.erase(0, 1);
// Search for same quote
size_t pos = out.find(quote);
if (pos != string::npos)
{
out.erase(pos);
}
2001-05-23 15:26:42 +02:00
}
}
static bool shutdown_dbb(thread_db* tdbb, Database* dbb)
{
/**************************************
*
* s h u t d o w n _ d b b
*
**************************************
*
* Functional description
* rollback every transaction,
* release every attachment,
* and shutdown database.
*
**************************************/
tdbb->setDatabase(dbb);
tdbb->tdbb_flags |= TDBB_shutdown_manager;
DatabaseContextHolder dbbHolder(tdbb);
if (!(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use | DBB_security_db)) &&
!(dbb->dbb_ast_flags & DBB_shutdown &&
dbb->dbb_ast_flags & DBB_shutdown_locks))
{
Attachment* att_next;
2008-01-16 10:29:37 +01:00
for (Attachment* attach = dbb->dbb_attachments; attach; attach = att_next)
{
att_next = attach->att_next;
tdbb->setAttachment(attach);
// purge_attachment() below can do an ERR_post
ISC_STATUS_ARRAY temp_status;
fb_utils::init_status(temp_status);
tdbb->tdbb_status_vector = temp_status;
try
{
// purge attachment, rollback any open transactions
purge_attachment(tdbb, temp_status, attach, true);
}
catch (const Exception& ex)
{
iscLogException("error while shutting down attachment", ex);
return false;
}
}
}
return true;
}
UCHAR* JRD_num_attachments(UCHAR* const buf, USHORT buf_len, JRD_info_tag flag,
ULONG* atts, ULONG* dbs)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* J R D _ n u m _ a t t a c h m e n t s
*
**************************************
*
* Functional description
* Count the number of active databases and
* attachments. If flag is set then put
* what it says into buf, if it fits. If it does not fit
* then allocate local buffer, put info into there, and
* return pointer to caller (in this case a caller must
* release memory allocated for local buffer).
*
**************************************/
2006-07-21 03:35:17 +02:00
// protect against NULL value for buf
2001-05-23 15:26:42 +02:00
UCHAR* lbuf = buf;
if (!lbuf)
buf_len = 0;
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
2006-07-21 03:35:17 +02:00
// Check that the buffer is big enough for the requested
// information. If not, unset the flag
2001-05-23 15:26:42 +02:00
if (flag == JRD_info_drivemask) {
if (buf_len < sizeof(ULONG)) {
lbuf = (UCHAR*) gds__alloc((SLONG) (sizeof(ULONG)));
if (!lbuf)
flag = JRD_info_none;
}
}
2001-05-23 15:26:42 +02:00
#endif
ULONG num_att = 0;
ULONG drive_mask = 0L;
ULONG total = 0;
HalfStaticArray<PathName, 8> dbFiles;
try
{
MutexLockGuard guard(databases_mutex);
// Zip through the list of databases and count the number of local
// connections. If buf is not NULL then copy all the database names
// that will fit into it.
2001-05-23 15:26:42 +02:00
for (Database* dbb = databases; dbb; dbb = dbb->dbb_next)
{
Database::SyncGuard dsGuard(dbb);
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
// Get drive letters for db files
if (flag == JRD_info_drivemask)
{
const PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
for (const jrd_file* files = pageSpace->file; files; files = files->fil_next)
ExtractDriveLetter(files->fil_string, &drive_mask);
2001-05-23 15:26:42 +02:00
}
#endif
2001-05-23 15:26:42 +02:00
if (!(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use | DBB_security_db)) &&
!(dbb->dbb_ast_flags & DBB_shutdown
&& dbb->dbb_ast_flags & DBB_shutdown_locks))
{
if (!dbFiles.exist(dbb->dbb_filename))
dbFiles.add(dbb->dbb_filename);
total += sizeof(USHORT) + dbb->dbb_filename.length();
for (const Attachment* attach = dbb->dbb_attachments; attach;
attach = attach->att_next)
{
num_att++;
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
// Get drive letters for temp directories
2001-05-23 15:26:42 +02:00
if (flag == JRD_info_drivemask) {
const TempDirectoryList dirList;
for (size_t i = 0; i < dirList.getCount(); i++) {
const PathName& path = dirList[i];
ExtractDriveLetter(path.c_str(), &drive_mask);
}
2001-05-23 15:26:42 +02:00
}
#endif
}
2001-05-23 15:26:42 +02:00
}
}
}
catch (const Exception&)
{
// Here we ignore possible errors from databases_mutex.
2008-12-05 02:20:14 +01:00
// They were always silently ignored, and for this function
// we really have no way to notify world about mutex problem.
// AP. 2008.
}
2001-05-23 15:26:42 +02:00
const ULONG num_dbs = dbFiles.getCount();
2001-05-23 15:26:42 +02:00
*atts = num_att;
*dbs = num_dbs;
if (num_dbs > 0)
2001-05-23 15:26:42 +02:00
{
if (flag == JRD_info_dbnames)
{
if (buf_len < (sizeof(USHORT) + total))
2001-05-23 15:26:42 +02:00
{
lbuf = (UCHAR*) gds__alloc(sizeof(USHORT) + total);
2001-05-23 15:26:42 +02:00
}
UCHAR* lbufp = lbuf;
if (lbufp)
2001-05-23 15:26:42 +02:00
{
/* Put db info into buffer. Format is as follows:
number of dbases sizeof (USHORT)
1st db name length sizeof (USHORT)
1st db name sizeof (TEXT) * length
2nd db name length
2nd db name
...
last db name length
last db name
*/
fb_assert(num_dbs < MAX_USHORT);
*lbufp++ = (UCHAR) num_dbs;
*lbufp++ = (UCHAR) (num_dbs >> 8);
for (size_t n = 0; n < num_dbs; ++n) {
const USHORT dblen = dbFiles[n].length();
*lbufp++ = (UCHAR) dblen;
*lbufp++ = (UCHAR) (dblen >> 8);
memcpy(lbufp, dbFiles[n].c_str(), dblen);
lbufp += dblen;
2001-05-23 15:26:42 +02:00
}
}
}
}
#ifdef WIN_NT
if (flag == JRD_info_drivemask)
*(ULONG *) lbuf = drive_mask;
#endif
// CVC: Apparently, the original condition will leak memory, because flag
// may be JRD_info_drivemask and memory could be allocated for that purpose,
// as few as sizeof(ULONG), but a leak is a leak! I added the ifdef below.
2001-05-23 15:26:42 +02:00
if (num_dbs == 0)
{
#ifdef WIN_NT
if (flag == JRD_info_drivemask && lbuf != buf)
gds__free(lbuf);
#endif
2001-05-23 15:26:42 +02:00
lbuf = NULL;
}
2001-05-23 15:26:42 +02:00
return lbuf;
}
#ifdef WIN_NT
static void ExtractDriveLetter(const TEXT* file_name, ULONG* drive_mask)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E x t r a c t D r i v e L e t t e r
*
**************************************
*
* Functional description
* Determine the drive letter of file_name
* and set the proper bit in the bit mask.
* bit 0 = drive A
* bit 1 = drive B and so on...
* This function is used to determine drive
* usage for use with Plug and Play for
* MS Windows 4.0.
*
**************************************/
ULONG mask = 1;
const SHORT shift = (*file_name - 'A');
2001-05-23 15:26:42 +02:00
mask <<= shift;
*drive_mask |= mask;
}
#endif
static unsigned int purge_transactions(thread_db* tdbb,
Attachment* attachment,
const bool force_flag,
const ULONG att_flags)
{
/**************************************
*
* p u r g e _ t r a n s a c t i o n s
*
**************************************
*
* Functional description
2008-12-05 02:20:14 +01:00
* commit or rollback all transactions
* from an attachment
*
**************************************/
Database* dbb = attachment->att_database;
jrd_tra* trans_dbk = attachment->att_dbkey_trans;
unsigned int count = 0;
jrd_tra* next;
2007-07-26 03:23:18 +02:00
for (jrd_tra* transaction = attachment->att_transactions;
transaction;
transaction = next)
{
next = transaction->tra_next;
if (transaction != trans_dbk)
{
2007-07-26 03:23:18 +02:00
if ((transaction->tra_flags & TRA_prepared) ||
(dbb->dbb_ast_flags & DBB_shutdown) ||
(att_flags & ATT_shutdown))
{
TRA_release_transaction(tdbb, transaction);
}
else if (force_flag)
TRA_rollback(tdbb, transaction, false, true);
else
++count;
}
}
if (count)
{
return count;
}
// If there's a side transaction for db-key scope, get rid of it
if (trans_dbk)
{
attachment->att_dbkey_trans = NULL;
2007-07-26 03:23:18 +02:00
if ((dbb->dbb_ast_flags & DBB_shutdown) ||
(att_flags & ATT_shutdown))
{
TRA_release_transaction(tdbb, trans_dbk);
}
else
{
TRA_commit(tdbb, trans_dbk, false);
}
}
return 0;
}
static void purge_attachment(thread_db* tdbb,
2003-04-10 08:49:16 +02:00
ISC_STATUS* user_status,
2008-01-16 10:29:37 +01:00
Attachment* attachment,
const bool force_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p u r g e _ a t t a c h m e n t
*
**************************************
*
* Functional description
* Zap an attachment, shutting down the database
* if it is the last one.
* NOTE: This routine assumes that upon entry,
* mutex databases_mutex will be locked.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = attachment->att_database;
2008-12-05 02:20:14 +01:00
if (!(dbb->dbb_flags & DBB_bugcheck))
{
try
{
if (!(attachment->att_flags & ATT_no_db_triggers) &&
!(attachment->att_flags & ATT_shutdown))
{
ThreadStatusGuard temp_status(tdbb);
jrd_tra* transaction = NULL;
try
{
// start a transaction to execute ON DISCONNECT triggers
transaction = TRA_start(tdbb, 0, NULL);
// run ON DISCONNECT triggers
EXE_execute_db_triggers(tdbb, transaction,
jrd_req::req_trigger_disconnect);
// and commit the transaction
TRA_commit(tdbb, transaction, false);
}
catch (const Exception&)
{
if (dbb->dbb_flags & DBB_bugcheck)
throw;
try
{
if (transaction)
TRA_rollback(tdbb, transaction, false, false);
}
catch (const Exception&)
{
if (dbb->dbb_flags & DBB_bugcheck)
throw;
}
}
}
}
catch (const Exception&)
{
attachment->att_flags |= (ATT_shutdown | ATT_purge_error);
throw;
}
}
const bool wasShuttingDown = engineShuttingDown;
try
{
// allow to free resources used by dynamic statements
engineShuttingDown = false;
EDS::Manager::jrdAttachmentEnd(tdbb, attachment);
engineShuttingDown = wasShuttingDown;
const ULONG att_flags = attachment->att_flags;
attachment->att_flags |= ATT_shutdown;
if (!(dbb->dbb_flags & DBB_bugcheck))
2001-05-23 15:26:42 +02:00
{
// Check for any pending transactions
unsigned int count = purge_transactions(tdbb, attachment, force_flag, att_flags);
if (count)
{
ERR_post(Arg::Gds(isc_open_trans) << Arg::Num(count));
}
2001-05-23 15:26:42 +02:00
SORT_shutdown(attachment);
}
}
catch (const Exception&)
{
engineShuttingDown = wasShuttingDown;
attachment->att_flags |= (ATT_shutdown | ATT_purge_error);
throw;
2001-05-23 15:26:42 +02:00
}
// Unlink attachment from database
2001-05-23 15:26:42 +02:00
release_attachment(tdbb, attachment); // normal release - no status vector processing
2001-05-23 15:26:42 +02:00
// If there are still attachments, do a partial shutdown
2001-05-23 15:26:42 +02:00
if (dbb->checkHandle())
2001-05-23 15:26:42 +02:00
{
if (!dbb->dbb_attachments && !(dbb->dbb_flags & DBB_being_opened))
2001-05-23 15:26:42 +02:00
{
shutdown_database(dbb, true);
2001-05-23 15:26:42 +02:00
}
}
}
static void run_commit_triggers(thread_db* tdbb, jrd_tra* transaction)
{
/**************************************
*
* r u n _ c o m m i t _ t r i g g e r s
*
**************************************
*
* Functional description
* Run ON TRANSACTION COMMIT triggers of a transaction.
*
**************************************/
SET_TDBB(tdbb);
if (transaction == tdbb->getDatabase()->dbb_sys_trans)
return;
// start a savepoint to rollback changes of all triggers
VIO_start_save_point(tdbb, transaction);
try
{
// run ON TRANSACTION COMMIT triggers
EXE_execute_db_triggers(tdbb, transaction,
jrd_req::req_trigger_trans_commit);
VIO_verb_cleanup(tdbb, transaction);
}
catch (const Exception&)
{
if (!(tdbb->getDatabase()->dbb_flags & DBB_bugcheck))
{
// rollbacks the created savepoint
++transaction->tra_save_point->sav_verb_count;
VIO_verb_cleanup(tdbb, transaction);
}
throw;
}
}
// verify_request_synchronization
//
// @brief Finds the sub-requests at the given level and replaces it with the
// original passed request (note the pointer by reference). If that specific
// sub-request is not found, throw the dreaded "request synchronization error".
// Notice that at this time, the calling function's "request" pointer has been
// set to null, so remember that if you write a debugging routine.
// This function replaced a chunk of code repeated four times.
//
// @param request The incoming, parent request to be replaced.
// @param level The level of the sub-request we need to find.
static void verify_request_synchronization(jrd_req*& request, SSHORT level)
{
const USHORT lev = level;
if (lev) {
const vec<jrd_req*>* vector = request->req_sub_requests;
if (!vector || lev >= vector->count() ||
!(request = (*vector)[lev]))
{
ERR_post(Arg::Gds(isc_req_sync));
}
}
}
/**
2008-12-05 02:20:14 +01:00
verify_database_name
2008-12-05 02:20:14 +01:00
@brief Verify database name for open/create
against given in conf file list of available directories
and security database name
@param name
@param status
**/
static vdnResult verify_database_name(const PathName& name, ISC_STATUS* status)
{
2005-02-24 13:24:38 +01:00
// Check for security2.fdb
2008-07-06 18:42:52 +02:00
static TEXT securityNameBuffer[MAXPATHLEN] = "";
static GlobalPtr<PathName> expandedSecurityNameBuffer;
static GlobalPtr<Mutex> mutex;
MutexLockGuard guard(mutex);
2008-07-06 18:42:52 +02:00
if (! securityNameBuffer[0]) {
SecurityDatabase::getPath(securityNameBuffer);
expandedSecurityNameBuffer->assign(securityNameBuffer);
ISC_expand_filename(expandedSecurityNameBuffer, false);
}
2008-07-06 18:42:52 +02:00
if (name == securityNameBuffer || name == expandedSecurityNameBuffer)
return vdnSecurity;
// Check for .conf
if (!JRD_verify_database_access(name)) {
2008-12-05 02:20:14 +01:00
ERR_build_status(status, Arg::Gds(isc_conf_access_denied) << Arg::Str("database") <<
Arg::Str(name));
return vdnFail;
}
return vdnOk;
}
/**
2008-12-05 02:20:14 +01:00
getUserInfo
2008-12-05 02:20:14 +01:00
@brief Checks the userinfo database to validate
password to that passed in.
2007-06-09 06:06:26 +02:00
Takes into account possible trusted authentication.
Fills UserId structure with resulting values.
@param user
@param options
**/
static void getUserInfo(UserId& user, const DatabaseOptions& options)
{
int id = -1, group = -1; // CVC: This var contained trash
int node_id = 0;
string name;
#ifdef BOOT_BUILD
bool wheel = true;
#else
bool wheel = false;
2008-01-16 10:29:37 +01:00
if (options.dpb_trusted_login.hasData())
{
2008-01-16 13:29:45 +01:00
name = options.dpb_trusted_login;
}
2008-01-16 10:29:37 +01:00
else
{
2008-12-05 02:20:14 +01:00
if (options.dpb_user_name.isEmpty() &&
options.dpb_network_protocol.isEmpty() && // This 2 checks ensure that we are not remote server
options.dpb_remote_address.isEmpty()) // process, i.e. can use unix OS auth.
{
wheel = ISC_get_user(&name, &id, &group,
options.dpb_sys_user_name.nullStr());
}
2008-01-16 10:29:37 +01:00
if (options.dpb_user_name.hasData() || (id == -1))
{
2008-12-05 02:20:14 +01:00
string remote = options.dpb_network_protocol +
2008-01-16 10:29:37 +01:00
(options.dpb_network_protocol.isEmpty() || options.dpb_remote_address.isEmpty() ? "" : "/") +
options.dpb_remote_address;
2008-12-05 02:20:14 +01:00
SecurityDatabase::verifyUser(name,
options.dpb_user_name.nullStr(),
options.dpb_password.nullStr(),
2008-01-16 10:29:37 +01:00
options.dpb_password_enc.nullStr(),
&id, &group, &node_id, remote);
}
2008-01-16 10:29:37 +01:00
}
2008-01-16 10:29:37 +01:00
// if the name from the user database is defined as SYSDBA,
// we define that user id as having system privileges
name.upper();
2008-01-16 10:29:37 +01:00
if (name == SYSDBA_USER_NAME)
{
wheel = true;
}
#endif // BOOT_BUILD
// In case we became WHEEL on an OS that didn't require name SYSDBA,
// (Like Unix) force the effective Database User name to be SYSDBA
if (wheel)
{
name = SYSDBA_USER_NAME;
}
if (name.length() > USERNAME_LENGTH)
{
2008-12-05 02:20:14 +01:00
status_exception::raise(Arg::Gds(isc_long_login) <<
Arg::Num(name.length()) << Arg::Num(USERNAME_LENGTH));
}
user.usr_user_name = name;
user.usr_project_name = "";
user.usr_org_name = "";
user.usr_sql_role_name = options.dpb_role_name;
user.usr_user_id = id;
user.usr_group_id = group;
user.usr_node_id = node_id;
2008-12-05 02:20:14 +01:00
if (wheel)
{
user.usr_flags |= USR_locksmith;
}
2008-01-16 10:29:37 +01:00
if (options.dpb_trusted_role)
{
user.usr_flags |= USR_trole;
}
}
2008-12-05 02:20:14 +01:00
static ISC_STATUS unwindAttach(const Exception& ex,
ISC_STATUS* userStatus,
thread_db* tdbb,
Attachment* attachment,
2008-01-16 10:29:37 +01:00
Database* dbb)
{
stuff_exception(userStatus, ex);
2008-06-24 14:02:29 +02:00
try
{
2008-06-24 14:02:29 +02:00
ThreadStatusGuard temp_status(tdbb);
2008-12-05 02:20:14 +01:00
2008-06-24 14:02:29 +02:00
dbb->dbb_flags &= ~DBB_being_opened;
2008-01-16 10:29:37 +01:00
2008-06-24 14:02:29 +02:00
if (attachment)
2008-01-16 10:29:37 +01:00
{
release_attachment(tdbb, attachment, userStatus);
2008-01-16 10:29:37 +01:00
}
2008-06-24 14:02:29 +02:00
if (dbb->checkHandle())
{
if (!dbb->dbb_attachments)
{
shutdown_database(dbb, true);
}
}
}
catch (const Exception&)
2008-06-24 14:02:29 +02:00
{
// no-op
2008-01-16 10:29:37 +01:00
}
return userStatus[1];
}
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM arg)
{
/**************************************
*
* s h u t d o w n _ t h r e a d
*
**************************************
*
* Functional description
* Shutdown the engine.
*
**************************************/
Semaphore* const semaphore = static_cast<Semaphore*>(arg);
ThreadContextHolder tdbb;
bool success = true;
try
{
MutexLockGuard guard(databases_mutex);
cancel_attachments();
Database* dbb_next;
for (Database* dbb = databases; dbb; dbb = dbb_next)
{
dbb_next = dbb->dbb_next;
if (!shutdown_dbb(tdbb, dbb))
{
success = false;
break;
}
}
Service::shutdownServices();
}
catch (const Exception&)
{
success = false;
}
if (success && semaphore)
{
semaphore->release();
}
return 0;
}
void thread_db::setTransaction(jrd_tra* val)
{
transaction = val;
traStat = val ? &val->tra_stats : RuntimeStatistics::getDummy();
}
void thread_db::setRequest(jrd_req* val)
{
request = val;
reqStat = val ? &val->req_stats : RuntimeStatistics::getDummy();
}
void JRD_autocommit_ddl(thread_db* tdbb, jrd_tra* transaction)
{
/**************************************
*
* J R D _ a u t o c o m m i t _ d d l
*
**************************************
*
* Functional description
*
**************************************/
// Perform an auto commit for autocommit transactions.
// This is slightly tricky. If the commit retain works,
// all is well. If TRA_commit() fails, we perform
// a rollback_retain(). This will backout the
// effects of the transaction, mark it dead and
// start a new transaction.
if (transaction->tra_flags & TRA_perform_autocommit)
{
transaction->tra_flags &= ~TRA_perform_autocommit;
try
{
TRA_commit(tdbb, transaction, true);
}
catch (const Exception&)
{
try
{
ThreadStatusGuard temp_status(tdbb);
TRA_rollback(tdbb, transaction, true, false);
}
catch (const Exception&)
{
// no-op
}
throw;
}
}
}
void JRD_ddl(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tra* transaction,
USHORT ddl_length, const UCHAR* ddl)
{
/**************************************
*
* J R D _ d d l
*
**************************************
*
* Functional description
*
**************************************/
DYN_ddl(attachment, transaction, ddl_length, ddl);
JRD_autocommit_ddl(tdbb, transaction);
}
void JRD_receive(thread_db* tdbb, jrd_req* request, USHORT msg_type, USHORT msg_length,
UCHAR* msg, SSHORT level
#ifdef SCROLLABLE_CURSORS
, USHORT direction, ULONG offset
#endif
)
{
/**************************************
*
* J R D _ r e c e i v e
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
verify_request_synchronization(request, level);
#ifdef SCROLLABLE_CURSORS
if (direction)
EXE_seek(tdbb, request, direction, offset);
#endif
EXE_receive(tdbb, request, msg_type, msg_length, msg, true);
check_autocommit(request, tdbb);
if (request->req_flags & req_warning)
request->req_flags &= ~req_warning;
}
void JRD_request_info(Jrd::thread_db*, jrd_req* request, SSHORT level, SSHORT item_length,
const SCHAR* items, SSHORT buffer_length, SCHAR* buffer)
{
/**************************************
*
* J R D _ r e q u e s t _ i n f o
*
**************************************
*
* Functional description
* Provide information on blob object.
*
**************************************/
verify_request_synchronization(request, level);
INF_request_info(request, items, item_length, buffer, buffer_length);
}
void JRD_start(Jrd::thread_db* tdbb, jrd_req* request, jrd_tra* transaction, SSHORT level)
{
/**************************************
*
* J R D _ s t a r t
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
if (level)
request = CMP_clone_request(tdbb, request, level, false);
EXE_unwind(tdbb, request);
EXE_start(tdbb, request, transaction);
check_autocommit(request, tdbb);
if (request->req_flags & req_warning)
request->req_flags &= ~req_warning;
}
void JRD_commit_transaction(thread_db* tdbb, jrd_tra** transaction)
{
/**************************************
*
* J R D _ c o m m i t _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Commit a transaction and keep the environment valid.
*
**************************************/
commit(tdbb, *transaction, false);
*transaction = NULL;
}
void JRD_commit_retaining(thread_db* tdbb, jrd_tra** transaction)
{
/**************************************
*
* J R D _ c o m m i t _ r e t a i n i n g
*
**************************************
*
* Functional description
* Commit a transaction.
*
**************************************/
commit(tdbb, *transaction, true);
}
void JRD_rollback_transaction(thread_db* tdbb, jrd_tra** transaction)
{
/**************************************
*
* J R D _ r o l l b a c k _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Abort a transaction.
*
**************************************/
rollback(tdbb, *transaction, false);
*transaction = NULL;
}
void JRD_rollback_retaining(thread_db* tdbb, jrd_tra** transaction)
{
/**************************************
*
* J R D _ r o l l b a c k _ r e t a i n i n g
*
**************************************
*
* Functional description
* Abort a transaction but keep the environment valid
*
**************************************/
rollback(tdbb, *transaction, true);
}
void JRD_start_and_send(thread_db* tdbb, jrd_req* request, jrd_tra* transaction, USHORT msg_type,
USHORT msg_length, SCHAR* msg, SSHORT level)
{
/**************************************
*
* J R D _ s t a r t _ a n d _ s e n d
*
**************************************
*
* Functional description
* Get a record from the host program.
*
**************************************/
///jrd_tra* transaction = find_transaction(tdbb, isc_req_wrong_db);
if (level)
request = CMP_clone_request(tdbb, request, level, false);
EXE_unwind(tdbb, request);
EXE_start(tdbb, request, transaction);
EXE_send(tdbb, request, msg_type, msg_length,
reinterpret_cast<UCHAR*>(msg));
check_autocommit(request, tdbb);
if (request->req_flags & req_warning)
request->req_flags &= ~req_warning;
}
void JRD_start_multiple(thread_db* tdbb, jrd_tra** tra_handle, USHORT count, TEB* vector)
{
/**************************************
*
* J R D _ s t a r t _ m u l t i p l e
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
jrd_tra* prior = NULL;
jrd_tra* transaction = NULL;
try
{
if (*tra_handle)
status_exception::raise(Arg::Gds(isc_bad_trans_handle));
if (count < 1 || count > MAX_DB_PER_TRANS)
{
status_exception::raise(Arg::Gds(isc_max_db_per_trans_allowed) << Arg::Num(MAX_DB_PER_TRANS));
}
if (vector == NULL)
{
status_exception::raise(Arg::Gds(isc_bad_teb_form));
}
for (TEB* v = vector; v < vector + count; v++)
{
Attachment* attachment = *v->teb_database;
AutoPtr<DatabaseContextHolder> dbbHolder;
if (attachment != tdbb->getAttachment())
{
validateHandle(tdbb, attachment);
dbbHolder = new DatabaseContextHolder(tdbb);
check_database(tdbb);
}
if ((v->teb_tpb_length < 0) ||
(v->teb_tpb_length > 0 && v->teb_tpb == NULL))
{
status_exception::raise(Arg::Gds(isc_bad_tpb_form));
}
transaction = TRA_start(tdbb, v->teb_tpb_length, v->teb_tpb);
transaction->tra_sibling = prior;
prior = transaction;
// run ON TRANSACTION START triggers
EXE_execute_db_triggers(tdbb, transaction,
jrd_req::req_trigger_trans_start);
}
*tra_handle = transaction;
}
catch (const Exception&)
{
if (prior)
{
ThreadStatusGuard temp_status(tdbb);
try
{
rollback(tdbb, prior, false);
}
catch (const Exception&)
{
}
}
throw;
}
}
void JRD_start_transaction(thread_db* tdbb, jrd_tra** transaction, SSHORT count, ...)
{
/**************************************
*
* J R D _ s t a r t _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Start a transaction.
*
**************************************/
if (count < 1 || USHORT(count) > MAX_DB_PER_TRANS)
{
status_exception::raise(Arg::Gds(isc_max_db_per_trans_allowed) << Arg::Num(MAX_DB_PER_TRANS));
}
HalfStaticArray<TEB, 16> tebs;
tebs.grow(count);
va_list ptr;
va_start(ptr, count);
for (TEB* teb_iter = tebs.begin(); teb_iter < tebs.end(); teb_iter++) {
teb_iter->teb_database = va_arg(ptr, Attachment**);
teb_iter->teb_tpb_length = va_arg(ptr, int);
teb_iter->teb_tpb = va_arg(ptr, UCHAR*);
}
va_end(ptr);
JRD_start_multiple(tdbb, transaction, count, tebs.begin());
}
void JRD_unwind_request(thread_db* tdbb, jrd_req* request, SSHORT level)
{
/**************************************
*
* J R D _ u n w i n d _ r e q u e s t
*
**************************************
*
* Functional description
* Unwind a running request. This is potentially nasty since it can
* be called asynchronously.
*
**************************************/
// Pick up and validate request level
verify_request_synchronization(request, level);
// Unwind request. This just tweaks some bits.
EXE_unwind(tdbb, request);
}
2008-03-08 22:20:26 +01:00
void JRD_compile(thread_db* tdbb,
Attachment* attachment,
jrd_req** req_handle,
SSHORT blr_length,
const UCHAR* blr,
2008-03-08 22:20:26 +01:00
USHORT string_length, const char* string,
USHORT dbginfo_length, const UCHAR* dbginfo)
{
/**************************************
*
2008-03-08 22:20:26 +01:00
* J R D _ c o m p i l e
*
**************************************
*
* Functional description
* Compile a request passing the SQL text and debug information.
*
**************************************/
if (*req_handle)
status_exception::raise(Arg::Gds(isc_bad_req_handle));
jrd_req* request = CMP_compile2(tdbb, blr, FALSE, dbginfo_length, dbginfo);
request->req_attachment = attachment;
request->req_request = attachment->att_requests;
attachment->att_requests = request;
request->req_sql_text.assign(string, string_length);
*req_handle = request;
}
namespace {
class DatabaseDirectoryList : public DirectoryList
{
private:
const PathName getConfigString() const {
return PathName(Config::getDatabaseAccess());
}
public:
explicit DatabaseDirectoryList(MemoryPool& p)
: DirectoryList(p)
2008-12-05 02:20:14 +01:00
{
initialize();
}
};
InitInstance<DatabaseDirectoryList> iDatabaseDirectoryList;
}
bool JRD_verify_database_access(const PathName& name)
{
/**************************************
*
2008-04-09 14:57:22 +02:00
* J R D _ v e r i f y _ d a t a b a s e _ a c c e s s
*
**************************************
*
* Functional description
* Verify 'name' against DatabaseAccess entry of firebird.conf.
*
**************************************/
return iDatabaseDirectoryList().isPathInList(name);
}