mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 14:03:02 +01:00
2444 lines
67 KiB
C++
2444 lines
67 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>
|
|
|
|
#ifdef WIN_NT
|
|
#include <process.h>
|
|
#endif
|
|
|
|
#include "../common/config/config.h"
|
|
#include "../jrd/fil.h"
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/pag.h"
|
|
#include "../jrd/ods.h"
|
|
#include "../jrd/os/pio.h"
|
|
#include "../jrd/os/path_utils.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../jrd/iberr.h"
|
|
#include "../jrd/gdsassert.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/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/ods_proto.h"
|
|
#include "../jrd/pag_proto.h"
|
|
#include "../jrd/os/pio_proto.h"
|
|
#include "../jrd/thd.h"
|
|
#include "../jrd/thread_proto.h"
|
|
#include "../jrd/isc_f_proto.h"
|
|
#include "../jrd/TempSpace.h"
|
|
|
|
using namespace Jrd;
|
|
using namespace Ods;
|
|
|
|
static void find_clump_space(SLONG, WIN*, pag**, USHORT, USHORT, const UCHAR*,
|
|
USHORT);
|
|
static bool find_type(SLONG, WIN*, pag**, USHORT, USHORT, UCHAR**,
|
|
const UCHAR**);
|
|
|
|
inline void err_post_if_database_is_readonly(const Database* dbb)
|
|
{
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
ERR_post(isc_read_only_database, 0);
|
|
}
|
|
|
|
// Class definitions (obsolete platforms are commented out)
|
|
// Class constant name consists of OS platform and CPU architecture.
|
|
//
|
|
// For ports created before Firebird 2.0 release 64-bit and 32-bit
|
|
// sub-architectures of the same CPU should use different classes.
|
|
// For 64-bit ports first created after or as a part of Firebird 2.0
|
|
// release CPU architecture may be the same for both variants.
|
|
|
|
static const int CLASS_UNKNOWN = 0;
|
|
//static const CLASS_APOLLO_68K = 1; // Apollo 68K, Dn 10K
|
|
static const int CLASS_SOLARIS_SPARC = 2; // Sun 68k, Sun Sparc, HP 9000/300, MAC AUX, IMP, DELTA, NeXT, UNIXWARE, DG_X86
|
|
static const int CLASS_SOLARIS_I386 = 3; // Sun 386i
|
|
//static const CLASS_VMS_VAX = 4; // VMS/VAX
|
|
//static const CLASS_ULTRIX_VAX = 5; // Ultrix/VAX
|
|
//static const CLASS_ULTRIX_MIPS = 6; // Ultrix/MIPS
|
|
static const int CLASS_HPUX_PA = 7; // HP-UX on PA-RISC (was: HP 900/800 (precision))
|
|
static const int CLASS_NETWARE_I386 = 8; // NetWare
|
|
//static const CLASS_MAC_OS = 9; // MAC-OS
|
|
static const int CLASS_AIX_PPC = 10; // AIX on PowerPC platform (was: IBM RS/6000)
|
|
//static const CLASS_DG_AVIION = 11; // DG AViiON
|
|
//static const CLASS_MPE_XL = 12; // MPE/XL
|
|
static const int CLASS_IRIX_MIPS = 13; // Silicon Graphics/IRIS
|
|
//static const int CLASS_CRAY = 14; // Cray
|
|
static const int CLASS_TRU64_ALPHA = 15; // Tru64 Unix running on Alpha (was: Dec OSF/1)
|
|
static const int CLASS_WINDOWS_I386 = 16; // NT -- post 4.0 (delivered 4.0 as class 8)
|
|
//static const CLASS_OS2 = 17; // OS/2
|
|
//static const CLASS_WIN16 = 18; // Windows 16 bit
|
|
static const int CLASS_LINUX_I386 = 19; // LINUX on Intel series
|
|
static const int CLASS_LINUX_SPARC = 20; // LINUX on sparc systems
|
|
static const int CLASS_FREEBSD_I386 = 21; // FreeBSD/i386
|
|
static const int CLASS_NETBSD_I386 = 22; // NetBSD/i386
|
|
static const int CLASS_DARWIN_PPC = 23; // Darwin/PowerPC
|
|
static const int CLASS_LINUX_AMD64 = 24; // LINUX on AMD64 systems
|
|
static const int CLASS_FREEBSD_AMD64 = 25;// FreeBSD/amd64
|
|
static const int CLASS_WINDOWS_AMD64 = 26;// Windows/amd64
|
|
static const int CLASS_LINUX_PPC = 27; // LINUX/PowerPC
|
|
static const int CLASS_DARWIN_I386 = 28; //Darwin/Intel
|
|
static const int CLASS_LINUX_MIPSEL = 29; // LINUX/MIPS
|
|
static const int CLASS_MAX10 = CLASS_LINUX_AMD64; // This should not be changed, no new ports with ODS10
|
|
static const int CLASS_MAX = CLASS_LINUX_MIPSEL;
|
|
|
|
// ARCHITECTURE COMPATIBILITY CLASSES
|
|
|
|
// For ODS10 and earlier things which normally define ODS compatibility are:
|
|
// 1) endianness (big-endian/little-endian)
|
|
// 2) alignment (32-bit or 64-bit), matters for record formats
|
|
// 3) pointer size (32-bit or 64-bit), also matters for record formats
|
|
//
|
|
// For ODS11 pointers are not stored in database and alignment is always 64-bit.
|
|
// So the only thing which normally matters for ODS11 is endiannes, but if
|
|
// endianness is wrong we are going to notice it during ODS version check,
|
|
// before architecture compatibility is tested. But we distinguish them here too,
|
|
// for consistency.
|
|
|
|
enum ArchitectureType {
|
|
archUnknown, // Unknown architecture, allow opening database only if CLASS matches exactly
|
|
archIntel86, // Little-endian platform with 32-bit pointers and 32-bit alignment (ODS10)
|
|
archLittleEndian, // Any little-endian platform with standard layout of data
|
|
archBigEndian // Any big-endian platform with standard layout of data
|
|
};
|
|
|
|
// Note that Sparc, HP and PowerPC disk structures should be compatible in theory,
|
|
// but in practice alignment on these platforms varies and actually depends on the
|
|
// compiler used to produce the build. Yes, some 32-bit RISC builds use 64-bit alignment.
|
|
// This is why we declare all such builds "Unknown" for ODS10.
|
|
|
|
static ArchitectureType archMatrix10[CLASS_MAX10 + 1] = {
|
|
archUnknown, // CLASS_UNKNOWN
|
|
archUnknown, // CLASS_APOLLO_68K
|
|
archUnknown, // CLASS_SOLARIS_SPARC
|
|
archIntel86, // CLASS_SOLARIS_I386
|
|
archUnknown, // CLASS_VMS_VAX
|
|
archUnknown, // CLASS_ULTRIX_VAX
|
|
archUnknown, // CLASS_ULTRIX_MIPS
|
|
archUnknown, // CLASS_HPUX_PA
|
|
archUnknown, // CLASS_NETWARE_I386
|
|
archUnknown, // CLASS_MAC_OS
|
|
archUnknown, // CLASS_AIX_PPC
|
|
archUnknown, // CLASS_DG_AVIION
|
|
archUnknown, // CLASS_MPE_XL
|
|
archUnknown, // CLASS_IRIX_MIPS
|
|
archUnknown, // CLASS_CRAY
|
|
archUnknown, // CLASS_TRU64_ALPHA
|
|
archIntel86, // CLASS_WINDOWS_I386
|
|
archUnknown, // CLASS_OS2
|
|
archUnknown, // CLASS_WIN16
|
|
archIntel86, // CLASS_LINUX_I386
|
|
archUnknown, // CLASS_LINUX_SPARC
|
|
archIntel86, // CLASS_FREEBSD_I386
|
|
archIntel86, // CLASS_NETBSD_I386
|
|
archUnknown, // CLASS_DARWIN_PPC
|
|
archUnknown // CLASS_LINUX_AMD64
|
|
};
|
|
|
|
static ArchitectureType archMatrix[CLASS_MAX + 1] = {
|
|
archUnknown, // CLASS_UNKNOWN
|
|
archUnknown, // CLASS_APOLLO_68K
|
|
archBigEndian, // CLASS_SOLARIS_SPARC
|
|
archLittleEndian, // CLASS_SOLARIS_I386
|
|
archUnknown, // CLASS_VMS_VAX
|
|
archUnknown, // CLASS_ULTRIX_VAX
|
|
archUnknown, // CLASS_ULTRIX_MIPS
|
|
archBigEndian, // CLASS_HPUX_PA
|
|
archUnknown, // CLASS_NETWARE_I386
|
|
archUnknown, // CLASS_MAC_OS
|
|
archBigEndian, // CLASS_AIX_PPC
|
|
archUnknown, // CLASS_DG_AVIION
|
|
archUnknown, // CLASS_MPE_XL
|
|
archBigEndian, // CLASS_IRIX_MIPS
|
|
archUnknown, // CLASS_CRAY
|
|
archBigEndian, // CLASS_TRU64_ALPHA
|
|
archLittleEndian, // CLASS_WINDOWS_I386
|
|
archUnknown, // CLASS_OS2
|
|
archUnknown, // CLASS_WIN16
|
|
archLittleEndian, // CLASS_LINUX_I386
|
|
archBigEndian, // CLASS_LINUX_SPARC
|
|
archLittleEndian, // CLASS_FREEBSD_I386
|
|
archLittleEndian, // CLASS_NETBSD_I386
|
|
archBigEndian, // CLASS_DARWIN_PPC
|
|
archLittleEndian, // CLASS_LINUX_AMD64
|
|
archLittleEndian, // CLASS_FREEBSD_AMD64
|
|
archLittleEndian, // CLASS_WINDOWS_AMD64
|
|
archBigEndian, // CLASS_LINUX_PPC
|
|
archLittleEndian, // CLASS_DARWIN_I386
|
|
archLittleEndian // CLASS_LINUX_MIPSEL
|
|
};
|
|
|
|
#ifdef sun
|
|
#ifdef i386
|
|
const SSHORT CLASS = CLASS_SOLARIS_I386;
|
|
#else
|
|
const SSHORT CLASS = CLASS_SOLARIS_SPARC;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef hpux
|
|
const SSHORT CLASS = CLASS_HPUX_PA;
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
const SSHORT CLASS = CLASS_VMS_VAX;
|
|
#endif
|
|
|
|
#ifdef AIX
|
|
const SSHORT CLASS = CLASS_AIX_PPC;
|
|
#endif
|
|
|
|
#ifdef AIX_PPC
|
|
const SSHORT CLASS = CLASS_AIX_PPC;
|
|
#endif
|
|
|
|
#ifdef WIN_NT
|
|
#if defined(I386)
|
|
const SSHORT CLASS = CLASS_WINDOWS_I386;
|
|
#elif defined(AMD64)
|
|
const SSHORT CLASS = CLASS_WINDOWS_AMD64;
|
|
#else
|
|
#error no support on other hardware for Windows
|
|
#endif
|
|
#endif // WIN_NT
|
|
|
|
#ifdef SINIXZ
|
|
const SSHORT CLASS = CLASS_LINUX_I386;
|
|
#endif
|
|
|
|
#ifdef LINUX
|
|
#if defined(i386) || defined(i586)
|
|
const SSHORT CLASS = CLASS_LINUX_I386;
|
|
#elif defined(sparc)
|
|
const SSHORT CLASS = CLASS_LINUX_SPARC;
|
|
#elif defined(AMD64)
|
|
const SSHORT CLASS = CLASS_LINUX_AMD64;
|
|
#elif defined(PPC)
|
|
const SSHORT CLASS = CLASS_LINUX_PPC;
|
|
#elif defined(MIPSEL)
|
|
const SSHORT CLASS = CLASS_LINUX_MIPSEL;
|
|
#else
|
|
#error no support on other hardware for Linux
|
|
#endif
|
|
#endif // LINUX
|
|
|
|
#ifdef FREEBSD
|
|
#if defined(i386)
|
|
const SSHORT CLASS = CLASS_FREEBSD_I386;
|
|
#elif defined(AMD64)
|
|
const SSHORT CLASS = CLASS_FREEBSD_AMD64;
|
|
#else
|
|
#error no support on other hardware for FreeBSD
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef NETBSD
|
|
const SSHORT CLASS = CLASS_NETBSD_I386;
|
|
#endif
|
|
|
|
#ifdef DARWIN
|
|
#ifdef i386
|
|
const SSHORT CLASS = CLASS_DARWIN_I386;
|
|
#endif
|
|
#ifdef powerpc
|
|
const SSHORT CLASS = CLASS_DARWIN_PPC;
|
|
#endif
|
|
#endif
|
|
static const char* const SCRATCH = "fb_table_";
|
|
|
|
static const int MIN_EXTEND_BYTES = 128 * 1024; // 128KB
|
|
|
|
// 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(DB_PAGE_SPACE, 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;
|
|
if (len)
|
|
{
|
|
if (must_write)
|
|
CCH_MARK_MUST_WRITE(tdbb, &window);
|
|
else
|
|
CCH_MARK(tdbb, &window);
|
|
|
|
memcpy(entry_p, entry, len);
|
|
}
|
|
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)
|
|
memmove(entry_p, r, 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 */
|
|
|
|
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
jrd_file* file = pageSpace->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, pageSpace->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 | DBB_no_fs_cache))
|
|
{
|
|
PIO_force_write(next,
|
|
dbb->dbb_flags & DBB_force_write,
|
|
dbb->dbb_flags & DBB_no_fs_cache);
|
|
}
|
|
|
|
WIN window(DB_PAGE_SPACE, 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. */
|
|
*(ISC_TIMESTAMP*)header->hdr_creation_date = Firebird::TimeStamp().value();
|
|
// should we include milliseconds or not?
|
|
//Firebird::TimeStamp::round_time(header->hdr_creation_date->timestamp_time, 0);
|
|
|
|
header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG;
|
|
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(pageSpace->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(pageSpace->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, USHORT 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);
|
|
|
|
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 (entry)
|
|
memcpy(p, entry, len);
|
|
else
|
|
memset(p, 0, len);
|
|
|
|
p += len;
|
|
}
|
|
|
|
*p = HDR_end;
|
|
|
|
header->hdr_end = p - (UCHAR *) header;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BUGCHECK(251);
|
|
return FALSE; /* Added to remove compiler warning */
|
|
}
|
|
|
|
|
|
void PAG_attach_temp_pages(thread_db* tdbb, USHORT pageSpaceID)
|
|
{
|
|
/***********************************************
|
|
*
|
|
* P A G _ a t t a c h _ t e m p _ p a g e s
|
|
*
|
|
***********************************************
|
|
*
|
|
* Functional description
|
|
* Attach a temporary page space
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->tdbb_database;
|
|
CHECK_DBB(dbb);
|
|
|
|
PageSpace* pageSpaceTemp = dbb->dbb_page_manager.addPageSpace(pageSpaceID);
|
|
if (!pageSpaceTemp->file)
|
|
{
|
|
Firebird::PathName file_name = TempFile::create(SCRATCH);
|
|
pageSpaceTemp->file = PIO_create(dbb, file_name, true, true, false);
|
|
PAG_format_pip(tdbb, *pageSpaceTemp);
|
|
}
|
|
}
|
|
|
|
|
|
int PAG_replace_entry_first(header_page* header, USHORT type, USHORT 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);
|
|
|
|
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);
|
|
|
|
PageManager& pageMgr = dbb->dbb_page_manager;
|
|
PageSpace* pageSpace = pageMgr.findPageSpace(
|
|
window->win_page.getPageSpaceID());
|
|
fb_assert(pageSpace);
|
|
|
|
// Not sure if this can be moved inside the loop. Maybe some data members
|
|
// should persist across iterations?
|
|
WIN pip_window(pageSpace->pageSpaceID, -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.
|
|
|
|
// in ODS 11.1 we store in pip_header.reserved number of pages allocated
|
|
// from this pointer page
|
|
const bool isODS11_1 = (dbb->dbb_ods_version == ODS_VERSION11 &&
|
|
dbb->dbb_minor_version == 1);
|
|
|
|
/* Find an allocation page with something on it */
|
|
|
|
SLONG relative_bit = -1;
|
|
SLONG sequence;
|
|
SLONG pipMin;
|
|
for (sequence = pageSpace->pipHighWater;; sequence++) {
|
|
pip_window.win_page = (sequence == 0) ?
|
|
pageSpace->ppFirst : sequence * dbb->dbb_page_manager.pagesPerPIP - 1;
|
|
page_inv_page* pip_page =
|
|
(page_inv_page*) CCH_FETCH(tdbb, &pip_window, LCK_write, pag_pages);
|
|
|
|
pipMin = MAX_SLONG;
|
|
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;
|
|
pipMin = MIN(pipMin, relative_bit);
|
|
|
|
const SLONG pageNum = relative_bit + sequence * pageMgr.pagesPerPIP;
|
|
window->win_page = pageNum;
|
|
new_page = CCH_fake(tdbb, window, 0); /* don't wait on latch */
|
|
if (new_page)
|
|
{
|
|
if (isODS11_1)
|
|
{
|
|
USHORT next_init_pages = 1;
|
|
// ensure there are space on disk for faked page
|
|
if (relative_bit + 1 > pip_page->pip_header.reserved)
|
|
{
|
|
fb_assert(relative_bit == pip_page->pip_header.reserved);
|
|
|
|
USHORT init_pages = 1;
|
|
if (!(dbb->dbb_flags & DBB_no_reserve))
|
|
{
|
|
const int minExtendPages = MIN_EXTEND_BYTES / dbb->dbb_page_size;
|
|
init_pages = sequence ? 64 : MIN(pip_page->pip_header.reserved / 16, 64);
|
|
|
|
// don't touch pages belongs to the next PIP
|
|
init_pages =
|
|
MIN(init_pages, pageMgr.pagesPerPIP - pip_page->pip_header.reserved);
|
|
|
|
if (init_pages < minExtendPages)
|
|
init_pages = 1;
|
|
|
|
next_init_pages = init_pages;
|
|
}
|
|
|
|
ISC_STATUS_ARRAY status;
|
|
const ULONG start =
|
|
sequence * pageMgr.pagesPerPIP + pip_page->pip_header.reserved;
|
|
|
|
init_pages = PIO_init_data(dbb, pageSpace->file, status, start, init_pages);
|
|
|
|
if (init_pages) {
|
|
pip_page->pip_header.reserved += init_pages;
|
|
}
|
|
else
|
|
{
|
|
// PIO_init_data returns zero - perhaps it is not supported, no space
|
|
// left on disk or IO error occured. Try to write one page and handle
|
|
// IO errors if any
|
|
CCH_must_write(window);
|
|
try {
|
|
CCH_RELEASE(tdbb, window);
|
|
pip_page->pip_header.reserved = relative_bit + 1;
|
|
}
|
|
catch (Firebird::status_exception)
|
|
{
|
|
// forget about this page as if we never tried to fake it
|
|
CCH_forget_page(tdbb, window);
|
|
|
|
// normally all page buffers now released by CCH_unwind
|
|
// only exception is when TDBB_no_cache_unwind flag is set
|
|
if (tdbb->tdbb_flags & TDBB_no_cache_unwind)
|
|
CCH_RELEASE(tdbb, &pip_window);
|
|
|
|
throw;
|
|
}
|
|
|
|
new_page = CCH_fake(tdbb, window, 1);
|
|
}
|
|
fb_assert(new_page);
|
|
}
|
|
|
|
if (!(dbb->dbb_flags & DBB_no_reserve))
|
|
{
|
|
const ULONG initialized =
|
|
sequence * pageMgr.pagesPerPIP + pip_page->pip_header.reserved;
|
|
|
|
// At this point we ensure database has at least "initialized" pages
|
|
// allocated. To avoid file growth by few pages when all this space
|
|
// will be used, extend file up to initialized + next_init_pages now
|
|
pageSpace->extend(tdbb, initialized + next_init_pages);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
pageSpace->pipHighWater = sequence;
|
|
|
|
CCH_MARK(tdbb, &pip_window);
|
|
*bytes &= ~bit;
|
|
page_inv_page* pip_page = (page_inv_page*) pip_window.win_buffer;
|
|
|
|
if (pipMin == relative_bit)
|
|
pipMin++;
|
|
pip_page->pip_min = pipMin;
|
|
|
|
if (relative_bit != pageMgr.pagesPerPIP - 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;
|
|
const UCHAR* end = (UCHAR *) new_pip_page + dbb->dbb_page_size;
|
|
memset(new_pip_page->pip_bits, 0xff, end - new_pip_page->pip_bits);
|
|
|
|
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(thread_db* tdbb)
|
|
{
|
|
/******************************************
|
|
*
|
|
* 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.
|
|
*
|
|
******************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
Attachment* attachment = tdbb->tdbb_attachment;
|
|
WIN window(DB_PAGE_SPACE, -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_NUMBER;
|
|
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, LCK_WAIT);
|
|
|
|
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(DB_PAGE_SPACE, 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)
|
|
memmove(entry_p, r, l);
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void PAG_format_header()
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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_NUMBER);
|
|
header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
|
|
header->hdr_header.pag_scn = 0;
|
|
*(ISC_TIMESTAMP*)header->hdr_creation_date = Firebird::TimeStamp().value();
|
|
// should we include milliseconds or not?
|
|
//Firebird::TimeStamp::round_time(header->hdr_creation_date->timestamp_time, 0);
|
|
header->hdr_header.pag_type = pag_header;
|
|
header->hdr_page_size = dbb->dbb_page_size;
|
|
header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG;
|
|
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_FIREBIRD_FLAG;
|
|
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()
|
|
{
|
|
/***********************************************
|
|
*
|
|
* 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_NUMBER);
|
|
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(thread_db* tdbb, PageSpace& pageSpace)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->tdbb_database;
|
|
CHECK_DBB(dbb);
|
|
|
|
/* Initialize Page Inventory Page */
|
|
|
|
WIN window(pageSpace.pageSpaceID, 1);
|
|
pageSpace.ppFirst = 1;
|
|
page_inv_page* pages = (page_inv_page*) CCH_fake(tdbb, &window, 1);
|
|
|
|
pages->pip_header.pag_type = pag_pages;
|
|
pages->pip_min = 4;
|
|
pages->pip_header.reserved = pages->pip_min - 1;
|
|
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* inout_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 inout_len <-> input and output value to avoid B.O.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
WIN window(DB_PAGE_SPACE, 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);
|
|
*inout_len = 0;
|
|
return false;
|
|
}
|
|
|
|
USHORT old_len = *inout_len;
|
|
*inout_len = entry_p[1];
|
|
entry_p += 2;
|
|
|
|
if (*inout_len)
|
|
{
|
|
// Avoid the B.O. but inform the caller the buffer is bigger.
|
|
if (*inout_len < old_len)
|
|
old_len = *inout_len;
|
|
memcpy(entry, entry_p, old_len);
|
|
}
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void PAG_header(bool info)
|
|
{
|
|
/**************************************
|
|
*
|
|
* P A G _ h e a d e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Checkout database header page.
|
|
* Done through the page cache.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
Attachment* attachment = tdbb->tdbb_attachment;
|
|
fb_assert(attachment);
|
|
|
|
WIN window(HEADER_PAGE_NUMBER);
|
|
header_page* header =
|
|
(header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
|
|
|
|
try {
|
|
|
|
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 */
|
|
}
|
|
|
|
if (header->hdr_flags & hdr_SQL_dialect_3)
|
|
dbb->dbb_flags |= DBB_DB_SQL_dialect_3;
|
|
|
|
jrd_rel* relation = MET_relation(tdbb, 0);
|
|
RelationPages* relPages = relation->getBasePages();
|
|
if (!relPages->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);
|
|
relPages->rel_pages = vector;
|
|
(*vector)[0] = header->hdr_PAGES;
|
|
}
|
|
|
|
dbb->dbb_next_transaction = header->hdr_next_transaction;
|
|
|
|
if (!info || dbb->dbb_oldest_transaction < header->hdr_oldest_transaction) {
|
|
dbb->dbb_oldest_transaction = header->hdr_oldest_transaction;
|
|
}
|
|
if (!info || dbb->dbb_oldest_active < header->hdr_oldest_active) {
|
|
dbb->dbb_oldest_active = header->hdr_oldest_active;
|
|
}
|
|
if (!info || dbb->dbb_oldest_snapshot < header->hdr_oldest_snapshot) {
|
|
dbb->dbb_oldest_snapshot = header->hdr_oldest_snapshot;
|
|
}
|
|
|
|
dbb->dbb_attachment_id = header->hdr_attachment_id;
|
|
dbb->dbb_creation_date = *(ISC_TIMESTAMP*) header->hdr_creation_date;
|
|
|
|
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_string, ERR_string(attachment->att_filename),
|
|
0);
|
|
}
|
|
|
|
const bool useFSCache = dbb->dbb_bcb->bcb_count < Config::getMaxFileSystemCache();
|
|
|
|
if ((header->hdr_flags & hdr_force_write) || !useFSCache)
|
|
{
|
|
dbb->dbb_flags |=
|
|
(header->hdr_flags & hdr_force_write ? DBB_force_write : 0) |
|
|
(useFSCache ? 0 : DBB_no_fs_cache);
|
|
|
|
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
for (jrd_file* file = pageSpace->file; file; file = file->fil_next) {
|
|
PIO_force_write(file,
|
|
(dbb->dbb_flags & DBB_force_write) && !(header->hdr_flags & hdr_read_only),
|
|
dbb->dbb_flags & DBB_no_fs_cache);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
} // try
|
|
catch (const Firebird::Exception&) {
|
|
CCH_RELEASE(tdbb, &window);
|
|
throw;
|
|
}
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
}
|
|
|
|
|
|
void PAG_header_init()
|
|
{
|
|
/**************************************
|
|
*
|
|
* P A G _ h e a d e r _ i n i t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Checkout the core part of the database header page.
|
|
* It includes the fields required to setup the I/O layer:
|
|
* ODS version, page size, page buffers.
|
|
* Done using a physical page read.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
Attachment* attachment = tdbb->tdbb_attachment;
|
|
fb_assert(attachment);
|
|
|
|
// 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 temp_buffer[2 * MIN_PAGE_SIZE];
|
|
SCHAR* temp_page = (SCHAR *) FB_ALIGN((IPTR) temp_buffer, MIN_PAGE_SIZE);
|
|
|
|
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_string, ERR_string(attachment->att_filename),
|
|
0);
|
|
}
|
|
|
|
const USHORT ods_version = header->hdr_ods_version & ~ODS_FIREBIRD_FLAG;
|
|
|
|
if (!Ods::isSupported(header->hdr_ods_version, header->hdr_ods_minor))
|
|
{
|
|
ERR_post(isc_wrong_ods,
|
|
isc_arg_string, ERR_string(attachment->att_filename),
|
|
isc_arg_number, (SLONG) ods_version,
|
|
isc_arg_number, (SLONG) header->hdr_ods_minor,
|
|
isc_arg_number, (SLONG) ODS_VERSION,
|
|
isc_arg_number, (SLONG) ODS_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
|
|
//
|
|
// Re-enable and recode the check to avoid BUGCHECK messages when database
|
|
// is accessed with engine built for another architecture. - Nickolay 9-Feb-2005
|
|
|
|
if (header->hdr_implementation != CLASS &&
|
|
ods_version < ODS_VERSION11 ?
|
|
(header->hdr_implementation < 0 || header->hdr_implementation > CLASS_MAX10 ||
|
|
archMatrix10[header->hdr_implementation] == archUnknown ||
|
|
archMatrix10[header->hdr_implementation] != archMatrix10[CLASS])
|
|
:
|
|
(header->hdr_implementation < 0 || header->hdr_implementation > CLASS_MAX ||
|
|
archMatrix[header->hdr_implementation] == archUnknown ||
|
|
archMatrix[header->hdr_implementation] != archMatrix[CLASS])
|
|
)
|
|
{
|
|
ERR_post(isc_bad_db_format,
|
|
isc_arg_string, ERR_string(attachment->att_filename),
|
|
0);
|
|
}
|
|
|
|
if (header->hdr_page_size < MIN_PAGE_SIZE ||
|
|
header->hdr_page_size > MAX_PAGE_SIZE)
|
|
{
|
|
ERR_post(isc_bad_db_format,
|
|
isc_arg_string, ERR_string(attachment->att_filename),
|
|
0);
|
|
}
|
|
|
|
dbb->dbb_ods_version = ods_version;
|
|
dbb->dbb_minor_version = header->hdr_ods_minor;
|
|
dbb->dbb_minor_original = header->hdr_ods_minor_original;
|
|
|
|
dbb->dbb_page_size = header->hdr_page_size;
|
|
dbb->dbb_page_buffers = header->hdr_page_buffers;
|
|
}
|
|
|
|
|
|
void PAG_init()
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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);
|
|
|
|
PageManager& pageMgr = dbb->dbb_page_manager;
|
|
PageSpace* pageSpace = pageMgr.findPageSpace(DB_PAGE_SPACE);
|
|
fb_assert(pageSpace);
|
|
|
|
pageMgr.bytesBitPIP = dbb->dbb_page_size - OFFSETA(page_inv_page*, pip_bits);
|
|
pageMgr.pagesPerPIP = pageMgr.bytesBitPIP * 8;
|
|
pageMgr.transPerTIP =
|
|
(dbb->dbb_page_size - OFFSETA(tx_inv_page*, tip_transactions)) * 4;
|
|
pageSpace->ppFirst = 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))
|
|
{
|
|
pageMgr.gensPerPage =
|
|
(dbb->dbb_page_size -
|
|
OFFSETA(generator_page*, gpg_values)) / sizeof(((generator_page*) NULL)->gpg_values);
|
|
}
|
|
else {
|
|
pageMgr.gensPerPage =
|
|
(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));
|
|
|
|
// Artifically reduce density of records to test high bits of record number
|
|
// dbb->dbb_max_records = 32000;
|
|
|
|
/* Optimize record numbers for new 64-bit sparse bitmap implementation
|
|
We need to measure if it is beneficial from performance point of view.
|
|
Price is slightly reduced density of record numbers, but for
|
|
ODS11 it doesn't matter because record numbers are 40-bit.
|
|
Benefit is ~1.5 times smaller sparse bitmaps on average and
|
|
faster bitmap iteration. */
|
|
// if (dbb->dbb_ods_version >= ODS_VERSION11)
|
|
// dbb->dbb_max_records = FB_ALIGN(dbb->dbb_max_records, 64);
|
|
|
|
/* 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. */
|
|
#ifdef SUPERSERVER_V2
|
|
dbb->dbb_prefetch_sequence = PREFETCH_MAX_TRANSFER / dbb->dbb_page_size;
|
|
dbb->dbb_prefetch_pages = dbb->dbb_prefetch_sequence * 2;
|
|
#endif
|
|
}
|
|
|
|
|
|
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 {
|
|
|
|
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
jrd_file* file = pageSpace->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(DB_PAGE_SPACE, -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 UCHAR* p = 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:
|
|
// CVC: Let's copy it always.
|
|
//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, false, file_name, false);
|
|
file->fil_max_page = last_page;
|
|
file = file->fil_next;
|
|
if (dbb->dbb_flags & (DBB_force_write | DBB_no_fs_cache))
|
|
{
|
|
PIO_force_write(file,
|
|
dbb->dbb_flags & DBB_force_write,
|
|
dbb->dbb_flags & DBB_no_fs_cache);
|
|
}
|
|
file->fil_min_page = last_page + 1;
|
|
file->fil_sequence = sequence++;
|
|
}
|
|
|
|
if (temp_buffer)
|
|
delete[] temp_buffer;
|
|
} // try
|
|
catch (const Firebird::Exception&) {
|
|
if (temp_buffer)
|
|
delete[] temp_buffer;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
SLONG PAG_last_page()
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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);
|
|
|
|
PageManager& pageMgr = dbb->dbb_page_manager;
|
|
PageSpace* pageSpace = pageMgr.findPageSpace(DB_PAGE_SPACE);
|
|
fb_assert(pageSpace);
|
|
|
|
const ULONG pages_per_pip = pageMgr.pagesPerPIP;
|
|
WIN window(DB_PAGE_SPACE, -1);
|
|
|
|
/* Find the last page allocated */
|
|
|
|
ULONG relative_bit = 0;
|
|
USHORT sequence;
|
|
for (sequence = 0;; ++sequence) {
|
|
window.win_page =
|
|
(!sequence) ? pageSpace->ppFirst : 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(const PageNumber& number, const PageNumber& 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.getPageNumber());
|
|
#endif
|
|
|
|
PageManager& pageMgr = dbb->dbb_page_manager;
|
|
PageSpace* pageSpace = pageMgr.findPageSpace(number.getPageSpaceID());
|
|
fb_assert(pageSpace);
|
|
|
|
const SLONG sequence = number.getPageNum() / pageMgr.pagesPerPIP;
|
|
const SLONG relative_bit = number.getPageNum() % pageMgr.pagesPerPIP;
|
|
|
|
WIN pip_window(number.getPageSpaceID(), (sequence == 0) ?
|
|
pageSpace->ppFirst : sequence * pageMgr.pagesPerPIP - 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);
|
|
|
|
pageSpace->pipHighWater = MIN(pageSpace->pipHighWater, 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_NUMBER);
|
|
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);
|
|
|
|
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
for (jrd_file* file = pageSpace->file; file; file = file->fil_next) {
|
|
PIO_force_write(file, flag != 0,
|
|
dbb->dbb_flags & DBB_no_fs_cache);
|
|
}
|
|
|
|
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,
|
|
dbb->dbb_flags & DBB_no_fs_cache);
|
|
}
|
|
}
|
|
|
|
|
|
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_NUMBER);
|
|
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_NUMBER);
|
|
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_NUMBER);
|
|
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_NUMBER);
|
|
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()
|
|
{
|
|
// **************************************
|
|
// *
|
|
// * 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);
|
|
|
|
SLONG count;
|
|
USHORT len = sizeof(count);
|
|
if (PAG_get_clump(HEADER_PAGE, HDR_unlicensed, &len, (UCHAR *) &count)) {
|
|
fb_assert(sizeof(count) == len);
|
|
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,
|
|
USHORT 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);
|
|
|
|
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)
|
|
{
|
|
memcpy(p, entry, len);
|
|
p += 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(DB_PAGE_SPACE, -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.getPageNum();
|
|
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.getPageNum();
|
|
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)
|
|
{
|
|
memcpy(p, entry, len);
|
|
p += 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;
|
|
}
|
|
}
|
|
|
|
PageSpace::~PageSpace()
|
|
{
|
|
if (file) {
|
|
PIO_close(file);
|
|
|
|
while (file)
|
|
{
|
|
jrd_file* next = file->fil_next;
|
|
delete file;
|
|
file = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
ULONG PageSpace::actAlloc(const USHORT pageSize)
|
|
{
|
|
/**************************************
|
|
*
|
|
* Functional description
|
|
* Compute actual number of physically allocated pages of database.
|
|
*
|
|
**************************************/
|
|
|
|
// Traverse the linked list of files and add up the
|
|
// number of pages in each file
|
|
ULONG tot_pages = 0;
|
|
for (const jrd_file* f = file; f != NULL; f = f->fil_next) {
|
|
tot_pages += PIO_get_number_of_pages(f, pageSize);
|
|
}
|
|
|
|
return tot_pages;
|
|
}
|
|
|
|
ULONG PageSpace::actAlloc(const Database* dbb)
|
|
{
|
|
PageSpace* pgSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
return pgSpace->actAlloc(dbb->dbb_page_size);
|
|
}
|
|
|
|
ULONG PageSpace::maxAlloc(const USHORT pageSize)
|
|
{
|
|
/**************************************
|
|
*
|
|
* Functional description
|
|
* Compute last physically allocated page of database.
|
|
*
|
|
**************************************/
|
|
const jrd_file* f = file;
|
|
while (f->fil_next) {
|
|
f = f->fil_next;
|
|
}
|
|
|
|
const ULONG nPages = f->fil_min_page - f->fil_fudge +
|
|
PIO_get_number_of_pages(f, pageSize);
|
|
|
|
if (maxPageNumber < nPages)
|
|
maxPageNumber = nPages;
|
|
|
|
return nPages;
|
|
}
|
|
|
|
ULONG PageSpace::maxAlloc(const Database* dbb)
|
|
{
|
|
PageSpace* pgSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
return pgSpace->maxAlloc(dbb->dbb_page_size);
|
|
}
|
|
|
|
bool PageSpace::extend(thread_db* tdbb, const ULONG pageNum)
|
|
{
|
|
/**************************************
|
|
*
|
|
* Functional description
|
|
* Extend database file(s) up to at least pageNum pages. Number of pages to
|
|
* extend can't be less than hardcoded value MIN_EXTEND_BYTES and more than
|
|
* configured value "DatabaseGrowthIncrement" (both values in bytes).
|
|
*
|
|
* If "DatabaseGrowthIncrement" is less than MIN_EXTEND_BYTES then don't
|
|
* extend file(s)
|
|
*
|
|
**************************************/
|
|
const int MAX_EXTEND_BYTES = Config::getDatabaseGrowthIncrement();
|
|
|
|
if (pageNum < maxPageNumber || MAX_EXTEND_BYTES < MIN_EXTEND_BYTES)
|
|
return true;
|
|
|
|
#ifdef WIN_NT
|
|
// hvlad: we need an attachment lock to get exclusive LCK_file_extend lock
|
|
// see comments in winnt.cpp\FileExtendLock::FileExtendLock
|
|
if (!tdbb->tdbb_attachment || !tdbb->tdbb_attachment->att_lock_owner_handle)
|
|
return false;
|
|
#endif
|
|
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
if (pageNum >= maxAlloc(dbb->dbb_page_size))
|
|
{
|
|
const ULONG minExtendPages = MIN_EXTEND_BYTES / dbb->dbb_page_size;
|
|
const ULONG maxExtendPages = MAX_EXTEND_BYTES / dbb->dbb_page_size;
|
|
const ULONG reqPages = pageNum - maxPageNumber + 1;
|
|
|
|
ULONG extPages;
|
|
extPages = MIN(MAX(maxPageNumber / 16, minExtendPages), maxExtendPages);
|
|
extPages = MAX(reqPages, extPages);
|
|
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
PIO_extend(file, extPages, dbb->dbb_page_size);
|
|
break;
|
|
}
|
|
catch (Firebird::status_exception)
|
|
{
|
|
if (extPages > reqPages)
|
|
{
|
|
extPages = MAX(reqPages, extPages / 2);
|
|
INIT_STATUS(tdbb->tdbb_status_vector);
|
|
}
|
|
else
|
|
{
|
|
gds__log("Error extending file \"%s\" by %lu page(s).\nCurrently allocated %lu pages, requested page number %lu",
|
|
file->fil_string, extPages, maxPageNumber, pageNum);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
maxPageNumber = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
PageSpace* PageManager::addPageSpace(const USHORT pageSpaceID)
|
|
{
|
|
PageSpace* newPageSpace = findPageSpace(pageSpaceID);
|
|
if (!newPageSpace)
|
|
{
|
|
newPageSpace = FB_NEW(pool) PageSpace(pageSpaceID);
|
|
pageSpaces.add(newPageSpace);
|
|
}
|
|
|
|
return newPageSpace;
|
|
}
|
|
|
|
PageSpace* PageManager::findPageSpace(const USHORT pageSpace) const
|
|
{
|
|
size_t pos;
|
|
if (pageSpaces.find(pageSpace, pos)) {
|
|
return pageSpaces[pos];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void PageManager::delPageSpace(const USHORT pageSpace)
|
|
{
|
|
size_t pos;
|
|
if (pageSpaces.find(pageSpace, pos))
|
|
{
|
|
PageSpace* pageSpaceToDelete = pageSpaces[pos];
|
|
pageSpaces.remove(pos);
|
|
delete pageSpaceToDelete;
|
|
}
|
|
}
|
|
|
|
void PageManager::closeAll()
|
|
{
|
|
for (size_t i = 0; i < pageSpaces.getCount(); i++)
|
|
if (pageSpaces[i]->file) {
|
|
PIO_close(pageSpaces[i]->file);
|
|
}
|
|
}
|
|
|
|
void PageManager::releaseLocks()
|
|
{
|
|
#ifdef WIN_NT
|
|
for (size_t i = 0; i < pageSpaces.getCount(); i++)
|
|
{
|
|
if (pageSpaces[i]->file && pageSpaces[i]->file->fil_ext_lock) {
|
|
delete pageSpaces[i]->file->fil_ext_lock;
|
|
pageSpaces[i]->file->fil_ext_lock = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
USHORT PageManager::getTempPageSpaceID(thread_db* tdbb)
|
|
{
|
|
#ifdef SUPERSERVER
|
|
return TEMP_PAGE_SPACE;
|
|
#else
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->tdbb_database;
|
|
Attachment* att = tdbb->tdbb_attachment;
|
|
if (!att->att_temp_pg_lock)
|
|
{
|
|
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(USHORT)) Lock();
|
|
lock->lck_type = LCK_page_space;
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
lock->lck_length = sizeof(USHORT);
|
|
lock->lck_dbb = dbb;
|
|
|
|
PAG_attachment_id(tdbb);
|
|
|
|
while (true)
|
|
{
|
|
const double tmp = rand() * (MAX_USHORT - TEMP_PAGE_SPACE - 1.0) / (RAND_MAX + 1.0);
|
|
lock->lck_key.lck_long = static_cast<SLONG>(tmp) + TEMP_PAGE_SPACE + 1;
|
|
if (LCK_lock(tdbb, lock, LCK_write, LCK_NO_WAIT))
|
|
break;
|
|
}
|
|
|
|
att->att_temp_pg_lock = lock;
|
|
}
|
|
|
|
return (USHORT) att->att_temp_pg_lock->lck_key.lck_long;
|
|
#endif
|
|
}
|