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

1900 lines
49 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: pag.cpp
* DESCRIPTION: Page level ods manager
*
* 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.02.15 Sean Leyne - Code Cleanup, removed obsolete ports:
* - EPSON, DELTA, IMP, NCR3000, M88K
* - HP9000 s300 and Apollo
*
* 2002.10.27 Sean Leyne - Completed removal of obsolete "DG_X86" port
* 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port
* 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix" port
* 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix/MIPS" port
*
* 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "SGI" port
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
*/
/*
* Modified by: Patrick J. P. Griffin
* Date: 11/29/2000
* Problem: Bug 116733 Too many generators corrupt database.
* DPM_gen_id was not calculating page and offset correctly.
* Change: Caculate pgc_gpg, number of generators per page,
* for use in DPM_gen_id.
*/
/* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
*
* 2001.08.07 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, second attempt
*
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "MAC" and "MAC_CP" defines
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "Apollo" port
*
*/
#include "firebird.h"
#include "../jrd/common.h"
#include <stdio.h>
#include <string.h>
#include "../jrd/jrd.h"
#include "../jrd/pag.h"
#include "../jrd/ods.h"
#include "../jrd/os/pio.h"
#include "../jrd/ibase.h"
#include "../jrd/gdsassert.h"
#include "../jrd/license.h"
#include "../jrd/lck.h"
#include "../jrd/sdw.h"
#include "../jrd/cch.h"
#include "../jrd/tra.h"
#ifdef VIO_DEBUG
#include "../jrd/vio_debug.h"
#include "../jrd/all.h"
#endif
#include "../jrd/all_proto.h"
#include "../jrd/cch_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/os/pio_proto.h"
#include "../jrd/thd.h"
#include "../jrd/isc_f_proto.h"
using namespace Jrd;
using namespace Ods;
static void find_clump_space(SLONG, WIN*, pag**, USHORT, SSHORT, const UCHAR*,
USHORT);
static bool find_type(SLONG, WIN*, pag**, USHORT, USHORT, UCHAR**,
const UCHAR**);
#define ERR_POST_IF_DATABASE_IS_READONLY(dbb) {if (dbb->dbb_flags & DBB_read_only) ERR_post (isc_read_only_database, 0);}
/* Class definitions
1 Apollo 68K, Dn 10K
2 Sun 68k, Sun Sparc, HP 9000/300, MAC AUX, IMP, DELTA, NeXT, UNIXWARE, DG_X86
3 Sun 386i
4 VMS
5 Ultrix/VAX
6 Ultrix/MIPS
7 HP 900/800 (precision)
8 NetWare
9 MAC-OS
10 IBM RS/6000
11 DG AViiON
12 HP MPE/XL
13 Silicon Grpahics/IRIS
14 Cray
15 Dec OSF/1
16 NT -- post 4.0 (delivered 4.0 as class 8)
17 OS/2
18 Windows 16 bit
19 LINUX on Intel series
20 LINUX on sparc systems
21 FreeBSD/i386
22 NetBSD/i386
23 Darwin/PowerPC
24 LINUX on AMD64 systems
*/
#ifdef sun
#ifdef i386
const SSHORT CLASS = 3;
#else
const SSHORT CLASS = 2;
#endif
#endif
#ifdef hpux
const SSHORT CLASS = 7;
#endif
#ifdef VMS
const SSHORT CLASS = 4;
#endif
#ifdef AIX
const SSHORT CLASS = 10;
#endif
#ifdef AIX_PPC
const SSHORT CLASS = 10;
#endif
#ifdef WIN_NT
const SSHORT CLASS = 16;
#endif
#ifdef SINIXZ
const SSHORT CLASS = 19;
#endif
#ifdef LINUX
#ifdef i386
const SSHORT CLASS = 19;
#endif
#ifdef i586
const SSHORT CLASS = 19;
#endif
#ifdef sparc
const SSHORT CLASS = 20;
#endif
#endif
#ifdef FREEBSD
const SSHORT CLASS = 21;
#endif
#ifdef NETBSD
const SSHORT CLASS = 22;
#endif
#ifdef DARWIN
const SSHORT CLASS = 23;
#endif
#if defined LINUX && defined AMD64
const SSHORT CLASS = 24;
#endif
// CVC: Since nobody checks the result from this function (strange!), I changed
// bool to void as the return type but left the result returned as comment.
void PAG_add_clump(
SLONG page_num,
USHORT type,
USHORT len, const UCHAR* entry, USHORT mode, USHORT must_write)
{
/***********************************************
*
* P A G _ a d d _ c l u m p
*
***********************************************
*
* Functional description
* Adds a clump to log/header page.
* mode
* 0 - add CLUMP_ADD
* 1 - replace CLUMP_REPLACE
* 2 - replace only! CLUMP_REPLACE_ONLY
* returns
* true - modified page
* false - nothing done => nobody checks this function's result.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
pag* page;
header_page* header = 0;
log_info_page* logp = 0;
USHORT* end_addr;
WIN window(page_num);
if (page_num == HEADER_PAGE) {
page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
header = (header_page*) page;
end_addr = &header->hdr_end;
}
else {
page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
logp = (log_info_page*) page;
end_addr = &logp->log_end;
}
UCHAR* entry_p;
const UCHAR* clump_end;
while (mode != CLUMP_ADD) {
const bool found =
find_type(page_num, &window, &page, LCK_write, type,
&entry_p, &clump_end);
/* If we did'nt find it and it is REPLACE_ONLY, return */
if ((!found) && (mode == CLUMP_REPLACE_ONLY)) {
CCH_RELEASE(tdbb, &window);
return; //false;
}
/* If not found, just go and add the entry */
if (!found)
break;
/* if same size, overwrite it */
if (entry_p[1] == len) {
entry_p += 2;
const UCHAR* r = entry;
USHORT l = len;
if (l) {
if (must_write)
CCH_MARK_MUST_WRITE(tdbb, &window);
else
CCH_MARK(tdbb, &window);
do {
*entry_p++ = *r++;
} while (--l);
}
CCH_RELEASE(tdbb, &window);
return; // true;
}
/* delete the entry
* Page is marked must write because of precedence problems. Later
* on we may allocate a new page and set up a precedence relationship.
* This may be the lower precedence page and so it cannot be dirty
*/
CCH_MARK_MUST_WRITE(tdbb, &window);
*end_addr -= (2 + entry_p[1]);
const UCHAR* r = entry_p + 2 + entry_p[1];
USHORT l = clump_end - r + 1;
if (l) {
do {
*entry_p++ = *r++;
} while (--l);
}
CCH_RELEASE(tdbb, &window);
/* refetch the page */
window.win_page = page_num;
if (page_num == HEADER_PAGE) {
page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
header = (header_page*) page;
end_addr = &header->hdr_end;
}
else {
page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
logp = (log_info_page*) page;
end_addr = &logp->log_end;
}
break;
}
/* Add the entry */
find_clump_space(page_num, &window, &page, type, len, entry, must_write);
CCH_RELEASE(tdbb, &window);
return; // true;
}
USHORT PAG_add_file(const TEXT* file_name, SLONG start)
{
/**************************************
*
* P A G _ a d d _ f i l e
*
**************************************
*
* Functional description
* Add a file to the current database. Return the sequence
* number for the new file.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
/* Find current last file */
jrd_file* file = dbb->dbb_file;
while (file->fil_next) {
file = file->fil_next;
}
// Verify database file path against DatabaseAccess entry of firebird.conf
if (!ISC_verify_database_access(file_name)) {
ERR_post(isc_conf_access_denied,
isc_arg_string, "additional database file",
isc_arg_string, ERR_cstring(file_name),
isc_arg_end);
}
/* Create the file. If the sequence number comes back zero, it didn't
work, so punt */
const USHORT sequence = PIO_add_file(dbb, dbb->dbb_file, file_name, start);
if (!sequence)
return 0;
/* Create header page for new file */
jrd_file* next = file->fil_next;
if (dbb->dbb_flags & DBB_force_write)
PIO_force_write(next, true);
WIN window(next->fil_min_page);
header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
header->hdr_header.pag_type = pag_header;
header->hdr_sequence = sequence;
header->hdr_page_size = dbb->dbb_page_size;
header->hdr_data[0] = HDR_end;
header->hdr_end = HDR_SIZE;
next->fil_sequence = sequence;
#ifdef SUPPORT_RAW_DEVICES
/* The following lines (taken from PAG_format_header) are needed to identify
this file in raw_devices_validate_database as a valid database attachment. */
MOV_time_stamp(reinterpret_cast<ISC_TIMESTAMP*>(header->hdr_creation_date));
header->hdr_ods_version = ODS_VERSION | ODS_TYPE_CURRENT;
header->hdr_implementation = CLASS;
header->hdr_ods_minor = ODS_CURRENT;
header->hdr_ods_minor_original = ODS_CURRENT;
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3)
header->hdr_flags |= hdr_SQL_dialect_3;
#endif
header->hdr_header.pag_checksum = CCH_checksum(window.win_bdb);
PIO_write(dbb->dbb_file, window.win_bdb, window.win_buffer,
tdbb->tdbb_status_vector);
CCH_RELEASE(tdbb, &window);
next->fil_fudge = 1;
/* Update the previous header page to point to new file */
file->fil_fudge = 0;
window.win_page = file->fil_min_page;
header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
if (!file->fil_min_page)
CCH_MARK_MUST_WRITE(tdbb, &window);
else
CCH_MARK(tdbb, &window);
--start;
if (file->fil_min_page) {
PAG_add_header_entry(header, HDR_file, strlen(file_name),
reinterpret_cast<const UCHAR*>(file_name));
PAG_add_header_entry(header, HDR_last_page, sizeof(SLONG),
(UCHAR *) & start);
}
else {
PAG_add_clump(HEADER_PAGE, HDR_file, strlen(file_name),
reinterpret_cast<const UCHAR*>(file_name), CLUMP_REPLACE, 1);
PAG_add_clump(HEADER_PAGE, HDR_last_page, sizeof(SLONG),
(UCHAR *) & start, CLUMP_REPLACE, 1);
}
header->hdr_header.pag_checksum = CCH_checksum(window.win_bdb);
PIO_write(dbb->dbb_file, window.win_bdb, window.win_buffer,
tdbb->tdbb_status_vector);
CCH_RELEASE(tdbb, &window);
if (file->fil_min_page)
file->fil_fudge = 1;
return sequence;
}
int PAG_add_header_entry(header_page* header, USHORT type, SSHORT len, const UCHAR* entry)
{
/***********************************************
*
* P A G _ a d d _ h e a d e r _ e n t r y
*
***********************************************
*
* Functional description
* Add an entry to header page.
* This will be used mainly for the shadow header page and adding
* secondary files.
* Will not follow to hdr_next_page
* Will not journal changes made to page. => obsolete
* RETURNS
* TRUE - modified page
* FALSE - nothing done
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
const UCHAR* q = entry;
UCHAR* p;
for (p = header->hdr_data; ((*p != HDR_end) && (*p != type));
p += 2 + p[1]);
if (*p != HDR_end)
return FALSE;
/* We are at HDR_end, add the entry */
const int free_space = dbb->dbb_page_size - header->hdr_end;
if (free_space > (2 + len)) {
fb_assert(type <= MAX_UCHAR);
fb_assert(len <= MAX_UCHAR);
*p++ = static_cast<UCHAR>(type);
*p++ = static_cast<UCHAR>(len);
if (len) {
if (q) {
do {
*p++ = *q++;
} while (--len);
}
else {
do {
*p++ = 0;
} while (--len);
}
}
*p = HDR_end;
header->hdr_end = p - (UCHAR *) header;
return TRUE;
}
BUGCHECK(251);
return FALSE; /* Added to remove compiler warning */
}
int PAG_replace_entry_first(header_page* header, USHORT type, SSHORT len, const UCHAR* entry)
{
/***********************************************
*
* P A G _ r e p l a c e _ e n t r y _ f i r s t
*
***********************************************
*
* Functional description
* Replace an entry in the header page so it will become first entry
* This will be used mainly for the clumplets used for backup purposes
* because they are needed to be read without page lock
* Will not follow to hdr_next_page
* Will not journal changes made to page. => obsolete
* RETURNS
* TRUE - modified page
* FALSE - nothing done
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
const UCHAR* q = entry;
UCHAR* p = header->hdr_data;
while ((*p != HDR_end) && (*p != type)) {
p += 2 + p[1];
}
// Remove item if found it somewhere
if (*p != HDR_end) {
UCHAR l = p[1] + 2;
memmove(p, p+l,
header->hdr_end - (p-(UCHAR*)header) - l + 1/*to preserve HDR_end*/);
header->hdr_end -= l;
}
if (!entry) {
return FALSE; // We were asked just to remove item. We finished.
}
// Check if we got enough space
if (dbb->dbb_page_size - header->hdr_end <= len + 2) {
BUGCHECK(251);
}
// Actually add the item
memmove(header->hdr_data + len + 2, header->hdr_data, header->hdr_end - HDR_SIZE + 1);
header->hdr_data[0] = type;
header->hdr_data[1] = len;
memcpy(header->hdr_data+2, entry, len);
header->hdr_end += len + 2;
return TRUE;
}
PAG PAG_allocate(WIN * window)
{
/**************************************
*
* P A G _ a l l o c a t e
*
**************************************
*
* Functional description
* Allocate a page and fake a read with a write lock. This is
* the universal sequence when allocating pages.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
PageControl* control = dbb->dbb_pcontrol;
// Not sure if this can be moved inside the loop. Maybe some data members
// should persist across iterations?
WIN pip_window(-1);
// CVC: Not sure of the initial value. Notice bytes and bit are used after the loop.
UCHAR* bytes = 0;
UCHAR bit = 0;
pag* new_page = 0; // NULL before the search for a new page.
/* Find an allocation page with something on it */
SLONG relative_bit = -1;
SLONG sequence;
for (sequence = control->pgc_high_water;; sequence++) {
pip_window.win_page = (sequence == 0) ?
control->pgc_pip : sequence * control->pgc_ppp - 1;
page_inv_page* pip_page =
(page_inv_page*) CCH_FETCH(tdbb, &pip_window, LCK_write, pag_pages);
const UCHAR* end = (UCHAR*) pip_page + dbb->dbb_page_size;
for (bytes = &pip_page->pip_bits[pip_page->pip_min >> 3]; bytes < end;
bytes++)
{
if (*bytes != 0) {
/* 'byte' is not zero, so it describes at least one free page. */
bit = 1;
for (SLONG i = 0; i < 8; i++, bit <<= 1) {
if (bit & *bytes) {
relative_bit =
((bytes - pip_page->pip_bits) << 3) + i;
window->win_page =
relative_bit + sequence * control->pgc_ppp;
new_page = CCH_fake(tdbb, window, 0); /* don't wait on latch */
if (new_page)
break; /* Found a page and successfully fake-ed it */
}
}
}
if (new_page)
break; /* Found a page and successfully fake-ed it */
}
if (new_page)
break; /* Found a page and successfully fake-ed it */
CCH_RELEASE(tdbb, &pip_window);
}
control->pgc_high_water = sequence;
CCH_MARK(tdbb, &pip_window);
*bytes &= ~bit;
if (relative_bit != control->pgc_ppp - 1) {
CCH_RELEASE(tdbb, &pip_window);
CCH_precedence(tdbb, window, pip_window.win_page);
#ifdef VIO_DEBUG
if (debug_flag > DEBUG_WRITES_INFO)
printf("\tPAG_allocate: allocated page %"SLONGFORMAT"\n",
window->win_page);
#endif
return new_page;
}
/* We've allocated the last page on the space management page. Rather
than returning it, format it as a page inventory page, and recurse. */
page_inv_page* new_pip_page = (page_inv_page*) new_page;
new_pip_page->pip_header.pag_type = pag_pages;
// CVC: If some tips on web sites are true, this can be improved by
// a pointer to ULONG setting memory to 0xffffffff.
const UCHAR* end = (UCHAR *) new_pip_page + dbb->dbb_page_size;
for (bytes = new_pip_page->pip_bits; bytes < end;)
*bytes++ = 0xff;
CCH_must_write(window);
CCH_RELEASE(tdbb, window);
CCH_must_write(&pip_window);
CCH_RELEASE(tdbb, &pip_window);
return PAG_allocate(window);
}
SLONG PAG_attachment_id(void)
{
/******************************************
*
* P A G _ a t t a c h m e n t _ i d
*
******************************************
*
* Functional description
* Get attachment id. If don't have one, get one. As a side
* effect, get a lock on it as well.
*
******************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
Attachment* attachment = tdbb->tdbb_attachment;
WIN window(-1);
/* If we've been here before just return the id */
if (attachment->att_id_lock)
return attachment->att_attachment_id;
/* Get new attachment id */
if (dbb->dbb_flags & DBB_read_only) {
attachment->att_attachment_id = ++dbb->dbb_attachment_id;
}
else {
window.win_page = HEADER_PAGE;
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK(tdbb, &window);
attachment->att_attachment_id = ++header->hdr_attachment_id;
CCH_RELEASE(tdbb, &window);
}
/* Take out lock on attachment id */
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock();
attachment->att_id_lock = lock;
lock->lck_type = LCK_attachment;
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_key.lck_long = attachment->att_attachment_id;
lock->lck_dbb = dbb;
LCK_lock(tdbb, lock, LCK_write, TRUE);
return attachment->att_attachment_id;
}
int PAG_delete_clump_entry(SLONG page_num, USHORT type)
{
/***********************************************
*
* P A G _ d e l e t e _ c l u m p _ e n t r y
*
***********************************************
*
* Functional description
* Gets rid on the entry 'type' from page.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
WIN window(page_num);
pag* page;
if (page_num == HEADER_PAGE)
page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
else
page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
UCHAR* entry_p;
const UCHAR* clump_end;
if (!find_type
(page_num, &window, &page, LCK_write, type, &entry_p, &clump_end))
{
CCH_RELEASE(tdbb, &window);
return FALSE;
}
CCH_MARK(tdbb, &window);
header_page* header = 0;
log_info_page* logp = 0;
USHORT* end_addr;
if (page_num == HEADER_PAGE) {
header = (header_page*) page;
end_addr = &header->hdr_end;
}
else {
logp = (log_info_page*) page;
end_addr = &logp->log_end;
}
*end_addr -= (2 + entry_p[1]);
const UCHAR* r = entry_p + 2 + entry_p[1];
USHORT l = clump_end - r + 1;
if (l) {
do {
*entry_p++ = *r++;
} while (--l);
}
CCH_RELEASE(tdbb, &window);
return TRUE;
}
void PAG_format_header(void)
{
/**************************************
*
* P A G _ f o r m a t _ h e a d e r
*
**************************************
*
* Functional description
* Create the header page for a new file.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
/* Initialize header page */
WIN window(HEADER_PAGE);
header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
header->hdr_header.pag_scn = 0;
MOV_time_stamp(reinterpret_cast <
ISC_TIMESTAMP * >(header->hdr_creation_date));
header->hdr_header.pag_type = pag_header;
header->hdr_page_size = dbb->dbb_page_size;
header->hdr_ods_version = ODS_VERSION | ODS_TYPE_CURRENT;
header->hdr_implementation = CLASS;
header->hdr_ods_minor = ODS_CURRENT;
header->hdr_ods_minor_original = ODS_CURRENT;
header->hdr_oldest_transaction = 1;
header->hdr_bumped_transaction = 1;
header->hdr_end = HDR_SIZE;
header->hdr_data[0] = HDR_end;
#ifdef SYNC_WRITE_DEFAULT
header->hdr_flags |= hdr_force_write;
#endif
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) {
header->hdr_flags |= hdr_SQL_dialect_3;
}
dbb->dbb_ods_version = header->hdr_ods_version & ~ODS_TYPE_MASK;
dbb->dbb_minor_version = header->hdr_ods_minor;
dbb->dbb_minor_original = header->hdr_ods_minor_original;
CCH_RELEASE(tdbb, &window);
}
// CVC: This function is mostly obsolete. Ann requested to keep it and the code that calls it.
// We won't read the log, anyway.
void PAG_format_log(void)
{
/***********************************************
*
* P A G _ f o r m a t _ l o g
*
***********************************************
*
* Functional description
* Initialize log page.
* Set all parameters to 0
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
WIN window(LOG_PAGE);
log_info_page* logp = (log_info_page*) CCH_fake(tdbb, &window, 1);
logp->log_header.pag_type = pag_log;
CCH_RELEASE(tdbb, &window);
}
void PAG_format_pip(void)
{
/**************************************
*
* P A G _ f o r m a t _ p i p
*
**************************************
*
* Functional description
* Create a page inventory page to
* complete the formatting of a new file
* into a rudimentary database.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
/* Initialize Page Inventory Page */
WIN window(1);
dbb->dbb_pcontrol->pgc_pip = 1;
page_inv_page* pages = (page_inv_page*) CCH_fake(tdbb, &window, 1);
pages->pip_header.pag_type = pag_pages;
pages->pip_min = 4;
UCHAR* p = pages->pip_bits;
int i = dbb->dbb_page_size - OFFSETA(page_inv_page*, pip_bits);
while (i--) {
*p++ = 0xff;
}
pages->pip_bits[0] &= ~(1 | 2 | 4);
CCH_RELEASE(tdbb, &window);
}
bool PAG_get_clump(SLONG page_num, USHORT type, USHORT* len, UCHAR* entry)
{
/***********************************************
*
* P A G _ g e t _ c l u m p
*
***********************************************
*
* Functional description
* Find 'type' clump in page_num
* true - Found it
* false - Not present
* RETURNS
* value of clump in entry
* length in len
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
*len = 0;
WIN window(page_num);
pag* page;
if (page_num == HEADER_PAGE)
page = CCH_FETCH(tdbb, &window, LCK_read, pag_header);
else
page = CCH_FETCH(tdbb, &window, LCK_read, pag_log);
UCHAR* entry_p;
const UCHAR* dummy;
if (!find_type(page_num, &window, &page, LCK_read, type, &entry_p, &dummy)) {
CCH_RELEASE(tdbb, &window);
return false;
}
USHORT l = entry_p[1];
*len = l;
entry_p += 2;
UCHAR* q = entry;
if (l) {
do {
*q++ = *entry_p++;
} while (--l);
}
CCH_RELEASE(tdbb, &window);
return true;
}
void PAG_header(const TEXT* file_name, USHORT file_length)
{
/**************************************
*
* P A G _ h e a d e r
*
**************************************
*
* Functional description
* Checkout database header page.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
/* allocate a spare buffer which is large enough,
and set up to release it in case of error; note
that dbb_page_size has not been set yet, so we
can't depend on this.
Make sure that buffer is aligned on a page boundary
and unit of transfer is a multiple of physical disk
sector for raw disk access. */
SCHAR* const temp_buffer = FB_NEW(*getDefaultMemoryPool()) SCHAR[2 * MIN_PAGE_SIZE];
SCHAR* temp_page =
(SCHAR *) (((U_IPTR) temp_buffer + MIN_PAGE_SIZE - 1) &
~((U_IPTR) MIN_PAGE_SIZE - 1));
try {
header_page* header = (header_page*) temp_page;
PIO_header(dbb, temp_page, MIN_PAGE_SIZE);
if (header->hdr_header.pag_type != pag_header || header->hdr_sequence) {
ERR_post(isc_bad_db_format,
isc_arg_cstring, file_length, ERR_string(file_name,
file_length), 0);
}
if (!ODS_SUPPORTED(header->hdr_ods_version))
{
ERR_post(isc_wrong_ods,
isc_arg_cstring, file_length, ERR_string(file_name,
file_length),
isc_arg_number, (SLONG) (header->hdr_ods_version & ~ODS_TYPE_MASK),
isc_arg_number, (SLONG) (header->hdr_ods_version & ODS_TYPE_MASK),
isc_arg_number, (SLONG) ODS_VERSION,
isc_arg_number, (SLONG) ODS_TYPE_CURRENT, 0);
}
/****
Note that if this check is turned on, it should be recoded in order that
the Intel platforms can share databases. At present (Feb 95) it is possible
to share databases between Windows and NT, but not with NetWare. Sharing
databases with OS/2 is unknown and needs to be investigated. The CLASS was
initially 8 for all Intel platforms, but was changed after 4.0 was released
in order to allow differentiation between databases created on various
platforms. This should allow us in future to identify where databases were
created. Even when we get to the stage where databases created on PC platforms
are sharable between all platforms, it would be useful to identify where they
were created for debugging purposes. - Deej 2/6/95
if (header->hdr_implementation && header->hdr_implementation != CLASS)
ERR_post (isc_bad_db_format,
isc_arg_cstring, file_length, ERR_string(file_name, file_length), 0);
****/
if (header->hdr_page_size < MIN_PAGE_SIZE ||
header->hdr_page_size > MAX_PAGE_SIZE)
{
ERR_post(isc_bad_db_format,
isc_arg_cstring, file_length, ERR_string(file_name,
file_length),
0);
}
if (header->hdr_next_transaction) {
if (header->hdr_oldest_active > header->hdr_next_transaction)
BUGCHECK(266); /*next transaction older than oldest active */
if (header->hdr_oldest_transaction > header->hdr_next_transaction)
BUGCHECK(267); /* next transaction older than oldest transaction */
}
dbb->dbb_ods_version = header->hdr_ods_version & ~ODS_TYPE_MASK;
dbb->dbb_minor_version = header->hdr_ods_minor;
dbb->dbb_minor_original = header->hdr_ods_minor_original;
if (header->hdr_flags & hdr_SQL_dialect_3)
dbb->dbb_flags |= DBB_DB_SQL_dialect_3;
jrd_rel* relation = MET_relation(tdbb, 0);
if (!relation->rel_pages) {
// 21-Dec-2003 Nickolay Samofatov
// No need to re-set first page for RDB$PAGES relation since
// current code cannot change its location after database creation
// Currently, this change only affects isc_database_info call,
// the only call which may call PAG_header multiple times.
// In fact, this isc_database_info behavior seems dangerous to me,
// but let somebody else fix that problem, I just fix the memory leak.
vcl* vector = vcl::newVector(*dbb->dbb_permanent, 1);
relation->rel_pages = vector;
(*vector)[0] = header->hdr_PAGES;
}
dbb->dbb_page_size = header->hdr_page_size;
dbb->dbb_page_buffers = header->hdr_page_buffers;
dbb->dbb_next_transaction = header->hdr_next_transaction;
dbb->dbb_oldest_transaction = header->hdr_oldest_transaction;
dbb->dbb_oldest_active = header->hdr_oldest_active;
dbb->dbb_oldest_snapshot = header->hdr_oldest_snapshot;
dbb->dbb_attachment_id = header->hdr_attachment_id;
if (header->hdr_flags & hdr_read_only) {
/* If Header Page flag says the database is ReadOnly, gladly accept it. */
dbb->dbb_flags &= ~DBB_being_opened_read_only;
dbb->dbb_flags |= DBB_read_only;
}
/* If hdr_read_only is not set... */
if (!(header->hdr_flags & hdr_read_only)
&& (dbb->dbb_flags & DBB_being_opened_read_only))
{
/* Looks like the Header page says, it is NOT ReadOnly!! But the database
* file system permission gives only ReadOnly access. Punt out with
* isc_no_priv error (no privileges)
*/
ERR_post(isc_no_priv,
isc_arg_string, "read-write",
isc_arg_string, "database",
isc_arg_cstring, file_length, ERR_string(file_name,
file_length), 0);
}
if (header->hdr_flags & hdr_force_write) {
dbb->dbb_flags |= DBB_force_write;
if (!(header->hdr_flags & hdr_read_only))
PIO_force_write(dbb->dbb_file, true);
}
if (header->hdr_flags & hdr_no_reserve)
dbb->dbb_flags |= DBB_no_reserve;
const USHORT sd_flags = header->hdr_flags & hdr_shutdown_mask;
if (sd_flags) {
dbb->dbb_ast_flags |= DBB_shutdown;
if (sd_flags == hdr_shutdown_full)
dbb->dbb_ast_flags |= DBB_shutdown_full;
else if (sd_flags == hdr_shutdown_single)
dbb->dbb_ast_flags |= DBB_shutdown_single;
}
if (temp_buffer)
delete[] temp_buffer;
} // try
catch (const std::exception&) {
if (temp_buffer)
delete[] temp_buffer;
throw;
}
}
void PAG_init(void)
{
/**************************************
*
* P A G _ i n i t
*
**************************************
*
* Functional description
* Initialize stuff for page handling.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
PageControl* control = FB_NEW(*dbb->dbb_permanent) PageControl();
dbb->dbb_pcontrol = control;
control->pgc_bytes = dbb->dbb_page_size - OFFSETA(page_inv_page*, pip_bits);
control->pgc_ppp = control->pgc_bytes * 8;
control->pgc_tpt =
(dbb->dbb_page_size - OFFSETA(tx_inv_page*, tip_transactions)) * 4;
control->pgc_pip = 1;
/* dbb_ods_version can be 0 when a new database is being created */
if ((dbb->dbb_ods_version == 0)
|| (dbb->dbb_ods_version >= ODS_VERSION10))
{
control->pgc_gpg =
(dbb->dbb_page_size -
OFFSETA(generator_page*, gpg_values)) / sizeof(((generator_page*) NULL)->gpg_values);
}
else {
control->pgc_gpg =
(dbb->dbb_page_size -
OFFSETA(pointer_page*, ppg_page)) / sizeof(((pointer_page*) NULL)->ppg_page);
}
/* Compute the number of data pages per pointer page. Each data page
requires a 32 bit pointer and a 2 bit control field. */
dbb->dbb_dp_per_pp = (dbb->dbb_page_size - OFFSETA(pointer_page*, ppg_page)) * 8 /
(BITS_PER_LONG + 2);
/* Compute the number of records that can fit on a page using the
size of the record index (dpb_repeat) and a record header. This
gives an artificially high number, reducing the density of db_keys. */
dbb->dbb_max_records = (dbb->dbb_page_size - sizeof(data_page)) /
(sizeof(data_page::dpg_repeat) + OFFSETA(RHD, rhd_data));
/* Compute the number of index roots that will fit on an index root page,
assuming that each index has only one key */
dbb->dbb_max_idx = (dbb->dbb_page_size - OFFSETA(index_root_page*, irt_rpt)) /
(sizeof(index_root_page::irt_repeat) +
(1 * (dbb->dbb_ods_version >= ODS_VERSION11) ?
sizeof(irtd) : sizeof(irtd_ods10)));
/* Compute prefetch constants from database page size and maximum prefetch
transfer size. Double pages per prefetch request so that cache reader
can overlap prefetch I/O with database computation over previously
prefetched pages. */
dbb->dbb_prefetch_sequence = PREFETCH_MAX_TRANSFER / dbb->dbb_page_size;
dbb->dbb_prefetch_pages = dbb->dbb_prefetch_sequence * 2;
}
void PAG_init2(USHORT shadow_number)
{
/**************************************
*
* P A G _ i n i t 2
*
**************************************
*
* Functional description
* Perform second phase of page initialization -- the eternal
* search for additional files.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
ISC_STATUS* status = tdbb->tdbb_status_vector;
/* allocate a spare buffer which is large enough,
and set up to release it in case of error. Align
the temporary page buffer for raw disk access. */
SCHAR* const temp_buffer = FB_NEW(*getDefaultMemoryPool()) SCHAR[dbb->dbb_page_size + MIN_PAGE_SIZE];
SCHAR* temp_page =
(SCHAR *) (((U_IPTR) temp_buffer + MIN_PAGE_SIZE - 1) &
~((U_IPTR) MIN_PAGE_SIZE - 1));
try {
jrd_file* file = dbb->dbb_file;
if (shadow_number) {
Shadow* shadow = dbb->dbb_shadow;
for (; shadow; shadow = shadow->sdw_next) {
if (shadow->sdw_number == shadow_number) {
file = shadow->sdw_file;
break;
}
}
if (!shadow)
BUGCHECK(161); /* msg 161 shadow block not found */
}
USHORT sequence = 1;
WIN window(-1);
TEXT buf[MAXPATHLEN + 1];
/* Loop thru files and header pages until everything is open */
for (;;)
{
TEXT* file_name = NULL;
window.win_page = file->fil_min_page;
USHORT file_length = 0;
ULONG last_page = 0;
BufferDesc temp_bdb;
SLONG next_page = 0;
do {
/* note that we do not have to get a read lock on
the header page (except for header page 0) because
the only time it will be modified is when adding a file,
which must be done with an exclusive lock on the database --
if this changes, this policy will have to be reevaluated;
at any rate there is a problem with getting a read lock
because the corresponding page in the main database file
may not exist */
if (!file->fil_min_page)
CCH_FETCH(tdbb, &window, LCK_read, pag_header);
header_page* header = (header_page*) temp_page;
temp_bdb.bdb_buffer = (PAG) header;
temp_bdb.bdb_page = window.win_page;
temp_bdb.bdb_dbb = dbb;
/* Read the required page into the local buffer */
PIO_read(file, &temp_bdb, (PAG) header, status);
if ((shadow_number) && (!file->fil_min_page))
CCH_RELEASE(tdbb, &window);
for (const TEXT* p = reinterpret_cast<TEXT*>(header->hdr_data);
*p != HDR_end;
p += 2 + p[1])
{
switch (*p) {
case HDR_file:
file_length = p[1];
file_name = buf;
MOVE_FAST(p + 2, buf, file_length);
break;
case HDR_last_page:
MOVE_FAST(p + 2, &last_page, sizeof(last_page));
break;
case HDR_sweep_interval:
if (!(dbb->dbb_flags & DBB_read_only))
MOVE_FAST(p + 2, &dbb->dbb_sweep_interval,
sizeof(SLONG));
break;
}
}
next_page = header->hdr_next_page;
if ((!shadow_number) && (!file->fil_min_page))
CCH_RELEASE(tdbb, &window);
window.win_page = next_page;
/*
* Make sure the header page and all the overflow header
* pages are traversed. For V4.0, only the header page for
* the primary database page will have overflow pages.
*/
} while (next_page);
if (file->fil_min_page)
file->fil_fudge = 1;
if (!file_name)
break;
// Verify database file path against DatabaseAccess entry of firebird.conf
file_name[file_length] = 0;
if (!ISC_verify_database_access(file_name)) {
ERR_post(isc_conf_access_denied,
isc_arg_string, "additional database file",
isc_arg_string, ERR_cstring(file_name),
isc_arg_end);
}
file->fil_next = PIO_open(dbb,
file_name,
file_length,
false,
0,
file_name,
file_length);
file->fil_max_page = last_page;
file = file->fil_next;
if (dbb->dbb_flags & DBB_force_write)
PIO_force_write(file, true);
file->fil_min_page = last_page + 1;
file->fil_sequence = sequence++;
}
if (temp_buffer)
delete[] temp_buffer;
} // try
catch (const std::exception&) {
if (temp_buffer)
delete[] temp_buffer;
throw;
}
}
SLONG PAG_last_page(void)
{
/**************************************
*
* P A G _ l a s t _ p a g e
*
**************************************
*
* Functional description
* Compute the highest page allocated. This is called by the
* shadow stuff to dump a database.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
const ULONG pages_per_pip = dbb->dbb_pcontrol->pgc_ppp;
WIN window(-1);
/* Find the last page allocated */
ULONG relative_bit = 0;
USHORT sequence;
for (sequence = 0;; ++sequence) {
window.win_page =
(!sequence) ? dbb->dbb_pcontrol->pgc_pip : sequence *
pages_per_pip - 1;
page_inv_page* page = (page_inv_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_pages);
UCHAR* bits;
for (bits = page->pip_bits + (pages_per_pip >> 3) - 1;
*bits == (UCHAR) - 1; --bits); // null loop body???
SSHORT bit;
for (bit = 7; bit >= 0; --bit) {
if (!(*bits & (1 << bit)))
break;
}
relative_bit = (bits - page->pip_bits) * 8 + bit;
CCH_RELEASE(tdbb, &window);
if (relative_bit != pages_per_pip - 1)
break;
}
return sequence * pages_per_pip + relative_bit;
}
void PAG_release_page(SLONG number, SLONG prior_page)
{
/**************************************
*
* P A G _ r e l e a s e _ p a g e
*
**************************************
*
* Functional description
* Release a page to the free page page.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
#ifdef VIO_DEBUG
if (debug_flag > DEBUG_WRITES_INFO)
printf("\tPAG_release_page: about to release page %"SLONGFORMAT"\n", number);
#endif
PageControl* control = dbb->dbb_pcontrol;
const SLONG sequence = number / control->pgc_ppp;
const SLONG relative_bit = number % control->pgc_ppp;
WIN pip_window((sequence == 0) ?
control->pgc_pip : sequence * control->pgc_ppp - 1);
page_inv_page* pages =
(page_inv_page*) CCH_FETCH(tdbb, &pip_window, LCK_write, pag_pages);
CCH_precedence(tdbb, &pip_window, prior_page);
CCH_MARK(tdbb, &pip_window);
pages->pip_bits[relative_bit >> 3] |= 1 << (relative_bit & 7);
pages->pip_min = MIN(pages->pip_min, relative_bit);
CCH_RELEASE(tdbb, &pip_window);
control->pgc_high_water = MIN(control->pgc_high_water, sequence);
}
void PAG_set_force_write(Database* dbb, SSHORT flag)
{
/**************************************
*
* P A G _ s e t _ f o r c e _ w r i t e
*
**************************************
*
* Functional description
* Turn on/off force write.
* The value 2 for flag means set to default.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
WIN window(HEADER_PAGE);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
if (flag == 2)
/* Set force write to the default for the platform */
#ifdef SYNC_WRITE_DEFAULT
flag = 1;
#else
flag = 0;
#endif
if (flag) {
header->hdr_flags |= hdr_force_write;
dbb->dbb_flags |= DBB_force_write;
}
else {
header->hdr_flags &= ~hdr_force_write;
dbb->dbb_flags &= ~DBB_force_write;
}
CCH_RELEASE(tdbb, &window);
for (jrd_file* file = dbb->dbb_file; file; file = file->fil_next) {
PIO_force_write(file, flag != 0);
}
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) {
for (jrd_file* file = shadow->sdw_file; file; file = file->fil_next)
PIO_force_write(file, flag != 0);
}
}
void PAG_set_no_reserve(Database* dbb, USHORT flag)
{
/**************************************
*
* P A G _ s e t _ n o _ r e s e r v e
*
**************************************
*
* Functional description
* Turn on/off reserving space for versions
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
WIN window(HEADER_PAGE);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
if (flag) {
header->hdr_flags |= hdr_no_reserve;
dbb->dbb_flags |= DBB_no_reserve;
}
else {
header->hdr_flags &= ~hdr_no_reserve;
dbb->dbb_flags &= ~DBB_no_reserve;
}
CCH_RELEASE(tdbb, &window);
}
void PAG_set_db_readonly(Database* dbb, bool flag)
{
/*********************************************
*
* P A G _ s e t _ d b _ r e a d o n l y
*
*********************************************
*
* Functional description
* Set database access mode to readonly OR readwrite
*
*********************************************/
thread_db* tdbb = JRD_get_thread_data();
WIN window(HEADER_PAGE);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
if (!flag) {
/* If the database is transitioning from RO to RW, reset the
* in-memory Database flag which indicates that the database is RO.
* This will allow the CCH subsystem to allow pages to be MARK'ed
* for WRITE operations
*/
header->hdr_flags &= ~hdr_read_only;
dbb->dbb_flags &= ~DBB_read_only;
}
CCH_MARK_MUST_WRITE(tdbb, &window);
if (flag) {
header->hdr_flags |= hdr_read_only;
dbb->dbb_flags |= DBB_read_only;
}
CCH_RELEASE(tdbb, &window);
}
void PAG_set_db_SQL_dialect(Database* dbb, SSHORT flag)
{
/*********************************************
*
* P A G _ s e t _ d b _ S Q L _ d i a l e c t
*
*********************************************
*
* Functional description
* Set database SQL dialect to SQL_DIALECT_V5 or SQL_DIALECT_V6
*
*********************************************/
thread_db* tdbb = JRD_get_thread_data();
const USHORT major_version = dbb->dbb_ods_version;
const USHORT minor_original = dbb->dbb_minor_original;
WIN window(HEADER_PAGE);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
if ((flag) && (ENCODE_ODS(major_version, minor_original) >= ODS_10_0)) {
switch (flag) {
case SQL_DIALECT_V5:
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3 ||
header->hdr_flags & hdr_SQL_dialect_3)
{
// Check the returned value here!
ERR_post_warning(isc_dialect_reset_warning, 0);
}
dbb->dbb_flags &= ~DBB_DB_SQL_dialect_3; /* set to 0 */
header->hdr_flags &= ~hdr_SQL_dialect_3; /* set to 0 */
break;
case SQL_DIALECT_V6:
dbb->dbb_flags |= DBB_DB_SQL_dialect_3; /* set to dialect 3 */
header->hdr_flags |= hdr_SQL_dialect_3; /* set to dialect 3 */
break;
default:
CCH_RELEASE(tdbb, &window);
ERR_post(isc_inv_dialect_specified, isc_arg_number, flag,
isc_arg_gds, isc_valid_db_dialects, isc_arg_string,
"1 and 3", isc_arg_gds, isc_dialect_not_changed, 0);
break;
}
}
CCH_MARK_MUST_WRITE(tdbb, &window);
CCH_RELEASE(tdbb, &window);
}
void PAG_set_page_buffers(ULONG buffers)
{
/**************************************
*
* P A G _ s e t _ p a g e _ b u f f e r s
*
**************************************
*
* Functional description
* Set database-specific page buffer cache
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
ERR_POST_IF_DATABASE_IS_READONLY(dbb);
WIN window(HEADER_PAGE);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
header->hdr_page_buffers = buffers;
CCH_RELEASE(tdbb, &window);
}
void PAG_sweep_interval(SLONG interval)
{
/**************************************
*
* P A G _ s w e e p _ i n t e r v a l
*
**************************************
*
* Functional description
* Set sweep interval.
*
**************************************/
PAG_add_clump(HEADER_PAGE, HDR_sweep_interval, sizeof(SLONG),
(UCHAR *) & interval, CLUMP_REPLACE, 1);
}
int PAG_unlicensed(void)
{
/**************************************
*
* P A G _ u n l i c e n s e d
*
**************************************
*
* Functional description
* Log unlicensed activity. Return current count of this
* sort of non-sense.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
WIN window(HEADER_PAGE);
CCH_FETCH(tdbb, &window, LCK_write, pag_header);
CCH_MARK_MUST_WRITE(tdbb, &window);
USHORT len;
SLONG count;
if (PAG_get_clump(HEADER_PAGE, HDR_unlicensed, &len, (UCHAR *) & count)) {
count++;
PAG_add_clump(HEADER_PAGE, HDR_unlicensed, sizeof(count),
(UCHAR *) & count, CLUMP_REPLACE_ONLY, 1);
}
else {
count = 1;
PAG_add_clump(HEADER_PAGE, HDR_unlicensed, sizeof(count),
(UCHAR *) & count, CLUMP_REPLACE, 1);
}
CCH_RELEASE(tdbb, &window);
return count;
}
static void find_clump_space(
SLONG page_num,
WIN* window,
PAG* ppage,
USHORT type,
SSHORT len, const UCHAR* entry, USHORT must_write)
{
/***********************************************
*
* f i n d _ c l u m p _ s p a c e
*
***********************************************
*
* Functional description
* Find space for the new clump.
* Add the entry at the end of clumplet list.
* Allocate a new page if required.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
Database* dbb = tdbb->tdbb_database;
CHECK_DBB(dbb);
const UCHAR* ptr = entry;
pag* page = *ppage;
header_page* header = 0; // used after the loop
log_info_page* logp = 0; // used after the loop
while (true) {
SLONG next_page, free_space;
USHORT* end_addr;
UCHAR* p;
if (page_num == HEADER_PAGE) {
header = (header_page*) page;
next_page = header->hdr_next_page;
free_space = dbb->dbb_page_size - header->hdr_end;
end_addr = &header->hdr_end;
p = (UCHAR *) header + header->hdr_end;
}
else {
logp = (log_info_page*) page;
next_page = logp->log_next_page;
free_space = dbb->dbb_page_size - logp->log_end;
end_addr = &logp->log_end;
p = (UCHAR *) logp + logp->log_end;
}
if (free_space > (2 + len)) {
if (must_write)
CCH_MARK_MUST_WRITE(tdbb, window);
else
CCH_MARK(tdbb, window);
fb_assert(type <= MAX_UCHAR);
fb_assert(len <= MAX_UCHAR);
*p++ = static_cast<UCHAR>(type);
*p++ = static_cast<UCHAR>(len);
if (len) {
do {
*p++ = *ptr++;
} while (--len);
}
*p = HDR_end;
*end_addr = (USHORT) (p - (UCHAR *) page);
return;
}
if (!next_page)
break;
/* Follow chain of header pages */
if (page_num == HEADER_PAGE)
*ppage = page =
CCH_HANDOFF(tdbb, window, next_page, LCK_write, pag_header);
else
*ppage = page =
CCH_HANDOFF(tdbb, window, next_page, LCK_write, pag_log);
}
WIN new_window(-1);
pag* new_page = (PAG) DPM_allocate(tdbb, &new_window);
if (must_write)
CCH_MARK_MUST_WRITE(tdbb, &new_window);
else
CCH_MARK(tdbb, &new_window);
header_page* new_header = 0;
log_info_page* new_logp = 0;
SLONG next_page;
USHORT* end_addr;
UCHAR* p;
if (page_num == HEADER_PAGE) {
new_header = (header_page*) new_page;
new_header->hdr_header.pag_type = pag_header;
new_header->hdr_end = HDR_SIZE;
new_header->hdr_page_size = dbb->dbb_page_size;
new_header->hdr_data[0] = HDR_end;
next_page = new_window.win_page;
end_addr = &new_header->hdr_end;
p = new_header->hdr_data;
}
else {
new_logp = (log_info_page*) new_page;
new_logp->log_header.pag_type = pag_log;
new_logp->log_data[0] = LOG_end;
new_logp->log_end = LIP_SIZE;
next_page = new_window.win_page;
end_addr = &new_logp->log_end;
p = new_logp->log_data;
}
fb_assert(type <= MAX_UCHAR);
fb_assert(len <= MAX_UCHAR);
*p++ = static_cast<UCHAR>(type);
*p++ = static_cast<UCHAR>(len);
if (len) {
do {
*p++ = *ptr++;
} while (--len);
}
*p = HDR_end;
*end_addr = (USHORT) (p - (UCHAR *) new_page);
CCH_RELEASE(tdbb, &new_window);
CCH_precedence(tdbb, window, next_page);
CCH_MARK(tdbb, window);
if (page_num == HEADER_PAGE)
header->hdr_next_page = next_page;
else
logp->log_next_page = next_page;
}
static bool find_type(
SLONG page_num,
WIN* window,
PAG* ppage,
USHORT lock,
USHORT type, UCHAR** entry_p, const UCHAR** clump_end)
{
/***********************************************
*
* f i n d _ t y p e
*
***********************************************
*
* Functional description
* Find the requested type in a page.
* RETURNS
* pointer to type, pointer to end of page, header.
* true - Found it
* false - Not present
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
while (true) {
header_page* header = 0;
log_info_page* logp = 0;
UCHAR* p;
SLONG next_page;
if (page_num == HEADER_PAGE) {
header = (header_page*) (*ppage);
p = header->hdr_data;
next_page = header->hdr_next_page;
}
else {
logp = (log_info_page*) (*ppage);
p = logp->log_data;
next_page = logp->log_next_page;
}
UCHAR* q = 0;
for (; (*p != HDR_end); p += 2 + p[1]) {
if (*p == type)
q = p;
}
if (q) {
*entry_p = q;
*clump_end = p;
return true;
}
/* Follow chain of pages */
if (next_page) {
if (page_num == HEADER_PAGE) {
*ppage =
CCH_HANDOFF(tdbb, window, next_page, lock, pag_header);
}
else {
*ppage = CCH_HANDOFF(tdbb, window, next_page, lock, pag_log);
}
}
else
return false;
}
}