8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 10:43:03 +01:00
firebird-mirror/src/jrd/DatabaseSnapshot.cpp
2006-07-24 15:56:50 +00:00

498 lines
14 KiB
C++

/*
* The contents of this file are subject to the Initial
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* 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 Dmitry Yemanov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2006 Dmitry Yemanov <dimitr@users.sf.net>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include <process.h>
#include "firebird.h"
#include "ids.h"
#include "../jrd/gdsassert.h"
#include "../jrd/jrd.h"
#include "../jrd/cch.h"
#include "../jrd/ini.h"
#include "../jrd/os/pio.h"
#include "../jrd/req.h"
#include "../jrd/tra.h"
#include "../jrd/blb_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/os/pio_proto.h"
#include "../jrd/Relation.h"
#include "../jrd/RecordBuffer.h"
#include "../jrd/DatabaseSnapshot.h"
using namespace Jrd;
DatabaseSnapshot* DatabaseSnapshot::create(thread_db* tdbb)
{
SET_TDBB(tdbb);
jrd_tra* transaction = tdbb->tdbb_transaction;
fb_assert(transaction);
if (!transaction->tra_db_snapshot)
{
MemoryPool& pool = *transaction->tra_pool;
transaction->tra_db_snapshot =
FB_NEW(pool) DatabaseSnapshot(tdbb, pool, transaction);
}
return transaction->tra_db_snapshot;
}
RecordBuffer* DatabaseSnapshot::allocBuffer(thread_db* tdbb,
MemoryPool& pool,
int rel_id)
{
jrd_rel* relation = MET_lookup_relation_id(tdbb, rel_id, false);
fb_assert(relation);
MET_scan_relation(tdbb, relation);
fb_assert(relation->isVirtual());
Format* format = MET_current(tdbb, relation);
fb_assert(format);
RecordBuffer* buffer = FB_NEW(pool) RecordBuffer(pool, format);
RelationData data = {relation->rel_id, buffer};
snapshot.add(data);
return buffer;
}
DatabaseSnapshot::DatabaseSnapshot(thread_db* tdbb, MemoryPool& pool, jrd_tra* tran)
: transaction(tran), snapshot(pool)
{
Database* dbb = tdbb->tdbb_database;
// Database information
putDatabase(dbb, allocBuffer(tdbb, pool, rel_mon_database));
// Attachment information
RecordBuffer* att_buffer =
allocBuffer(tdbb, pool, rel_mon_attachments);
RecordBuffer* tra_buffer =
allocBuffer(tdbb, pool, rel_mon_transactions);
RecordBuffer* req_buffer =
allocBuffer(tdbb, pool, rel_mon_statements);
for (Attachment* attachment = dbb->dbb_attachments;
attachment; attachment = attachment->att_next)
{
putAttachment(attachment, att_buffer);
// Transaction information
for (jrd_tra* transaction = attachment->att_transactions;
transaction; transaction = transaction->tra_next)
{
putTransaction(transaction, tra_buffer);
}
// Request information
for (jrd_req* request = attachment->att_requests;
request; request = request->req_request)
{
// Ignore GDML requests
if (request->req_sql_text.hasData() || request->req_caller)
{
putRequest(request, req_buffer);
}
}
}
}
DatabaseSnapshot::~DatabaseSnapshot()
{
for (size_t i = 0; i < snapshot.getCount(); i++)
{
delete snapshot[i].data;
}
}
RecordBuffer* DatabaseSnapshot::getData(const jrd_rel* relation) const
{
fb_assert(relation);
for (size_t i = 0; i < snapshot.getCount(); i++)
{
if (snapshot[i].rel_id == relation->rel_id)
return snapshot[i].data;
}
return NULL;
}
void DatabaseSnapshot::clearRecord(Record* record)
{
// Initialize all fields to NULLs
memset(record->rec_data, 0, record->rec_length);
const size_t null_bytes = (record->rec_format->fmt_count + 7) >> 3;
memset(record->rec_data, 0xFF, null_bytes);
}
void DatabaseSnapshot::putField(Record* record, int id, const void* source)
{
fb_assert(record);
const Format* const format = record->rec_format;
fb_assert(format && id < format->fmt_count);
if (!source) {
SET_NULL(record, id);
return;
}
const dsc desc = format->fmt_desc[id];
UCHAR* const address = record->rec_data + (IPTR) desc.dsc_address;
switch (desc.dsc_dtype) {
case dtype_text:
{
const char* const string = (char*) source;
const size_t max_length = desc.dsc_length;
const size_t length = MIN(strlen(string), max_length);
memcpy(address, string, length);
memcpy(address + length, " ", max_length - length);
}
break;
case dtype_varying:
{
const char* const string = (char*) source;
const size_t max_length = desc.dsc_length - sizeof(USHORT);
const size_t length = MIN(strlen(string), max_length);
vary* varying = (vary*) address;
varying->vary_length = length;
memcpy(varying->vary_string, string, length);
}
break;
case dtype_short:
*(SSHORT*) address = *(SSHORT*) source;
break;
case dtype_long:
*(SLONG*) address = *(SLONG*) source;
break;
case dtype_int64:
*(SINT64*) address = *(SINT64*) source;
break;
case dtype_real:
*(float*) address = *(float*) source;
break;
case dtype_double:
*(double*) address = *(double*) source;
break;
case dtype_sql_date:
*(ISC_DATE*) address = *(ISC_DATE*) source;
break;
case dtype_sql_time:
*(ISC_TIME*) address = *(ISC_TIME*) source;
break;
case dtype_timestamp:
*(ISC_TIMESTAMP*) address = *(ISC_TIMESTAMP*) source;
break;
case dtype_blob:
{
thread_db* tdbb = JRD_get_thread_data();
bid blob_id;
blb* blob = BLB_create2(tdbb, transaction, &blob_id, 0, NULL);
const char* const string = (char*) source;
const size_t length = MIN(strlen(string), MAX_USHORT);
BLB_put_segment(tdbb, blob, (UCHAR*) string, length);
BLB_close(tdbb, blob);
*(bid*) address = blob_id;
}
break;
default:
fb_assert(false);
}
CLEAR_NULL(record, id);
}
void DatabaseSnapshot::putDatabase(Database* dbb, RecordBuffer* buffer)
{
fb_assert(dbb && buffer);
// Reload header
const PageSpace* const pageSpace =
dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
const jrd_file* const file = pageSpace->file;
PAG_header(file->fil_string, file->fil_length, true);
Record* record = buffer->getTempRecord();
// Initialize all fields to NULLs
clearRecord(record);
SSHORT temp_short;
// SLONG temp_long;
SINT64 temp_int64;
// database name or alias
putField(record, f_mon_db_name, dbb->dbb_database_name.c_str());
// page size
putField(record, f_mon_db_page_size, &dbb->dbb_page_size);
// major ODS version
putField(record, f_mon_db_ods_major, &dbb->dbb_ods_version);
// minor ODS version
putField(record, f_mon_db_ods_minor, &dbb->dbb_minor_version);
// oldest interesting transaction
putField(record, f_mon_db_oit, &dbb->dbb_oldest_transaction);
// oldest active transaction
putField(record, f_mon_db_oat, &dbb->dbb_oldest_active);
// oldest snapshot transaction
putField(record, f_mon_db_ost, &dbb->dbb_oldest_snapshot);
// next transaction
putField(record, f_mon_db_nt, &dbb->dbb_next_transaction);
// number of page buffers
putField(record, f_mon_db_page_bufs, &dbb->dbb_bcb->bcb_count);
// SQL dialect
temp_short = (dbb->dbb_flags & DBB_DB_SQL_dialect_3) ? 3 : 1;
putField(record, f_mon_db_dialect, &temp_short);
// shutdown mode
if (dbb->dbb_ast_flags & DBB_shutdown_full)
temp_short = shut_mode_full;
else if (dbb->dbb_ast_flags & DBB_shutdown_single)
temp_short = shut_mode_single;
else if (dbb->dbb_ast_flags & DBB_shutdown)
temp_short = shut_mode_multi;
else
temp_short = shut_mode_online;
putField(record, f_mon_db_shut_mode, &temp_short);
// sweep interval
putField(record, f_mon_db_sweep_int, &dbb->dbb_sweep_interval);
// read only flag
temp_short = (dbb->dbb_flags & DBB_read_only) ? 1 : 0;
putField(record, f_mon_db_read_only, &temp_short);
// forced writes flag
temp_short = (dbb->dbb_flags & DBB_force_write) ? 1 : 0;
putField(record, f_mon_db_forced_writes, &temp_short);
// reserve space flag
temp_short = (dbb->dbb_flags & DBB_no_reserve) ? 0 : 1;
putField(record, f_mon_db_res_space, &temp_short);
// creation date
putField(record, f_mon_db_created, &dbb->dbb_creation_date.value());
// database size
temp_int64 = PIO_act_alloc(dbb);
putField(record, f_mon_db_size, &temp_int64);
// current memory usage
temp_int64 = dbb->dbb_memory_stats.get_current_usage();
putField(record, f_mon_db_cur_mem, &temp_int64);
// maximum memory usage
temp_int64 = dbb->dbb_memory_stats.get_maximum_usage();
putField(record, f_mon_db_max_mem, &temp_int64);
// page reads
temp_int64 = dbb->dbb_reads;
putField(record, f_mon_db_page_reads, &temp_int64);
// page writes
temp_int64 = dbb->dbb_writes;
putField(record, f_mon_db_page_writes, &temp_int64);
// page fetches
temp_int64 = dbb->dbb_fetches;
putField(record, f_mon_db_page_fetches, &temp_int64);
// page marks
temp_int64 = dbb->dbb_marks;
putField(record, f_mon_db_page_marks, &temp_int64);
buffer->store(record);
}
void DatabaseSnapshot::putAttachment(Attachment* attachment, RecordBuffer* buffer)
{
fb_assert(attachment && buffer);
Record* record = buffer->getTempRecord();
// Initialize all fields to NULLs
clearRecord(record);
SSHORT temp_short;
SLONG temp_long;
// SINT64 temp_int64;
SSHORT state = att_s_idle;
for (jrd_tra* transaction = attachment->att_transactions;
transaction; transaction = transaction->tra_next)
{
if (transaction->tra_requests)
state = att_s_active;
}
// attachment id
putField(record, f_mon_att_id, &attachment->att_attachment_id);
// process id
temp_long = getpid();
putField(record, f_mon_att_server_pid, &temp_long);
// state
putField(record, f_mon_att_state, &state);
// attachment name
putField(record, f_mon_att_name, attachment->att_filename.c_str());
// user
putField(record, f_mon_att_user, attachment->att_user->usr_user_name);
// role
putField(record, f_mon_att_role, attachment->att_user->usr_sql_role_name);
// remote protocol
putField(record, f_mon_att_remote_proto, attachment->att_network_protocol.c_str());
// remote address
putField(record, f_mon_att_remote_addr, attachment->att_remote_address.c_str());
// charset
putField(record, f_mon_att_charset_id, &attachment->att_charset);
// timestamp
putField(record, f_mon_att_timestamp, &attachment->att_timestamp.value());
// garbage collection flag
temp_short = (attachment->att_flags & ATT_no_cleanup) ? 0 : 1;
putField(record, f_mon_att_gc, &temp_short);
buffer->store(record);
}
void DatabaseSnapshot::putTransaction(jrd_tra* transaction, RecordBuffer* buffer)
{
fb_assert(transaction && buffer);
Record* record = buffer->getTempRecord();
// Initialize all fields to NULLs
clearRecord(record);
SSHORT temp_short;
// SLONG temp_long;
// SINT64 temp_int64;
// transaction id
putField(record, f_mon_tra_id, &transaction->tra_number);
// attachment id
putField(record, f_mon_tra_att_id, &transaction->tra_attachment->att_attachment_id);
// state
temp_short = transaction->tra_requests ? tra_s_active : tra_s_idle;
putField(record, f_mon_tra_state, &temp_short);
// timestamp
putField(record, f_mon_tra_timestamp, &transaction->tra_timestamp.value());
// top transaction
putField(record, f_mon_tra_top, &transaction->tra_top);
// oldest transaction
putField(record, f_mon_tra_oit, &transaction->tra_oldest);
// oldest active transaction
putField(record, f_mon_tra_oat, &transaction->tra_oldest_active);
// isolation mode
if (transaction->tra_flags & TRA_degree3)
temp_short = iso_mode_consistency;
else if (transaction->tra_flags & TRA_read_committed)
temp_short = (transaction->tra_flags & TRA_rec_version) ?
iso_mode_rc_version : iso_mode_rc_no_version;
else
temp_short = iso_mode_concurrency;
putField(record, f_mon_tra_iso_mode, &temp_short);
// lock timeout
putField(record, f_mon_tra_lock_timeout, &transaction->tra_lock_timeout);
// read only flag
temp_short = (transaction->tra_flags & TRA_readonly) ? 1 : 0;
putField(record, f_mon_tra_read_only, &temp_short);
// autocommit flag
temp_short = (transaction->tra_flags & TRA_autocommit) ? 1 : 0;
putField(record, f_mon_tra_auto_commit, &temp_short);
// auto undo flag
temp_short = (transaction->tra_flags & TRA_no_auto_undo) ? 0 : 1;
putField(record, f_mon_tra_auto_undo, &temp_short);
buffer->store(record);
}
void DatabaseSnapshot::putRequest(jrd_req* request, RecordBuffer* buffer)
{
fb_assert(request && buffer);
Record* record = buffer->getTempRecord();
// Initialize all fields to NULLs
clearRecord(record);
SSHORT temp_short;
// SLONG temp_long;
// SINT64 temp_int64;
// request id
putField(record, f_mon_stmt_id, &request->req_id);
// attachment id
if (request->req_attachment) {
putField(record, f_mon_stmt_att_id,
&request->req_attachment->att_attachment_id);
}
// transaction id
if (request->req_transaction) {
putField(record, f_mon_stmt_tra_id,
&request->req_transaction->tra_number);
}
// timestamp
if (!request->req_timestamp.isEmpty()) {
putField(record, f_mon_stmt_timestamp, &request->req_timestamp.value());
}
// state
if (request->req_flags & req_active)
temp_short = stmt_s_active;
else if (request->req_flags & req_stall)
temp_short = stmt_s_stalled;
else
temp_short = stmt_s_idle;
putField(record, f_mon_stmt_state, &temp_short);
// caller
if (request->req_caller) {
putField(record, f_mon_stmt_caller_id, &request->req_caller->req_id);
}
// sql text
if (request->req_sql_text.hasData()) {
putField(record, f_mon_stmt_sql_text, request->req_sql_text.c_str());
}
// selects
putField(record, f_mon_stmt_rec_selects, &request->req_records_selected);
// inserts
putField(record, f_mon_stmt_rec_inserts, &request->req_records_inserted);
// updates
putField(record, f_mon_stmt_rec_updates, &request->req_records_updated);
// deletes
putField(record, f_mon_stmt_rec_deletes, &request->req_records_deleted);
buffer->store(record);
}