8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 07:23:03 +01:00
firebird-mirror/src/jrd/jrd.cpp

6226 lines
146 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/isc.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/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 "../jrd/iberr.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/iberr_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/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;
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;
Firebird::GlobalPtr<Firebird::Mutex> databases_mutex;
2008-01-16 10:29:37 +01:00
class EngineStartup
{
public:
static void init()
{
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() {}
};
2008-01-16 10:29:37 +01:00
Firebird::InitMutex<EngineStartup> engineStartup;
inline void validateHandle(thread_db* tdbb, Attachment* const attachment)
{
if (!attachment->checkHandle() ||
!attachment->att_database->checkHandle())
{
Firebird::status_exception::raise(isc_bad_db_handle, 0);
}
tdbb->setAttachment(attachment);
tdbb->setDatabase(attachment->att_database);
}
inline void validateHandle(thread_db* tdbb, jrd_tra* transaction)
{
if (!transaction->checkHandle())
Firebird::status_exception::raise(isc_bad_trans_handle, 0);
validateHandle(tdbb, transaction->tra_attachment);
tdbb->setTransaction(transaction);
}
inline void validateHandle(thread_db* tdbb, jrd_req* request)
{
if (!request->checkHandle())
Firebird::status_exception::raise(isc_bad_req_handle, 0);
validateHandle(tdbb, request->req_attachment);
}
2008-02-28 14:48:16 +01:00
inline void validateHandle(thread_db* tdbb, dsql_req* statement)
{
if (!statement->checkHandle())
Firebird::status_exception::raise(isc_bad_req_handle, 0);
validateHandle(tdbb, statement->req_dbb->dbb_attachment);
}
inline void validateHandle(thread_db* tdbb, blb* blob)
{
if (!blob->checkHandle())
Firebird::status_exception::raise(isc_bad_segstr_handle, 0);
validateHandle(tdbb, blob->blb_transaction);
validateHandle(tdbb, blob->blb_attachment);
}
inline void validateHandle(Service* service)
{
if (!service->checkHandle())
Firebird::status_exception::raise(isc_bad_svc_handle, 0);
}
class DatabaseContextHolder : public Jrd::ContextPoolHolder
{
public:
DatabaseContextHolder(thread_db* arg)
: Jrd::ContextPoolHolder(arg, arg->getDatabase()->dbb_permanent),
tdbb(arg)
{
Database* dbb = tdbb->getDatabase();
dbb->dbb_sync.lock();
++dbb->dbb_use_count;
}
~DatabaseContextHolder()
{
Database* dbb = tdbb->getDatabase();
if (dbb->checkHandle())
{
--dbb->dbb_use_count;
dbb->dbb_sync.unlock();
}
}
private:
// copying is prohibited
DatabaseContextHolder(const DatabaseContextHolder&);
DatabaseContextHolder& operator=(const DatabaseContextHolder&);
thread_db* tdbb;
};
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 Firebird::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;
}
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
}
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;
2001-05-23 15:26:42 +02:00
ULONG dpb_buffers;
USHORT dpb_debug;
USHORT dpb_verify;
USHORT dpb_sweep;
USHORT dpb_trace;
USHORT dpb_disable;
USHORT dpb_dbkey_scope;
USHORT dpb_page_size;
bool dpb_activate_shadow;
bool dpb_delete_shadow;
2001-05-23 15:26:42 +02:00
USHORT dpb_no_garbage;
USHORT dpb_shutdown;
SSHORT dpb_shutdown_delay;
USHORT dpb_online;
SSHORT dpb_force_write;
UCHAR dpb_set_force_write;
UCHAR dpb_no_reserve;
UCHAR dpb_set_no_reserve;
SSHORT dpb_interp;
USHORT 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;
ULONG dpb_flags; // to OR'd with dbb_flags
2008-01-16 10:29:37 +01:00
// here begin compound objects
// for constructor to work properly dpb_sys_user_name
// MUST be FIRST
Firebird::string dpb_sys_user_name;
Firebird::string dpb_user_name;
Firebird::string dpb_password;
Firebird::string dpb_password_enc;
Firebird::string dpb_role_name;
Firebird::string dpb_journal;
Firebird::string dpb_key;
Firebird::PathName dpb_lc_messages;
Firebird::string dpb_lc_ctype;
Firebird::PathName dpb_working_directory;
Firebird::string dpb_set_db_charset;
Firebird::string dpb_network_protocol;
Firebird::string dpb_remote_address;
2006-12-08 19:38:15 +01:00
Firebird::string dpb_trusted_login;
Firebird::PathName dpb_remote_process;
public:
DatabaseOptions()
{
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 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);
2008-01-16 10:29:37 +01:00
static void init_database_locks(thread_db*, Database*);
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 Firebird::PathName&, ISC_STATUS*);
2008-01-16 10:29:37 +01:00
static ISC_STATUS unwindAttach(const Firebird::Exception& ex,
ISC_STATUS* userStatus,
thread_db* tdbb,
Attachment* attachment,
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 Firebird::PathName&, bool);
static void prepare(thread_db*, jrd_tra*, USHORT, const UCHAR*);
static void release_attachment(thread_db*, Attachment*);
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(Firebird::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
//____________________________________________________________
//
// 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
if (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 GDS_CANCEL_OPERATION jrd8_cancel_operation
#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,
SSHORT _file_length,
const TEXT* _file_name,
Attachment** handle,
SSHORT dpb_length,
const UCHAR* dpb,
const TEXT* _expanded_filename)
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
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
}
Firebird::PathName file_name(_file_name,
_file_length ? _file_length : strlen(_file_name));
Firebird::PathName expanded_name(file_name);
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Resolve given alias name
const bool is_alias = ResolveDatabaseAlias(expanded_name, expanded_name);
if (is_alias)
{
ISC_expand_filename(expanded_name, false);
}
else
{
expanded_name = _expanded_filename;
}
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);
// Ccheck for correct credentials supplied
getUserInfo(userId, options);
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(user_status);
return user_status[1];
}
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;
try
{
// Unless we're already attached, do some initialization
dbb = init(tdbb, expanded_name, true);
}
catch (const Firebird::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
if (ISC_check_if_remote(expanded_name, true)) {
2003-11-08 17:40:17 +01:00
ERR_post(isc_unavailable, 0);
2003-04-06 11:08:58 +02:00
}
2001-05-23 15:26:42 +02: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)
{
ERR_post(isc_no_priv,
isc_arg_string, "direct",
isc_arg_string, "security database",
isc_arg_string,
ERR_string(file_name),
0);
}
2006-07-21 03:35:17 +02:00
// Worry about encryption key
2001-05-23 15:26:42 +02:00
if (dbb->dbb_decrypt) {
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))
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_no_priv,
isc_arg_string, "encryption",
isc_arg_string, "database",
isc_arg_string,
ERR_string(file_name),
0);
}
2001-05-23 15:26:42 +02: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
}
tdbb->setAttachment((attachment = FB_NEW(*dbb->dbb_permanent) Attachment(dbb)));
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)
{
Firebird::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
//
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
2001-05-23 15:26:42 +02:00
INI_init();
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
pageSpace->file =
2007-03-09 08:59:05 +01:00
PIO_open(dbb, expanded_name, options.dpb_trace != 0, file_name, false);
SHUT_init(tdbb);
2007-03-09 09:55:07 +01:00
PAG_header_init();
2001-05-23 15:26:42 +02:00
INI_init2();
PAG_init();
if (options.dpb_set_page_buffers) {
2001-05-23 15:26:42 +02:00
dbb->dbb_page_buffers = options.dpb_page_buffers;
}
2006-04-29 07:42:43 +02:00
CCH_init(tdbb, options.dpb_buffers);
// Initialize locks
init_database_locks(tdbb, dbb);
// 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(0);
PAG_header(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
2001-05-23 15:26:42 +02:00
SDW_init(options.dpb_activate_shadow,
options.dpb_delete_shadow);
/* dimitr: disabled due to unreliable behaviour of minor ODS upgrades
a) in the case of any failure it's impossible to attach the database
b) there's no way to handle failures properly, because upgrade is
being made in the context of system transaction which doesn't use
the backout logic
2001-05-23 15:26:42 +02:00
INI_update_database();
*/
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
Firebird::status_exception::raise(isc_bad_dpb_content, 0);
}
}
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) {
2003-11-08 17:40:17 +01:00
ERR_post(isc_lock_timeout, isc_arg_gds, isc_obj_in_use,
isc_arg_string,
ERR_string(file_name),
0);
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(isc_inv_client_dialect_specified, isc_arg_number,
options.dpb_sql_dialect,
isc_arg_gds, isc_valid_client_dialects,
2005-10-14 06:12:36 +02:00
isc_arg_string, "1, 2 or 3", 0);
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
2001-05-23 15:26:42 +02: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:
{
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);
for (Firebird::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
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(false, userId, tdbb);
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
ERR_post(isc_no_priv,
isc_arg_string, "shutdown or online",
isc_arg_string, "database",
isc_arg_string,
ERR_string(file_name),
0);
}
}
if (options.dpb_online)
{
if (!SHUT_online(tdbb, options.dpb_online))
{
if (user_status[1] != FB_SUCCESS)
2001-05-23 15:26:42 +02:00
ERR_punt();
else
2003-11-08 17:40:17 +01:00
ERR_post(isc_no_priv,
isc_arg_string, "shutdown or online",
isc_arg_string, "database",
isc_arg_string,
ERR_string(file_name),
0);
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(isc_shutdown, isc_arg_string,
ERR_string(file_name), 0);
}
else {
ERR_post(isc_att_shutdown, 0);
}
}
if (!attachment_succeeded) {
ERR_post(isc_shutdown, isc_arg_string,
ERR_string(file_name), 0);
}
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
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_shutinprog, isc_arg_string,
ERR_string(file_name), 0);
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(isc_shutdown, isc_arg_string,
ERR_string(file_name), 0);
}
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)) {
2003-11-08 17:40:17 +01:00
ERR_post(isc_bad_dpb_content, isc_arg_gds, isc_cant_validate, 0);
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(isc_bad_dpb_content,
isc_arg_gds, isc_cant_start_journal,
0);
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(isc_no_wal, 0);
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())
{
2001-05-23 15:26:42 +02:00
ERR_post(isc_adm_task_denied, 0);
}
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)
{
if (attachment->locksmith())
attachment->att_flags |= ATT_no_db_triggers;
else
ERR_post(isc_adm_task_denied, 0);
}
if (options.dpb_set_db_sql_dialect) {
2001-05-23 15:26:42 +02:00
PAG_set_db_SQL_dialect(dbb, options.dpb_set_db_sql_dialect);
}
if (options.dpb_sweep_interval != -1) {
2001-05-23 15:26:42 +02:00
PAG_sweep_interval(options.dpb_sweep_interval);
dbb->dbb_sweep_interval = options.dpb_sweep_interval;
}
if (options.dpb_set_force_write) {
2001-05-23 15:26:42 +02:00
PAG_set_force_write(dbb, options.dpb_force_write);
}
if (options.dpb_set_no_reserve) {
2001-05-23 15:26:42 +02:00
PAG_set_no_reserve(dbb, options.dpb_no_reserve);
}
if (options.dpb_set_page_buffers) {
2001-05-23 15:26:42 +02:00
PAG_set_page_buffers(options.dpb_page_buffers);
}
if (options.dpb_set_db_readonly) {
if (!CCH_exclusive(tdbb, LCK_EX, WAIT_PERIOD)) {
2003-11-08 17:40:17 +01:00
ERR_post(isc_lock_timeout, isc_arg_gds, isc_obj_in_use,
isc_arg_string,
ERR_string(file_name),
0);
}
PAG_set_db_readonly(dbb, 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
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 Firebird::Exception&)
{
if (!(dbb->dbb_flags & DBB_bugcheck) && transaction)
TRA_rollback(tdbb, transaction, false, false);
throw;
}
}
databases_mutex->leave();
*handle = attachment;
2001-12-24 03:51:06 +01:00
} // try
catch (const DelayFailedLogin& ex)
2008-01-16 10:29:37 +01:00
{
const ISC_STATUS s = unwindAttach(ex, user_status, tdbb, attachment, dbb);
2008-01-16 10:29:37 +01:00
ex.sleep();
return s;
}
catch (const Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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_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);
2001-05-23 15:26:42 +02:00
switch (option) {
case CANCEL_disable:
attachment->att_flags |= ATT_cancel_disable;
2001-05-23 15:26:42 +02:00
break;
case CANCEL_enable:
attachment->att_flags &= ~ATT_cancel_disable;
break;
2001-05-23 15:26:42 +02:00
case CANCEL_raise:
attachment->att_flags |= ATT_cancel_raise;
break;
2001-05-23 15:26:42 +02:00
default:
fb_assert(false);
}
}
catch (const Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
2008-02-28 14:48:16 +01:00
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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)
Firebird::status_exception::raise(isc_bad_segstr_handle, 0);
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 Firebird::Exception& ex)
{
Firebird::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,
USHORT _file_length,
const TEXT* _file_name,
Attachment** handle,
USHORT dpb_length,
const UCHAR* dpb,
USHORT db_type,
const TEXT* _expanded_filename)
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
Firebird::PathName file_name(_file_name,
_file_length ? _file_length : strlen(_file_name));
Firebird::PathName expanded_name(file_name);
2001-05-23 15:26:42 +02:00
2006-07-21 03:35:17 +02:00
// Resolve given alias name
const bool is_alias = ResolveDatabaseAlias(expanded_name, expanded_name);
if (is_alias)
{
ISC_expand_filename(expanded_name, false);
}
else
{
expanded_name = _expanded_filename;
}
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 Firebird::Exception& e)
{
e.stuff_exception(user_status);
return user_status[1];
}
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];
}
Database* dbb = NULL;
try
{
dbb = init(tdbb, expanded_name, false);
}
catch (const Firebird::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
if (ISC_check_if_remote(expanded_name, true))
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_unavailable, 0);
}
2001-05-23 15:26:42 +02: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
tdbb->setAttachment((attachment = FB_NEW(*dbb->dbb_permanent) Attachment(dbb)));
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:
// 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(isc_database_create_failed, isc_arg_string,
2004-03-16 18:35:03 +01:00
expanded_name.c_str(), isc_arg_gds, isc_inv_dialect_specified,
2001-05-23 15:26:42 +02:00
isc_arg_number, options.dpb_sql_dialect, isc_arg_gds,
isc_valid_db_dialects, isc_arg_string, "1 and 3", 0);
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
2001-05-23 15:26:42 +02:00
INI_init();
PAG_init();
initing_security = true;
2001-05-23 15:26:42 +02:00
SCL_init(true, userId, tdbb);
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 (Firebird::status_exception)
{
if (options.dpb_overwrite)
{
if (GDS_ATTACH_DATABASE(user_status, _file_length, _file_name, handle,
dpb_length, dpb, _expanded_filename) == 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
user_status[0] = 0;
allow_overwrite = true;
}
if (allow_overwrite)
{
// file is a database and the user (SYSDBA or owner) has right to overwrite
2006-05-22 00:07:35 +02:00
pageSpace->file =
2007-03-09 08:59:05 +01:00
PIO_create(dbb, expanded_name, options.dpb_overwrite, false, false);
}
else
{
ERR_post(isc_no_priv,
isc_arg_string, "overwrite",
isc_arg_string, "database",
isc_arg_string,
ERR_cstring(expanded_name.c_str()), 0);
}
}
else
throw;
}
2006-05-22 00:07:35 +02:00
const jrd_file* first_dbb_file = pageSpace->file;
2001-05-23 15:26:42 +02:00
if (options.dpb_set_page_buffers)
dbb->dbb_page_buffers = options.dpb_page_buffers;
2006-04-29 07:42:43 +02:00
CCH_init(tdbb, options.dpb_buffers);
// Initialize locks
init_database_locks(tdbb, dbb);
// 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_normal);
dbb->dbb_backup_manager->dbCreating = true;
2001-05-23 15:26:42 +02:00
PAG_format_header();
INI_init2();
PAG_format_log();
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(options.dpb_page_buffers);
if (options.dpb_set_no_reserve)
PAG_set_no_reserve(dbb, options.dpb_no_reserve);
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(isc_bad_shutdown_mode, isc_arg_string, ERR_string(file_name), 0);
if (options.dpb_shutdown) {
if (!SHUT_database(tdbb, options.dpb_shutdown,
options.dpb_shutdown_delay))
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_no_priv,
isc_arg_string, "shutdown or online",
isc_arg_string, "database",
isc_arg_string,
ERR_string(file_name), 0);
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
if (options.dpb_sweep_interval != -1) {
PAG_sweep_interval(options.dpb_sweep_interval);
dbb->dbb_sweep_interval = options.dpb_sweep_interval;
}
if (options.dpb_set_force_write)
PAG_set_force_write(dbb, options.dpb_force_write);
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(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))
2003-11-08 17:40:17 +01:00
ERR_post (isc_lock_timeout, isc_arg_gds, isc_obj_in_use,
isc_arg_string,
ERR_string (file_name),
0);
PAG_set_db_readonly (dbb, 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
first_dbb_file->fil_length);
#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;
databases_mutex->leave();
*handle = attachment;
2001-12-24 03:51:06 +01:00
} // try
catch (const DelayFailedLogin& ex)
2008-01-16 10:29:37 +01:00
{
const ISC_STATUS s = unwindAttach(ex, user_status, tdbb, attachment, dbb);
2008-01-16 10:29:37 +01:00
ex.sleep();
return s;
}
catch (const Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex) {
Firebird::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
Firebird::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 Firebird::Exception&)
{
dbb->dbb_flags &= ~DBB_not_in_use;
throw;
}
}
}
2001-12-24 03:51:06 +01:00
*handle = NULL;
SecurityDatabase::shutdown();
}
catch (const Firebird::Exception& ex)
{
Firebird::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
{
Firebird::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 Firebird::PathName& file_name = attachment->att_filename;
if (!attachment->locksmith())
2008-01-29 02:36:41 +01:00
{
ERR_post(isc_no_priv,
isc_arg_string, "drop",
isc_arg_string, "database",
isc_arg_string, ERR_cstring(file_name), 0);
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(isc_shutdown, isc_arg_string,
ERR_cstring(file_name), 0);
}
else {
ERR_post(isc_att_shutdown, 0);
}
}
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(isc_lock_timeout,
isc_arg_gds, isc_obj_in_use,
isc_arg_string, ERR_cstring(file_name), 0);
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(isc_no_meta_update, isc_arg_gds, isc_obj_in_use,
isc_arg_string, "DATABASE", 0);
}
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);
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);
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::deleteDbb(dbb);
2001-05-23 15:26:42 +02:00
if (err) {
user_status[0] = isc_arg_gds;
user_status[1] = isc_drdb_completed_with_errs;
user_status[2] = isc_arg_end;
}
}
catch (const Firebird::Exception& ex)
{
Firebird::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_GET_SEGMENT(ISC_STATUS * user_status,
blb** blob_handle,
USHORT * length,
USHORT buffer_length,
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;
user_status[2] = isc_arg_end;
if (blob->blb_flags & BLB_eof) {
user_status[1] = isc_segstr_eof;
}
else if (blob->blb_fragment_size) {
user_status[1] = isc_segment;
}
2001-05-23 15:26:42 +02:00
}
catch (const Firebird::Exception& ex)
{
Firebird::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);
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 Firebird::Exception& ex)
{
Firebird::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)
Firebird::status_exception::raise(isc_bad_segstr_handle, 0);
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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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];
}
*id = EVENT_que(user_status,
attachment->att_event_session,
lock->lck_length,
(const TEXT*) &lock->lck_key, length, items, ast, arg);
}
catch (const Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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)
Firebird::status_exception::raise(isc_bad_trans_handle, 0);
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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
2008-02-28 14:48:16 +01:00
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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);
EXE_send(tdbb, request, msg_type, msg_length,
2003-10-07 12:43:20 +02:00
reinterpret_cast<UCHAR*>(msg));
check_autocommit(request, tdbb);
if (request->req_flags & req_warning)
request->req_flags &= ~req_warning;
}
catch (const Firebird::Exception& ex)
{
Firebird::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,
USHORT service_length,
2003-12-03 09:19:24 +01:00
const TEXT* service_name,
Service** svc_handle,
USHORT spb_length,
2003-12-03 09:19:24 +01:00
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)
Firebird::status_exception::raise(isc_bad_svc_handle, 0);
2001-05-23 15:26:42 +02:00
*svc_handle = new Service(service_length, service_name, spb_length,
reinterpret_cast<const UCHAR*>(spb));
}
catch (const DelayFailedLogin& ex)
2008-01-16 10:29:37 +01:00
{
ex.sleep();
Firebird::stuff_exception(user_status, ex);
2008-01-16 10:29:37 +01:00
}
catch (const Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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);
// 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 Firebird::Exception& ex)
{
Firebird::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));
if (service->getStatus()[1]) {
ISC_STATUS* svc_status = service->getStatus();
2003-04-10 08:49:16 +02:00
ISC_STATUS* tdbb_status = tdbb->tdbb_status_vector;
while (*svc_status) {
*tdbb_status++ = *svc_status++;
}
*tdbb_status = isc_arg_end;
}
2001-05-23 15:26:42 +02:00
}
catch (const Firebird::Exception& ex)
{
Firebird::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);
JRD_start_and_send(tdbb, request, transaction, msg_type, msg_length, msg, level);
}
catch (const Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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)
{
Firebird::Semaphore shutdown_semaphore;
ThreadStart::start(shutdown_thread, &shutdown_semaphore, THREAD_medium, 0, 0);
if (!shutdown_semaphore.tryEnter(0, timeout))
{
const char errorMsg[] = "Server shutdown is still in progress after the specified timeout";
Firebird::status_exception::raise(isc_random,
isc_arg_string, (ISC_STATUS) errorMsg,
0);
}
}
else
{
shutdown_thread(NULL);
}
}
catch (const Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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)
{
Firebird::status_exception::raise(isc_max_db_per_trans_allowed,
isc_arg_number, MAX_DB_PER_TRANS,
0);
}
Firebird::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 Firebird::Exception& ex)
{
Firebird::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,
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
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);
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) )
{
if ((int) (IPTR) node->nod_arg[e_msg_number] == 0)
{
in_message = node;
}
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
}
2008-01-29 11:11:52 +01:00
catch (const Firebird::Exception&)
{
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)
{
ERR_post(isc_port_len,
isc_arg_number, (SLONG) in_msg_length,
isc_arg_number, (SLONG) len,
0);
}
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) {
2003-11-08 17:40:17 +01:00
ERR_post(isc_port_len,
isc_arg_number, (SLONG) out_msg_length,
2003-11-08 17:40:17 +01:00
isc_arg_number, (SLONG) len, 0);
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 Firebird::Exception& ex)
2001-12-24 03:51:06 +01:00
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
2001-05-23 15:26:42 +02:00
{
Firebird::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)
Firebird::status_exception::raise(isc_bad_req_handle, 0);
Attachment* const attachment = *db_handle;
validateHandle(tdbb, attachment);
DatabaseContextHolder dbbHolder(tdbb);
check_database(tdbb);
*stmt_handle = DSQL_allocate_statement(tdbb, attachment);
}
catch (const Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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 Firebird::Exception& ex)
{
Firebird::stuff_exception(user_status, ex);
}
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) {
2006-07-01 10:50:21 +02:00
char buff[MAXPATHLEN + 25];
2001-05-23 15:26:42 +02:00
sprintf(buff, "Failed to open %s\n", fname);
gds__log(buff, 0);
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 Firebird::PathName& file_name = attachment->att_filename;
if (punt) {
CCH_unwind(tdbb, false);
ERR_post(isc_shutdown, isc_arg_string,
ERR_cstring(file_name), 0);
}
else {
ISC_STATUS* status = tdbb->tdbb_status_vector;
*status++ = isc_arg_gds;
*status++ = isc_shutdown;
*status++ = isc_arg_string;
*status++ = (ISC_STATUS) ERR_cstring(file_name);
*status++ = isc_arg_end;
return true;
}
}
else if (attachment->att_flags & ATT_shutdown &&
!(tdbb->tdbb_flags & TDBB_shutdown_manager))
{
2001-05-23 15:26:42 +02:00
if (punt) {
CCH_unwind(tdbb, false);
ERR_post(isc_att_shutdown, 0);
2001-05-23 15:26:42 +02:00
}
else {
ISC_STATUS* status = tdbb->tdbb_status_vector;
2001-05-23 15:26:42 +02:00
*status++ = isc_arg_gds;
*status++ = isc_att_shutdown;
2001-05-23 15:26:42 +02:00
*status++ = isc_arg_end;
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;
if (punt) {
CCH_unwind(tdbb, false);
ERR_post(isc_cancelled, 0);
}
else {
ISC_STATUS* status = tdbb->tdbb_status_vector;
*status++ = isc_arg_gds;
*status++ = isc_cancelled;
*status++ = isc_arg_end;
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(isc_cancelled, 0);
}
else {
ISC_STATUS* status = tdbb->tdbb_status_vector;
*status++ = isc_arg_gds;
*status++ = isc_cancelled;
*status++ = isc_arg_end;
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) ?
(quantum ? quantum : QUANTUM) : 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)
Firebird::status_exception::raise(isc_bad_db_handle, 0);
if (dbb->dbb_flags & DBB_bugcheck)
2008-01-16 10:29:37 +01:00
{
static const char string[] = "can't continue after bugcheck";
Firebird::status_exception::raise(isc_bug_check,
isc_arg_string, string,
0);
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 Firebird::PathName& filename = attachment->att_filename;
Firebird::status_exception::raise(isc_shutdown,
isc_arg_string, ERR_cstring(filename),
0);
}
else
{
Firebird::status_exception::raise(isc_att_shutdown, 0);
}
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;
Firebird::status_exception::raise(isc_cancelled, 0);
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;
Firebird::status_exception::raise(isc_cancelled, 0);
}
}
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))
{
IBERR_build_status(status, isc_io_error,
2003-11-08 17:40:17 +01:00
isc_arg_string, "unlink",
isc_arg_string,
2003-01-18 17:31:23 +01:00
ERR_cstring(file->fil_string),
2001-05-23 15:26:42 +02:00
isc_arg_gds, isc_io_delete_err,
SYS_ERR, errno,
0);
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
Firebird::status_exception::raise(error_code, 0);
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());
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
2003-11-08 17:40:17 +01:00
ERR_post(isc_bad_dpb_content,
isc_arg_gds, isc_charset_not_found,
isc_arg_string, ERR_cstring(options->dpb_lc_ctype),
2001-05-23 15:26:42 +02:00
0);
}
}
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)
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_bad_dpb_form, 0);
}
Firebird::ClumpletReader rdr(Firebird::ClumpletReader::Tagged, dpb, dpb_length);
2001-05-23 15:26:42 +02:00
if (rdr.getBufferTag() != isc_dpb_version1)
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_bad_dpb_form, isc_arg_gds, isc_wrodpbver, 0);
}
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
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_bad_dpb_content, 0);
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)
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_bad_dpb_content, 0);
}
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:
dpb_debug = (USHORT) 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:
dpb_trace = (USHORT) 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.
2003-11-08 17:40:17 +01:00
ERR_post(isc_num_old_files, 0);
// 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
2003-11-08 17:40:17 +01:00
ERR_post(isc_uns_ext, isc_arg_gds, isc_random,
isc_arg_string, "Encryption not supported", 0);
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_disable_journal:
dpb_disable = 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 = (SSHORT) rdr.getInt();
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 = (UCHAR) rdr.getInt();
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:
{
Firebird::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:
{
Firebird::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:
{
Firebird::ClumpletReader address_stack(Firebird::ClumpletReader::UnTagged,
rdr.getBytes(), rdr.getClumpLength());
while (!address_stack.isEof()) {
if (address_stack.getClumpTag() != isc_dpb_address) {
address_stack.moveNext();
continue;
}
Firebird::ClumpletReader address(Firebird::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;
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(isc_bad_dpb_form, 0);
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.
*
**************************************/
ISC_STATUS* vector = user_status;
2003-11-08 17:40:17 +01:00
*vector++ = isc_arg_gds;
2001-05-23 15:26:42 +02:00
*vector++ = code;
2003-11-08 17:40:17 +01:00
*vector = isc_arg_end;
2001-05-23 15:26:42 +02:00
return code;
}
static Database* init(thread_db* tdbb,
const Firebird::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.
*
**************************************/
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
databases_mutex->enter();
Database* dbb = NULL;
2001-05-23 15:26:42 +02:00
try
{
// 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)
2001-05-23 15:26:42 +02:00
{
if (!(dbb->dbb_flags & (DBB_bugcheck | DBB_not_in_use)) &&
(dbb->dbb_filename == expanded_filename))
{
if (attach_flag)
return dbb;
2008-02-14 08:31:25 +01:00
ERR_post(isc_no_meta_update, isc_arg_gds, isc_obj_in_use,
isc_arg_string, "DATABASE", 0);
}
2001-05-23 15:26:42 +02:00
}
#endif
2001-12-24 03:51:06 +01:00
Firebird::MemoryStats temp_stats;
MemoryPool* perm = MemoryPool::createPool(NULL, temp_stats);
dbb = Database::newDbb(perm);
perm->setStatsGroup(dbb->dbb_memory_stats);
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, perm);
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
GenerateGuid(&dbb->dbb_guid);
// set a garbage collection policy
if ((dbb->dbb_flags & (DBB_gc_cooperative | DBB_gc_background)) == 0)
{
Firebird::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;
}
else
fb_assert(false);
}
}
// Initialize a number of subsystems
2001-05-23 15:26:42 +02:00
TRA_init(tdbb);
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) {
Firebird::string encrypt_entrypoint(ENCRYPT);
Firebird::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);
}
catch (const Firebird::Exception&)
{
databases_mutex->leave();
throw;
}
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
return dbb;
2001-05-23 15:26:42 +02:00
}
2006-07-19 08:19:56 +02:00
static void init_database_locks(thread_db* tdbb, Database* dbb)
{
/**************************************
*
* i n i t _ d a t a b a s e _ l o c k s
*
**************************************
*
* Functional description
* Initialize secondary database locks.
*
**************************************/
SET_TDBB(tdbb);
fb_assert(dbb);
// Lock shared by all dbb owners, used to signal other processes
// to dump their monitoring data and synchronize operations
Lock* 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 = reinterpret_cast<blk*>(dbb);
lock->lck_ast = DatabaseSnapshot::blockingAst;
LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT);
// Lock that identifies a dbb instance
const size_t key_length = sizeof(FB_GUID);
lock = FB_NEW_RPT(*dbb->dbb_permanent, key_length) Lock();
dbb->dbb_instance_lock = lock;
lock->lck_type = LCK_instance;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_length = key_length;
memcpy(lock->lck_key.lck_string, &dbb->dbb_guid, key_length);
lock->lck_dbb = dbb;
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)
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;
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
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
if (attachment->att_compatibility_table)
2001-12-24 03:51:06 +01: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;
}
}
}
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(Database* dbb) :
att_database(dbb),
att_lock_owner_id(Database::getLockOwnerId()),
att_lc_messages(*dbb->dbb_permanent),
att_working_directory(*dbb->dbb_permanent),
att_filename(*dbb->dbb_permanent),
att_context_vars(*dbb->dbb_permanent),
att_network_protocol(*dbb->dbb_permanent),
att_remote_address(*dbb->dbb_permanent),
att_remote_process(*dbb->dbb_permanent),
att_dsql_cache(*dbb->dbb_permanent)
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(),
// but for special ones like GC should be done also in dtor -
// they do not (and should not) call release_attachment().
// It's no danger calling detachLocksFromAttachment()
// once more here because it nulls att_long_locks.
// AP 2007
detachLocksFromAttachment(this);
}
PreparedStatement* Attachment::prepareStatement(thread_db* tdbb, Firebird::MemoryPool& pool,
jrd_tra* transaction, const Firebird::string& text)
{
return new PreparedStatement(tdbb, pool, this, transaction, text);
}
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 Firebird::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 Firebird::Exception& ex)
{
Firebird::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 Firebird::Exception& ex)
{
Firebird::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)
Firebird::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);
if (dbb->dbb_backup_manager)
dbb->dbb_backup_manager->shutdown(tdbb);
2001-05-23 15:26:42 +02:00
if (dbb->dbb_instance_lock)
LCK_release(tdbb, dbb->dbb_instance_lock);
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);
// 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)
2001-05-23 15:26:42 +02:00
for (d_ptr = &databases; *(d_ptr); d_ptr = &(*d_ptr)->dbb_next) {
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);
2008-01-29 11:36:49 +01:00
Database::deleteDbb(dbb);
2001-05-23 15:26:42 +02:00
}
}
static void strip_quotes(Firebird::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
*
**************************************/
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 != Firebird::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 = {0};
tdbb->tdbb_status_vector = temp_status;
try
{
// purge attachment, rollback any open transactions
purge_attachment(tdbb, temp_status, attach, true);
}
catch (const Firebird::Exception&)
{
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;
Firebird::HalfStaticArray<Firebird::PathName, 8> dbFiles;
try
{
Firebird::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 Firebird::TempDirectoryList dirList;
for (size_t i = 0; i < dirList.getCount(); i++) {
const Firebird::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 Firebird::Exception&)
{
// Here we ignore possible errors from databases_mutex.
// 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
* 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-01-16 10:29:37 +01:00
if (!(dbb->dbb_flags & DBB_bugcheck))
{
2008-01-16 10:29:37 +01:00
ISC_STATUS* const original_status = tdbb->tdbb_status_vector;
2006-12-13 15:28:57 +01:00
try
{
if (!(attachment->att_flags & ATT_no_db_triggers) &&
!(attachment->att_flags & ATT_shutdown))
{
ISC_STATUS_ARRAY temp_status = {0};
tdbb->tdbb_status_vector = temp_status;
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 Firebird::Exception&)
{
if (dbb->dbb_flags & DBB_bugcheck)
throw;
try
{
if (transaction)
TRA_rollback(tdbb, transaction, false, false);
}
catch (const Firebird::Exception&)
{
if (dbb->dbb_flags & DBB_bugcheck)
throw;
}
}
}
}
catch (const Firebird::Exception&)
{
2006-12-13 15:28:57 +01:00
tdbb->tdbb_status_vector = original_status;
attachment->att_flags |= ATT_shutdown;
throw;
}
2006-12-13 15:28:57 +01:00
tdbb->tdbb_status_vector = original_status;
}
const ULONG att_flags = attachment->att_flags;
attachment->att_flags |= ATT_shutdown;
if (!(dbb->dbb_flags & DBB_bugcheck))
{
// Check for any pending transactions
unsigned int count = purge_transactions(tdbb, attachment, force_flag, att_flags);
2001-05-23 15:26:42 +02:00
if (count)
{
ERR_post(isc_open_trans, isc_arg_number, (SLONG) count, 0);
2001-05-23 15:26:42 +02:00
}
SORT_shutdown(attachment);
}
// Unlink attachment from database
2001-05-23 15:26:42 +02:00
release_attachment(tdbb, attachment);
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))
{
2003-11-10 10:16:38 +01:00
// There are still attachments so do a partial shutdown
2001-05-23 15:26:42 +02:00
// CMP_release() advances the pointer before the deallocation.
jrd_req* request;
2001-12-24 03:51:06 +01:00
while ( (request = attachment->att_requests) ) {
2001-05-23 15:26:42 +02:00
CMP_release(tdbb, request);
}
SCL_release_all(attachment->att_security_classes);
delete attachment->att_user;
2001-12-24 03:51:06 +01:00
delete attachment;
2001-05-23 15:26:42 +02:00
}
else
{
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 Firebird::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]))
{
2003-11-08 17:40:17 +01:00
ERR_post(isc_req_sync, 0);
}
}
}
/**
verify_database_name
@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 Firebird::PathName& name, ISC_STATUS* status)
{
2005-02-24 13:24:38 +01:00
// Check for security2.fdb
static TEXT SecurityNameBuffer[MAXPATHLEN] = "";
static Firebird::PathName ExpandedSecurityNameBuffer(*getDefaultMemoryPool());
static Firebird::Mutex mutex;
Firebird::MutexLockGuard guard(mutex);
if (! SecurityNameBuffer[0]) {
SecurityDatabase::getPath(SecurityNameBuffer);
ExpandedSecurityNameBuffer = SecurityNameBuffer;
ISC_expand_filename(ExpandedSecurityNameBuffer, false);
}
if (name == SecurityNameBuffer || name == ExpandedSecurityNameBuffer)
return vdnSecurity;
// Check for .conf
if (!ISC_verify_database_access(name)) {
2003-11-08 17:40:17 +01:00
status[0] = isc_arg_gds;
status[1] = isc_conf_access_denied;
status[2] = isc_arg_string;
// CVC: Using STATUS to hold pointer to literal string!
2003-10-07 12:43:20 +02:00
status[3] = reinterpret_cast<ISC_STATUS>("database");
2003-11-08 17:40:17 +01:00
status[4] = isc_arg_string;
status[5] = (ISC_STATUS)(U_IPTR) ERR_cstring(name.c_str());
2003-11-08 17:40:17 +01:00
status[6] = isc_arg_end;
return vdnFail;
}
return vdnOk;
}
/**
getUserInfo
@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;
Firebird::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-01-16 10:29:37 +01:00
if (options.dpb_user_name.isEmpty())
{
2008-01-16 13:29:45 +01:00
wheel = ISC_get_user(&name, &id, &group,
2008-01-16 10:29:37 +01:00
options.dpb_sys_user_name.nullStr());
}
2008-01-16 10:29:37 +01:00
if (options.dpb_user_name.hasData() || (id == -1))
{
2008-01-16 10:29:37 +01:00
Firebird::string remote = options.dpb_network_protocol +
(options.dpb_network_protocol.isEmpty() || options.dpb_remote_address.isEmpty() ? "" : "/") +
options.dpb_remote_address;
SecurityDatabase::verifyUser(name,
options.dpb_user_name.nullStr(),
options.dpb_password.nullStr(),
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
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)
{
Firebird::status_exception::raise(isc_long_login,
isc_arg_number, name.length(),
isc_arg_number, USERNAME_LENGTH,
0);
}
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;
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;
}
}
static ISC_STATUS unwindAttach(const Firebird::Exception& ex,
ISC_STATUS* userStatus,
thread_db* tdbb,
Attachment* attachment,
Database* dbb)
{
ISC_STATUS_ARRAY temp_status = {0};
2008-01-16 10:29:37 +01:00
ISC_STATUS* const save_status = tdbb->tdbb_status_vector;
tdbb->tdbb_status_vector = temp_status;
try
{
dbb->dbb_flags &= ~DBB_being_opened;
release_attachment(tdbb, attachment);
2008-01-16 10:29:37 +01:00
if (dbb->checkHandle())
2008-01-16 10:29:37 +01:00
{
if (!dbb->dbb_attachments)
{
shutdown_database(dbb, true);
}
else if (attachment)
{
delete attachment;
}
}
}
catch (const Firebird::Exception&)
2008-02-19 03:34:55 +01:00
{
// no-op
}
2008-01-16 10:29:37 +01:00
tdbb->tdbb_status_vector = save_status;
databases_mutex->leave();
2008-01-16 10:29:37 +01:00
Firebird::stuff_exception(userStatus, ex);
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.
*
**************************************/
Firebird::Semaphore* const semaphore = static_cast<Firebird::Semaphore*>(arg);
ThreadContextHolder tdbb;
bool success = true;
try
{
Firebird::MutexLockGuard guard(databases_mutex);
Database* dbb_next;
for (Database* dbb = databases; dbb; dbb = dbb_next)
{
dbb_next = dbb->dbb_next;
if (!shutdown_dbb(tdbb, dbb))
{
success = false;
break;
}
}
}
catch (const Firebird::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_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);
// 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 Firebird::Exception&)
{
ISC_STATUS* const old_status = tdbb->tdbb_status_vector;
ISC_STATUS_ARRAY temp_status = {0};
tdbb->tdbb_status_vector = temp_status;
try
{
TRA_rollback(tdbb, transaction, true, false);
}
catch (const Firebird::Exception&)
{
// no-op
}
tdbb->tdbb_status_vector = old_status;
throw;
}
}
}
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);
}
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);
}
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)
Firebird::status_exception::raise(isc_bad_trans_handle, 0);
if (count < 1 || count > MAX_DB_PER_TRANS)
{
Firebird::status_exception::raise(isc_max_db_per_trans_allowed,
isc_arg_number, MAX_DB_PER_TRANS,
0);
}
if (vector == NULL)
{
Firebird::status_exception::raise(isc_bad_teb_form, isc_arg_end);
}
for (TEB* v = vector; v < vector + count; v++)
{
Attachment* attachment = *v->teb_database;
Firebird::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))
{
Firebird::status_exception::raise(isc_bad_tpb_form, isc_arg_end);
}
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 Firebird::Exception&)
{
if (prior)
{
ISC_STATUS_ARRAY temp_status = {0};
ISC_STATUS* const old_status = tdbb->tdbb_status_vector;
tdbb->tdbb_status_vector = temp_status;
try
{
rollback(tdbb, prior, false);
}
catch (const Firebird::Exception&)
{
}
tdbb->tdbb_status_vector = old_status;
}
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)
{
Firebird::status_exception::raise(isc_max_db_per_trans_allowed,
isc_arg_number, MAX_DB_PER_TRANS,
0);
}
Firebird::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)
Firebird::status_exception::raise(isc_bad_req_handle, 0);
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;
}