8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 13:23:02 +01:00
firebird-mirror/src/jrd/pag.cpp

2534 lines
70 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: pag.cpp
2001-05-23 15:26:42 +02:00
* 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-16 03:21:35 +01:00
*
2009-08-22 14:16:47 +02:00
* 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
2002-02-16 06:06:17 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete ports:
* - EPSON, DELTA, IMP, NCR3000, M88K
* - HP9000 s300 and Apollo
2002-02-16 03:21:35 +01:00
*
* 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-29 03:45:09 +01:00
* 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 03:45:09 +01:00
*
2002-10-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2004-04-29 00:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
2006-05-22 00:07:35 +02:00
#ifdef WIN_NT
#include <process.h>
#endif
#include "../common/config/config.h"
2008-02-14 12:52:59 +01:00
#include "../common/utils_proto.h"
2006-05-22 00:07:35 +02:00
#include "../jrd/fil.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/pag.h"
#include "../jrd/ods.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio.h"
#include "../jrd/os/path_utils.h"
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/gdsassert.h"
#include "../jrd/lck.h"
#include "../jrd/sdw.h"
#include "../jrd/cch.h"
#include "../jrd/nbak.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/tra.h"
#ifdef VIO_DEBUG
#include "../jrd/vio_debug.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"
2001-05-23 15:26:42 +02:00
#include "../jrd/pag_proto.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio_proto.h"
#include "../jrd/thread_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../jrd/TempSpace.h"
#include "../jrd/extds/ExtDS.h"
2001-05-23 15:26:42 +02:00
using namespace Jrd;
using namespace Ods;
using namespace Firebird;
static int blocking_ast_attachment(void*);
2009-01-07 10:30:57 +01:00
static void find_clump_space(thread_db* tdbb, SLONG, WIN*, pag**, USHORT, USHORT, const UCHAR*);
static bool find_type(thread_db* tdbb, SLONG, WIN*, pag**, USHORT, USHORT, UCHAR**, const UCHAR**);
2001-05-23 15:26:42 +02:00
2006-04-23 10:25:10 +02:00
inline void err_post_if_database_is_readonly(const Database* dbb)
{
if (dbb->dbb_flags & DBB_read_only)
ERR_post(Arg::Gds(isc_read_only_database));
2006-04-23 10:25:10 +02:00
}
2001-05-23 15:26:42 +02:00
// Class definitions (obsolete platforms are commented out)
// Class constant name consists of OS platform and CPU architecture.
//
2008-12-05 02:20:14 +01:00
// 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.
2005-02-10 00:21:29 +01:00
static const int CLASS_UNKNOWN = 0;
2009-08-22 14:16:47 +02:00
//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
2008-04-18 03:37:44 +02:00
static const int CLASS_LINUX_MIPSEL = 29; // LINUX/MIPSEL
static const int CLASS_LINUX_MIPS = 30; // LINUX/MIPS
static const int CLASS_DARWIN_X64 = 31; // Darwin/x64
static const int CLASS_SOLARIS_AMD64 = 32; // Solaris/amd64
static const int CLASS_LINUX_ARM = 33; // LINUX/ARM
2008-05-22 17:02:59 +02:00
static const int CLASS_LINUX_IA64 = 34; // LINUX/IA64
2009-08-22 14:16:47 +02:00
static const int CLASS_DARWIN_PPC64 = 35; // Darwin/PowerPC64
static const int CLASS_LINUX_S390X = 36; // LINUX/s390x
static const int CLASS_LINUX_S390 = 37; // LINUX/s390
static const int CLASS_LINUX_SH = 38; // LINUX/SH (little-endian)
static const int CLASS_LINUX_SHEB = 39; // LINUX/SH (big-endian)
2008-04-15 16:14:57 +02:00
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_SHEB;
// 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
//
2008-12-05 02:20:14 +01:00
// 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.
2008-12-05 02:20:14 +01:00
enum ArchitectureType {
2009-08-22 14:16:47 +02:00
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
};
2008-12-05 02:20:14 +01:00
// 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.
2008-01-16 10:41:31 +01:00
static const 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
};
2008-01-16 10:41:31 +01:00
static const 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
2006-07-27 10:11:51 +02:00
archLittleEndian, // CLASS_FREEBSD_AMD64
archLittleEndian, // CLASS_WINDOWS_AMD64
2007-03-09 11:32:58 +01:00
archBigEndian, // CLASS_LINUX_PPC
2007-04-01 12:04:28 +02:00
archLittleEndian, // CLASS_DARWIN_I386
archLittleEndian, // CLASS_LINUX_MIPSEL
2008-04-15 16:14:57 +02:00
archBigEndian, // CLASS_LINUX_MIPS
archLittleEndian, // CLASS_DARWIN_X64
archLittleEndian, // CLASS_SOLARIS_AMD64
2008-05-22 17:02:59 +02:00
archLittleEndian, // CLASS_LINUX_ARM
2008-06-09 14:02:59 +02:00
archLittleEndian, // CLASS_LINUX_IA64
archBigEndian, // CLASS_DARWIN_PPC64
archBigEndian, // CLASS_LINUX_S390X
archBigEndian, // CLASS_LINUX_S390
archLittleEndian, // CLASS_LINUX_SH
archBigEndian // CLASS_LINUX_SHEB
};
2001-05-23 15:26:42 +02:00
2009-04-10 12:29:13 +02:00
#ifdef __sun
#ifdef __i386
const SSHORT CLASS = CLASS_SOLARIS_I386;
2009-04-10 12:29:13 +02:00
#elif defined (__sparc)
const SSHORT CLASS = CLASS_SOLARIS_SPARC;
2009-04-10 12:29:13 +02:00
#elif defined (__amd64)
2008-04-25 11:42:19 +02:00
const SSHORT CLASS = CLASS_SOLARIS_AMD64;
2009-04-10 12:29:13 +02:00
#else
#error no support for this hardware on SUN
2008-04-25 11:42:19 +02:00
#endif
2009-04-10 12:29:13 +02:00
#endif // __sun
2007-11-12 16:18:49 +01:00
#ifdef HPUX
const SSHORT CLASS = CLASS_HPUX_PA;
2001-05-23 15:26:42 +02:00
#endif
#ifdef AIX_PPC
const SSHORT CLASS = CLASS_AIX_PPC;
2001-05-23 15:26:42 +02:00
#endif
#ifdef WIN_NT
2006-07-27 10:11:51 +02:00
#if defined(I386)
const SSHORT CLASS = CLASS_WINDOWS_I386;
2006-07-27 10:11:51 +02:00
#elif defined(AMD64)
const SSHORT CLASS = CLASS_WINDOWS_AMD64;
#else
#error no support on other hardware for Windows
#endif
2006-07-28 03:44:36 +02:00
#endif // WIN_NT
2001-05-23 15:26:42 +02:00
#ifdef LINUX
2006-07-27 10:11:51 +02:00
#if defined(i386) || defined(i586)
const SSHORT CLASS = CLASS_LINUX_I386;
2006-07-27 10:11:51 +02:00
#elif defined(sparc)
const SSHORT CLASS = CLASS_LINUX_SPARC;
2006-07-27 10:11:51 +02:00
#elif defined(AMD64)
const SSHORT CLASS = CLASS_LINUX_AMD64;
2008-04-15 16:14:57 +02:00
#elif defined(ARM)
const SSHORT CLASS = CLASS_LINUX_ARM;
2006-10-25 10:59:36 +02:00
#elif defined(PPC)
const SSHORT CLASS = CLASS_LINUX_PPC;
2007-04-01 12:04:28 +02:00
#elif defined(MIPSEL)
const SSHORT CLASS = CLASS_LINUX_MIPSEL;
#elif defined(MIPS)
const SSHORT CLASS = CLASS_LINUX_MIPS;
2008-05-22 17:02:59 +02:00
#elif defined(IA64)
const SSHORT CLASS = CLASS_LINUX_IA64;
#elif defined(__s390__)
# if defined(__s390x__)
const SSHORT CLASS = CLASS_LINUX_S390X;
# else
const SSHORT CLASS = CLASS_LINUX_S390;
# endif // defined(__s390x__)
#elif defined(SH)
const SSHORT CLASS = CLASS_LINUX_SH;
#elif defined(SHEB)
const SSHORT CLASS = CLASS_LINUX_SHEB;
2006-07-27 10:11:51 +02:00
#else
#error no support on other hardware for Linux
2001-05-23 15:26:42 +02:00
#endif
2006-07-28 03:44:36 +02:00
#endif // LINUX
2001-05-23 15:26:42 +02:00
#ifdef FREEBSD
#if defined(i386)
const SSHORT CLASS = CLASS_FREEBSD_I386;
#elif defined(AMD64)
const SSHORT CLASS = CLASS_FREEBSD_AMD64;
#else
2006-03-16 03:26:52 +01:00
#error no support on other hardware for FreeBSD
#endif
2001-05-23 15:26:42 +02:00
#endif
#ifdef NETBSD
const SSHORT CLASS = CLASS_NETBSD_I386;
2001-05-23 15:26:42 +02:00
#endif
2001-07-12 07:46:06 +02:00
#ifdef DARWIN
#if defined(i386)
2007-03-09 11:32:58 +01:00
const SSHORT CLASS = CLASS_DARWIN_I386;
#elif defined(DARWIN64)
2008-01-18 15:12:26 +01:00
const SSHORT CLASS = CLASS_DARWIN_X64;
2008-06-09 14:02:59 +02:00
#elif defined(powerpc)
const SSHORT CLASS = CLASS_DARWIN_PPC;
2008-06-09 14:02:59 +02:00
#elif defined(DARWINPPC64)
const SSHORT CLASS = CLASS_DARWIN_PPC64;
2001-07-12 07:46:06 +02:00
#endif
#endif // DARWIN
static const char* const SCRATCH = "fb_table_";
2001-05-23 15:26:42 +02:00
static const int MIN_EXTEND_BYTES = 128 * 1024; // 128KB
2003-12-31 06:36:12 +01:00
// 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(thread_db* tdbb,
SLONG page_num, USHORT type,
2009-01-07 10:30:57 +01:00
USHORT len, const UCHAR* entry, ClumpOper mode) // bool must_write
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* 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.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
pag* page;
header_page* header = 0;
2003-12-31 06:36:12 +01:00
log_info_page* logp = 0;
USHORT* end_addr;
2006-05-22 00:07:35 +02:00
WIN window(DB_PAGE_SPACE, page_num);
2009-08-23 09:31:48 +02:00
if (page_num == HEADER_PAGE)
{
2001-05-23 15:26:42 +02:00
page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
header = (header_page*) page;
2001-05-23 15:26:42 +02:00
end_addr = &header->hdr_end;
}
2009-08-23 09:31:48 +02:00
else
{
2001-05-23 15:26:42 +02:00
page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
logp = (log_info_page*) page;
2001-05-23 15:26:42 +02:00
end_addr = &logp->log_end;
}
2003-12-31 06:36:12 +01:00
UCHAR* entry_p;
const UCHAR* clump_end;
2009-01-07 10:30:57 +01:00
while (mode != CLUMP_ADD)
{
2008-12-05 02:20:14 +01:00
const bool found =
find_type(tdbb, page_num, &window, &page, LCK_write, type, &entry_p, &clump_end);
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// If we did'nt find it and it is REPLACE_ONLY, return
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (!found && mode == CLUMP_REPLACE_ONLY)
{
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2003-12-31 06:36:12 +01:00
return; //false;
2001-05-23 15:26:42 +02:00
}
2009-08-22 14:16:47 +02:00
// If not found, just go and add the entry
2001-05-23 15:26:42 +02:00
if (!found)
break;
2009-08-22 14:16:47 +02:00
// if same size, overwrite it
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
if (entry_p[1] == len)
{
2001-05-23 15:26:42 +02:00
entry_p += 2;
if (len)
{
2009-01-07 10:30:57 +01:00
//if (must_write)
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, &window);
2009-01-07 10:30:57 +01:00
//else
// CCH_MARK(tdbb, &window);
2008-12-05 02:20:14 +01:00
memcpy(entry_p, entry, len);
2001-05-23 15:26:42 +02:00
}
CCH_RELEASE(tdbb, &window);
2003-12-31 06:36:12 +01:00
return; // true;
2001-05-23 15:26:42 +02:00
}
2009-08-22 14:16:47 +02:00
// delete the entry
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// 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
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, &window);
*end_addr -= (2 + entry_p[1]);
const UCHAR* r = entry_p + 2 + entry_p[1];
2003-12-31 06:36:12 +01:00
USHORT l = clump_end - r + 1;
if (l)
memmove(entry_p, r, l);
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2009-08-22 14:16:47 +02:00
// refetch the page
2001-05-23 15:26:42 +02:00
window.win_page = page_num;
2009-08-23 09:31:48 +02:00
if (page_num == HEADER_PAGE)
{
2001-05-23 15:26:42 +02:00
page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
header = (header_page*) page;
2001-05-23 15:26:42 +02:00
end_addr = &header->hdr_end;
}
2009-08-23 09:31:48 +02:00
else
{
2001-05-23 15:26:42 +02:00
page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
logp = (log_info_page*) page;
2001-05-23 15:26:42 +02:00
end_addr = &logp->log_end;
}
break;
}
// Add the entry
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
find_clump_space(tdbb, page_num, &window, &page, type, len, entry);
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2003-12-31 06:36:12 +01:00
return; // true;
2001-05-23 15:26:42 +02:00
}
USHORT PAG_add_file(thread_db* tdbb, const TEXT* file_name, SLONG start)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ a d d _ f i l e
*
**************************************
*
* Functional description
2009-01-07 10:30:57 +01:00
* Add a file to the current database. Return the sequence number for the new file.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// Find current last file
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
jrd_file* file = pageSpace->file;
2003-12-31 06:36:12 +01:00
while (file->fil_next) {
file = file->fil_next;
}
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// Verify database file path against DatabaseAccess entry of firebird.conf
2009-08-23 09:31:48 +02:00
if (!JRD_verify_database_access(file_name))
{
2008-08-31 03:10:41 +02:00
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") <<
Arg::Str(file_name));
2003-10-01 17:27:00 +02:00
}
2009-08-22 14:16:47 +02:00
// Create the file. If the sequence number comes back zero, it didn't work, so punt
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
const USHORT sequence = PIO_add_file(dbb, pageSpace->file, file_name, start);
2003-12-31 06:36:12 +01:00
if (!sequence)
2001-05-23 15:26:42 +02:00
return 0;
2009-08-22 14:16:47 +02:00
// Create header page for new file
2001-05-23 15:26:42 +02:00
jrd_file* next = file->fil_next;
2001-05-23 15:26:42 +02:00
if (dbb->dbb_flags & (DBB_force_write | DBB_no_fs_cache))
2007-07-26 03:23:18 +02:00
{
2008-12-22 10:00:05 +01:00
PIO_force_write(next, dbb->dbb_flags & DBB_force_write, dbb->dbb_flags & DBB_no_fs_cache);
2007-07-26 03:23:18 +02:00
}
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
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;
2001-05-23 15:26:42 +02:00
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
2009-08-22 14:16:47 +02:00
// The following lines (taken from PAG_format_header) are needed to identify
// this file in raw_devices_validate_database as a valid database attachment.
2008-03-30 22:43:36 +02:00
*(ISC_TIMESTAMP*) header->hdr_creation_date = Firebird::TimeStamp::getCurrentTimeStamp().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);
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
next->fil_fudge = 1;
2009-08-22 14:16:47 +02:00
// Update the previous header page to point to new file
2001-05-23 15:26:42 +02:00
file->fil_fudge = 0;
window.win_page = file->fil_min_page;
header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
if (!file->fil_min_page)
CCH_MARK_MUST_WRITE(tdbb, &window);
else
CCH_MARK(tdbb, &window);
--start;
2009-08-23 09:31:48 +02:00
if (file->fil_min_page)
{
PAG_add_header_entry(tdbb, header, HDR_file, strlen(file_name),
2003-12-31 06:36:12 +01:00
reinterpret_cast<const UCHAR*>(file_name));
2008-12-22 10:00:05 +01:00
PAG_add_header_entry(tdbb, header, HDR_last_page, sizeof(SLONG), (UCHAR*) &start);
2001-05-23 15:26:42 +02:00
}
2009-08-23 09:31:48 +02:00
else
{
PAG_add_clump(tdbb, HEADER_PAGE, HDR_file, strlen(file_name),
2009-01-07 10:30:57 +01:00
reinterpret_cast<const UCHAR*>(file_name), CLUMP_REPLACE); //, true;
PAG_add_clump(tdbb, HEADER_PAGE, HDR_last_page, sizeof(SLONG),
2009-01-07 10:30:57 +01:00
(UCHAR*) &start, CLUMP_REPLACE); //, true
2001-05-23 15:26:42 +02:00
}
header->hdr_header.pag_checksum = CCH_checksum(window.win_bdb);
PIO_write(pageSpace->file, window.win_bdb, window.win_buffer, tdbb->tdbb_status_vector);
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
if (file->fil_min_page)
file->fil_fudge = 1;
return sequence;
}
2009-01-07 10:30:57 +01:00
bool PAG_add_header_entry(thread_db* tdbb, header_page* header,
USHORT type, USHORT len, const UCHAR* entry)
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* 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
2001-05-23 15:26:42 +02:00
* RETURNS
2009-01-07 10:30:57 +01:00
* true - modified page
* false - nothing done
2008-01-16 10:41:31 +01:00
* CVC: Nobody checks the result of this function!
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2008-12-22 10:00:05 +01:00
UCHAR* p = header->hdr_data;
while (*p != HDR_end && *p != type)
p += 2 + p[1];
2001-05-23 15:26:42 +02:00
if (*p != HDR_end)
2009-01-07 10:30:57 +01:00
return false;
2001-05-23 15:26:42 +02:00
2008-01-16 10:41:31 +01:00
// We are at HDR_end, add the entry
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
const int free_space = dbb->dbb_page_size - header->hdr_end;
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
if (free_space > (2 + len))
{
2003-11-04 00:59:24 +01:00
fb_assert(type <= MAX_UCHAR);
fb_assert(len <= MAX_UCHAR);
*p++ = static_cast<UCHAR>(type);
*p++ = static_cast<UCHAR>(len);
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (len)
{
2008-01-16 10:41:31 +01:00
if (entry) {
memcpy(p, entry, len);
2008-01-16 10:41:31 +01:00
}
else {
memset(p, 0, len);
2008-01-16 10:41:31 +01:00
}
p += len;
2001-05-23 15:26:42 +02:00
}
*p = HDR_end;
2009-08-23 13:04:36 +02:00
header->hdr_end = p - (UCHAR*) header;
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
return true;
2001-05-23 15:26:42 +02:00
}
BUGCHECK(251);
2009-08-22 14:16:47 +02:00
return false; // Added to remove compiler warning
2001-05-23 15:26:42 +02:00
}
2006-05-23 05:37:00 +02:00
2006-05-22 00:07:35 +02:00
void PAG_attach_temp_pages(thread_db* tdbb, USHORT pageSpaceID)
{
2006-05-23 20:32:50 +02:00
/***********************************************
*
* 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
*
**************************************/
2006-05-22 00:07:35 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2006-05-22 00:07:35 +02:00
CHECK_DBB(dbb);
PageSpace* pageSpaceTemp = dbb->dbb_page_manager.addPageSpace(pageSpaceID);
if (!pageSpaceTemp->file)
{
Firebird::PathName file_name = TempFile::create(SCRATCH);
2007-03-09 08:59:05 +01:00
pageSpaceTemp->file = PIO_create(dbb, file_name, true, true, false);
2006-05-22 00:07:35 +02:00
PAG_format_pip(tdbb, *pageSpaceTemp);
}
}
2001-05-23 15:26:42 +02:00
2006-05-23 05:37:00 +02:00
2009-01-07 10:30:57 +01:00
bool PAG_replace_entry_first(thread_db* tdbb, header_page* header,
2009-08-23 09:31:48 +02:00
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
2009-01-07 10:30:57 +01:00
* true - modified page
* false - nothing done
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
CHECK_DBB(dbb);
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
UCHAR* p = header->hdr_data;
2008-12-22 10:00:05 +01:00
while (*p != HDR_end && *p != type) {
p += 2 + p[1];
}
// Remove item if found it somewhere
2009-08-23 09:31:48 +02:00
if (*p != HDR_end)
{
UCHAR l = p[1] + 2;
2008-12-22 10:00:05 +01:00
memmove(p, p + l, header->hdr_end - (p - (UCHAR*) header) - l + 1); // to preserve HDR_end
header->hdr_end -= l;
}
2007-10-10 04:06:20 +02:00
if (!entry) {
2009-01-07 10:30:57 +01:00
return false; // We were asked just to remove item. We finished.
}
2007-10-10 04:06:20 +02:00
// Check if we got enough space
if (dbb->dbb_page_size - header->hdr_end <= len + 2) {
BUGCHECK(251);
}
2007-10-10 04:06:20 +02:00
// 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;
2004-11-30 07:18:39 +01:00
memcpy(header->hdr_data + 2, entry, len);
header->hdr_end += len + 2;
2007-10-10 04:06:20 +02:00
2009-01-07 10:30:57 +01:00
return true;
}
PAG PAG_allocate(thread_db* tdbb, WIN* window)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-05-22 00:07:35 +02:00
PageManager& pageMgr = dbb->dbb_page_manager;
2008-12-22 10:00:05 +01:00
PageSpace* pageSpace = pageMgr.findPageSpace(window->win_page.getPageSpaceID());
2006-05-22 00:07:35 +02:00
fb_assert(pageSpace);
// Not sure if this can be moved inside the loop. Maybe some data members
// should persist across iterations?
2006-05-22 00:07:35 +02:00
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;
2008-12-05 02:20:14 +01:00
pag* new_page = 0; // NULL before the search for a new page.
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// Starting from ODS 11.1 we store in pip_header.reserved number of pages
// allocated from this pointer page. There is intention to create dedicated
// field at page_inv_page for this purpose in ODS 12.
2008-12-22 10:00:05 +01:00
const bool isODS11_x = (dbb->dbb_ods_version == ODS_VERSION11 && dbb->dbb_minor_version >= 1);
2009-07-25 20:43:25 +02:00
// Find an allocation page with something on it
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
SLONG relative_bit = -1;
SLONG sequence;
SLONG pipMin;
2009-01-07 10:30:57 +01:00
for (sequence = pageSpace->pipHighWater; true; sequence++)
{
2001-05-23 15:26:42 +02:00
pip_window.win_page = (sequence == 0) ?
2006-05-22 00:07:35 +02:00
pageSpace->ppFirst : sequence * dbb->dbb_page_manager.pagesPerPIP - 1;
2008-12-22 10:00:05 +01:00
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;
2008-12-22 10:00:05 +01:00
for (bytes = &pip_page->pip_bits[pip_page->pip_min >> 3]; bytes < end; bytes++)
{
2009-08-23 09:31:48 +02:00
if (*bytes != 0)
{
2009-07-25 20:43:25 +02:00
// 'byte' is not zero, so it describes at least one free page.
bit = 1;
2009-07-25 20:43:25 +02:00
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);
2008-12-05 02:20:14 +01:00
const SLONG pageNum = relative_bit + sequence * pageMgr.pagesPerPIP;
window->win_page = pageNum;
2009-07-25 20:43:25 +02:00
new_page = CCH_fake(tdbb, window, 0); // don't wait on latch
2001-05-23 15:26:42 +02:00
if (new_page)
{
2009-07-25 20:43:25 +02:00
if (!isODS11_x)
break;
BackupManager::StateReadGuard stateGuard(tdbb);
const bool nbak_stalled =
dbb->dbb_backup_manager->getState() == nbak_state_stalled;
USHORT next_init_pages = 1;
// ensure there are space on disk for faked page
if (relative_bit + 1 > pip_page->pip_header.reserved)
{
2009-07-25 20:43:25 +02:00
fb_assert(relative_bit == pip_page->pip_header.reserved);
2009-07-25 20:43:25 +02:00
USHORT init_pages = 0;
if (!nbak_stalled)
{
2009-07-25 20:43:25 +02:00
init_pages = 1;
if (!(dbb->dbb_flags & DBB_no_reserve))
{
2009-07-25 20:43:25 +02:00
const int minExtendPages =
MIN_EXTEND_BYTES / dbb->dbb_page_size;
2009-04-04 18:39:31 +02:00
2009-07-25 20:43:25 +02:00
init_pages = sequence ?
64 : MIN(pip_page->pip_header.reserved / 16, 64);
2009-04-04 18:39:31 +02:00
2009-07-25 20:43:25 +02:00
// don't touch pages belongs to the next PIP
init_pages = MIN(init_pages,
pageMgr.pagesPerPIP - pip_page->pip_header.reserved);
2009-04-04 18:39:31 +02:00
2009-07-25 20:43:25 +02:00
if (init_pages < minExtendPages)
init_pages = 1;
2009-04-04 18:39:31 +02:00
2009-07-25 20:43:25 +02:00
next_init_pages = init_pages;
}
2009-07-25 20:43:25 +02:00
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 occurred. 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;
}
2009-07-25 20:43:25 +02:00
catch (Firebird::status_exception)
{
2009-07-25 20:43:25 +02:00
// 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;
}
2009-07-25 20:43:25 +02:00
new_page = CCH_fake(tdbb, window, 1);
}
2007-09-19 03:46:45 +02:00
2009-07-25 20:43:25 +02:00
fb_assert(new_page);
}
2009-07-25 20:43:25 +02:00
if (!(dbb->dbb_flags & DBB_no_reserve) && !nbak_stalled)
{
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);
}
2009-07-25 20:43:25 +02:00
break; // Found a page and successfully fake-ed it
}
2001-05-23 15:26:42 +02:00
}
}
}
if (new_page)
2009-07-25 20:43:25 +02:00
break; // Found a page and successfully fake-ed it
2001-05-23 15:26:42 +02:00
}
2009-07-25 20:43:25 +02:00
2001-05-23 15:26:42 +02:00
if (new_page)
2009-07-25 20:43:25 +02:00
break; // Found a page and successfully fake-ed it
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &pip_window);
}
2006-05-22 00:07:35 +02:00
pageSpace->pipHighWater = sequence;
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (relative_bit != pageMgr.pagesPerPIP - 1)
{
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &pip_window);
CCH_precedence(tdbb, window, pip_window.win_page);
#ifdef VIO_DEBUG
if (debug_flag > DEBUG_WRITES_INFO)
2004-04-29 00:43:34 +02:00
printf("\tPAG_allocate: allocated page %"SLONGFORMAT"\n",
2008-12-03 16:17:58 +01:00
window->win_page.getPageNum());
2001-05-23 15:26:42 +02:00
#endif
return new_page;
}
2009-07-25 20:43:25 +02:00
// We've allocated the last page on the space management page. Rather
// than returning it, format it as a page inventory page, and recurse.
2001-05-23 15:26:42 +02:00
page_inv_page* new_pip_page = (page_inv_page*) new_page;
new_pip_page->pip_header.pag_type = pag_pages;
2009-08-23 13:04:36 +02:00
const UCHAR* end = (UCHAR*) new_pip_page + dbb->dbb_page_size;
2007-05-16 10:23:41 +02:00
memset(new_pip_page->pip_bits, 0xff, end - new_pip_page->pip_bits);
2001-05-23 15:26:42 +02:00
CCH_must_write(window);
CCH_RELEASE(tdbb, window);
CCH_must_write(&pip_window);
CCH_RELEASE(tdbb, &pip_window);
return PAG_allocate(tdbb, window);
2001-05-23 15:26:42 +02:00
}
2006-05-22 00:07:35 +02:00
SLONG PAG_attachment_id(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/******************************************
*
* 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.
*
******************************************/
2006-05-22 00:07:35 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
Jrd::Attachment* attachment = tdbb->getAttachment();
2006-05-22 00:07:35 +02:00
WIN window(DB_PAGE_SPACE, -1);
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// If we've been here before just return the id
2001-05-23 15:26:42 +02:00
if (attachment->att_id_lock)
return attachment->att_attachment_id;
2009-08-22 14:16:47 +02:00
// Get new attachment id
2001-05-23 15:26:42 +02:00
if (dbb->dbb_flags & DBB_read_only) {
attachment->att_attachment_id = dbb->dbb_attachment_id + dbb->genSharedUniqueNumber(tdbb);
2001-05-23 15:26:42 +02:00
}
2009-08-23 09:31:48 +02:00
else
{
2006-05-22 00:07:35 +02:00
window.win_page = HEADER_PAGE_NUMBER;
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
CCH_MARK(tdbb, &window);
attachment->att_attachment_id = ++header->hdr_attachment_id;
CCH_RELEASE(tdbb, &window);
}
2009-08-22 14:16:47 +02:00
// Take out lock on attachment id
2001-05-23 15:26:42 +02:00
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock();
2003-12-31 06:36:12 +01:00
attachment->att_id_lock = lock;
2001-05-23 15:26:42 +02:00
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;
lock->lck_ast = blocking_ast_attachment;
lock->lck_object = attachment;
LCK_lock(tdbb, lock, LCK_EX, LCK_WAIT);
2001-05-23 15:26:42 +02:00
return attachment->att_attachment_id;
}
2009-01-07 10:30:57 +01:00
bool PAG_delete_clump_entry(thread_db* tdbb, SLONG page_num, USHORT type)
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* 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.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(DB_PAGE_SPACE, page_num);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
pag* page;
2001-05-23 15:26:42 +02:00
if (page_num == HEADER_PAGE)
page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
else
page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
2003-12-31 06:36:12 +01:00
UCHAR* entry_p;
const UCHAR* clump_end;
if (!find_type(tdbb, page_num, &window, &page, LCK_write, type, &entry_p, &clump_end))
2003-12-31 06:36:12 +01:00
{
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2009-01-07 10:30:57 +01:00
return false;
2001-05-23 15:26:42 +02:00
}
CCH_MARK(tdbb, &window);
2003-12-31 06:36:12 +01:00
header_page* header = 0;
2003-12-31 06:36:12 +01:00
log_info_page* logp = 0;
USHORT* end_addr;
2009-08-23 09:31:48 +02:00
if (page_num == HEADER_PAGE)
{
header = (header_page*) page;
2001-05-23 15:26:42 +02:00
end_addr = &header->hdr_end;
}
2009-08-23 09:31:48 +02:00
else
{
logp = (log_info_page*) page;
2001-05-23 15:26:42 +02:00
end_addr = &logp->log_end;
}
*end_addr -= (2 + entry_p[1]);
2003-12-31 06:36:12 +01:00
const UCHAR* r = entry_p + 2 + entry_p[1];
USHORT l = clump_end - r + 1;
if (l)
memmove(entry_p, r, l);
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2009-01-07 10:30:57 +01:00
return true;
2001-05-23 15:26:42 +02:00
}
void PAG_format_header(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2009-08-22 14:16:47 +02:00
// Initialize header page
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
header->hdr_header.pag_scn = 0;
2008-03-30 22:43:36 +02:00
*(ISC_TIMESTAMP*) header->hdr_creation_date = Firebird::TimeStamp::getCurrentTimeStamp().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;
2001-05-23 15:26:42 +02:00
header->hdr_page_size = dbb->dbb_page_size;
header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG;
2001-05-23 15:26:42 +02:00
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;
header->hdr_flags |= hdr_force_write;
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) {
2001-05-23 15:26:42 +02:00
header->hdr_flags |= hdr_SQL_dialect_3;
}
2001-05-23 15:26:42 +02:00
dbb->dbb_ods_version = header->hdr_ods_version & ~ODS_FIREBIRD_FLAG;
2001-05-23 15:26:42 +02:00
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(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* P A G _ f o r m a t _ l o g
*
***********************************************
*
* Functional description
* Initialize log page.
* Set all parameters to 0
*
**************************************/
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(LOG_PAGE_NUMBER);
2003-12-31 06:36:12 +01:00
log_info_page* logp = (log_info_page*) CCH_fake(tdbb, &window, 1);
logp->log_header.pag_type = pag_log;
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
}
2006-05-22 00:07:35 +02:00
void PAG_format_pip(thread_db* tdbb, PageSpace& pageSpace)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
2006-05-22 00:07:35 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2009-08-22 14:16:47 +02:00
// Initialize Page Inventory Page
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(pageSpace.pageSpaceID, 1);
pageSpace.ppFirst = 1;
page_inv_page* pages = (page_inv_page*) CCH_fake(tdbb, &window, 1);
2001-05-23 15:26:42 +02:00
pages->pip_header.pag_type = pag_pages;
2001-05-23 15:26:42 +02:00
pages->pip_min = 4;
pages->pip_header.reserved = pages->pip_min - 1;
2003-12-31 06:36:12 +01:00
UCHAR* p = pages->pip_bits;
int i = dbb->dbb_page_size - OFFSETA(page_inv_page*, pip_bits);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
while (i--) {
2001-05-23 15:26:42 +02:00
*p++ = 0xff;
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
pages->pip_bits[0] &= ~(1 | 2 | 4);
CCH_RELEASE(tdbb, &window);
}
bool PAG_get_clump(thread_db* tdbb, SLONG page_num, USHORT type, USHORT* inout_len, UCHAR* entry)
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* P A G _ g e t _ c l u m p
*
***********************************************
*
* Functional description
* Find 'type' clump in page_num
2003-12-31 06:36:12 +01:00
* true - Found it
* false - Not present
2001-05-23 15:26:42 +02:00
* RETURNS
* value of clump in entry
* length in inout_len <-> input and output value to avoid B.O.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(DB_PAGE_SPACE, page_num);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
pag* page;
2001-05-23 15:26:42 +02:00
if (page_num == HEADER_PAGE)
page = CCH_FETCH(tdbb, &window, LCK_read, pag_header);
else
page = CCH_FETCH(tdbb, &window, LCK_read, pag_log);
2003-12-31 06:36:12 +01:00
UCHAR* entry_p;
const UCHAR* dummy;
2009-08-23 09:31:48 +02:00
if (!find_type(tdbb, page_num, &window, &page, LCK_read, type, &entry_p, &dummy))
{
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
*inout_len = 0;
2003-12-31 06:36:12 +01:00
return false;
2001-05-23 15:26:42 +02:00
}
USHORT old_len = *inout_len;
*inout_len = entry_p[1];
2001-05-23 15:26:42 +02:00
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);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2003-12-31 06:36:12 +01:00
return true;
2001-05-23 15:26:42 +02:00
}
void PAG_header(thread_db* tdbb, bool info)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ h e a d e r
*
**************************************
*
* Functional description
* Checkout database header page.
2007-03-09 09:55:07 +01:00
* Done through the page cache.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
Jrd::Attachment* attachment = tdbb->getAttachment();
2007-03-09 09:55:07 +01:00
fb_assert(attachment);
2001-05-23 15:26:42 +02:00
2007-03-09 09:55:07 +01:00
WIN window(HEADER_PAGE_NUMBER);
2008-12-22 10:00:05 +01:00
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (header->hdr_next_transaction)
{
2001-05-23 15:26:42 +02:00
if (header->hdr_oldest_active > header->hdr_next_transaction)
2009-08-22 14:16:47 +02:00
BUGCHECK(266); // next transaction older than oldest active
2001-05-23 15:26:42 +02:00
if (header->hdr_oldest_transaction > header->hdr_next_transaction)
2009-08-22 14:16:47 +02:00
BUGCHECK(267); // next transaction older than oldest transaction
2001-05-23 15:26:42 +02:00
}
if (header->hdr_flags & hdr_SQL_dialect_3)
dbb->dbb_flags |= DBB_DB_SQL_dialect_3;
2003-12-31 06:36:12 +01:00
jrd_rel* relation = MET_relation(tdbb, 0);
2006-05-22 00:07:35 +02:00
RelationPages* relPages = relation->getBasePages();
2009-08-23 09:31:48 +02:00
if (!relPages->rel_pages)
{
// 21-Dec-2003 Nickolay Samofatov
2008-12-05 02:20:14 +01:00
// No need to re-set first page for RDB$PAGES relation since
2007-03-09 09:55:07 +01:00
// 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.
2003-12-31 06:36:12 +01:00
vcl* vector = vcl::newVector(*dbb->dbb_permanent, 1);
2006-05-22 00:07:35 +02:00
relPages->rel_pages = vector;
(*vector)[0] = header->hdr_PAGES;
}
2001-05-23 15:26:42 +02:00
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;
}
2001-05-23 15:26:42 +02:00
dbb->dbb_attachment_id = header->hdr_attachment_id;
dbb->dbb_creation_date = *(ISC_TIMESTAMP*) header->hdr_creation_date;
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (header->hdr_flags & hdr_read_only)
{
2009-08-22 14:16:47 +02:00
// If Header Page flag says the database is ReadOnly, gladly accept it.
2001-05-23 15:26:42 +02:00
dbb->dbb_flags &= ~DBB_being_opened_read_only;
dbb->dbb_flags |= DBB_read_only;
}
2009-08-22 14:16:47 +02:00
// If hdr_read_only is not set...
2008-12-18 12:29:10 +01:00
if (!(header->hdr_flags & hdr_read_only) && (dbb->dbb_flags & DBB_being_opened_read_only))
2003-12-31 06:36:12 +01:00
{
2009-08-22 14:16:47 +02:00
// 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)
2008-08-31 03:10:41 +02:00
ERR_post(Arg::Gds(isc_no_priv) << Arg::Str("read-write") <<
Arg::Str("database") <<
Arg::Str(attachment->att_filename));
2001-05-23 15:26:42 +02:00
}
2009-08-30 12:08:19 +02:00
const bool useFSCache = dbb->dbb_bcb->bcb_count < Config::getFileSystemCacheThreshold();
2007-07-26 03:23:18 +02:00
if ((header->hdr_flags & hdr_force_write) || !useFSCache)
{
2008-12-05 02:20:14 +01:00
dbb->dbb_flags |=
(header->hdr_flags & hdr_force_write ? DBB_force_write : 0) |
2007-07-26 03:23:18 +02:00
(useFSCache ? 0 : DBB_no_fs_cache);
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
2009-08-23 09:31:48 +02:00
for (jrd_file* file = pageSpace->file; file; file = file->fil_next)
{
2008-12-05 02:20:14 +01:00
PIO_force_write(file,
(dbb->dbb_flags & DBB_force_write) && !(header->hdr_flags & hdr_read_only),
dbb->dbb_flags & DBB_no_fs_cache);
}
2001-05-23 15:26:42 +02:00
}
if (header->hdr_flags & hdr_no_reserve)
dbb->dbb_flags |= DBB_no_reserve;
2004-03-07 08:58:55 +01:00
const USHORT sd_flags = header->hdr_flags & hdr_shutdown_mask;
2009-08-23 09:31:48 +02:00
if (sd_flags)
{
2001-05-23 15:26:42 +02:00
dbb->dbb_ast_flags |= DBB_shutdown;
2004-03-07 08:58:55 +01:00
if (sd_flags == hdr_shutdown_full)
dbb->dbb_ast_flags |= DBB_shutdown_full;
2004-03-07 08:58:55 +01:00
else if (sd_flags == hdr_shutdown_single)
dbb->dbb_ast_flags |= DBB_shutdown_single;
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
} // try
2009-08-23 09:31:48 +02:00
catch (const Firebird::Exception&)
{
2007-03-09 09:55:07 +01:00
CCH_RELEASE(tdbb, &window);
throw;
2001-12-24 03:51:06 +01:00
}
2007-03-09 09:55:07 +01:00
CCH_RELEASE(tdbb, &window);
2001-05-23 15:26:42 +02:00
}
void PAG_header_init(thread_db* tdbb)
2007-03-09 09:55:07 +01:00
{
/**************************************
*
* 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.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
2007-03-09 09:55:07 +01:00
Jrd::Attachment* const attachment = tdbb->getAttachment();
2007-03-09 09:55:07 +01:00
fb_assert(attachment);
2007-03-11 03:45:50 +01:00
// 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.
2007-03-09 09:55:07 +01:00
2007-03-12 01:42:29 +01:00
SCHAR temp_buffer[2 * MIN_PAGE_SIZE];
2009-08-23 13:04:36 +02:00
SCHAR* temp_page = (SCHAR*) FB_ALIGN((IPTR) temp_buffer, MIN_PAGE_SIZE);
2007-03-09 09:55:07 +01:00
PIO_header(dbb, temp_page, MIN_PAGE_SIZE);
2009-06-08 14:15:19 +02:00
const header_page* header = (header_page*) temp_page;
2007-03-09 09:55:07 +01:00
if (header->hdr_header.pag_type != pag_header || header->hdr_sequence) {
ERR_post(Arg::Gds(isc_bad_db_format) << Arg::Str(attachment->att_filename));
2007-03-09 09:55:07 +01:00
}
const USHORT ods_version = header->hdr_ods_version & ~ODS_FIREBIRD_FLAG;
if (!Ods::isSupported(header->hdr_ods_version, header->hdr_ods_minor))
{
2008-08-31 03:10:41 +02:00
ERR_post(Arg::Gds(isc_wrong_ods) << Arg::Str(attachment->att_filename) <<
Arg::Num(ods_version) <<
Arg::Num(header->hdr_ods_minor) <<
Arg::Num(ODS_VERSION) <<
Arg::Num(ODS_CURRENT));
2007-03-09 09:55:07 +01:00
}
2007-03-11 03:45:50 +01:00
// 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
2007-03-09 09:55:07 +01:00
2009-01-03 10:14:29 +01:00
if (header->hdr_implementation != CLASS)
2007-03-09 09:55:07 +01:00
{
2009-01-03 10:14:29 +01:00
const int classmax = ods_version < ODS_VERSION11 ? CLASS_MAX10 : CLASS_MAX;
const ArchitectureType* matrix = ods_version < ODS_VERSION11 ? archMatrix10 : archMatrix;
const int hdrImpl = header->hdr_implementation;
if (hdrImpl < 0 || hdrImpl > classmax ||
2009-01-07 10:30:57 +01:00
matrix[hdrImpl] == archUnknown || matrix[hdrImpl] != matrix[CLASS])
2009-01-03 10:14:29 +01:00
{
ERR_post(Arg::Gds(isc_bad_db_format) << Arg::Str(attachment->att_filename));
}
2007-03-09 09:55:07 +01:00
}
2008-12-22 10:00:05 +01:00
if (header->hdr_page_size < MIN_PAGE_SIZE || header->hdr_page_size > MAX_PAGE_SIZE)
2007-03-09 09:55:07 +01:00
{
ERR_post(Arg::Gds(isc_bad_db_format) << Arg::Str(attachment->att_filename));
2007-03-09 09:55:07 +01:00
}
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;
}
2008-12-05 02:20:14 +01:00
2007-03-09 09:55:07 +01:00
void PAG_init(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ i n i t
*
**************************************
*
* Functional description
* Initialize stuff for page handling.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-05-22 00:07:35 +02:00
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;
2008-12-22 10:00:05 +01:00
pageMgr.transPerTIP = (dbb->dbb_page_size - OFFSETA(tx_inv_page*, tip_transactions)) * 4;
2006-05-22 00:07:35 +02:00
pageSpace->ppFirst = 1;
2009-08-22 14:16:47 +02:00
// dbb_ods_version can be 0 when a new database is being created
2008-08-31 03:10:41 +02:00
if ((dbb->dbb_ods_version == 0) || (dbb->dbb_ods_version >= ODS_VERSION10))
2003-12-31 06:36:12 +01:00
{
2006-05-22 00:07:35 +02:00
pageMgr.gensPerPage =
2001-05-23 15:26:42 +02:00
(dbb->dbb_page_size -
2004-02-02 12:02:12 +01:00
OFFSETA(generator_page*, gpg_values)) / sizeof(((generator_page*) NULL)->gpg_values);
2003-12-31 06:36:12 +01:00
}
2009-08-23 09:31:48 +02:00
else
{
2006-05-22 00:07:35 +02:00
pageMgr.gensPerPage =
2001-05-23 15:26:42 +02:00
(dbb->dbb_page_size -
2004-02-02 12:02:12 +01:00
OFFSETA(pointer_page*, ppg_page)) / sizeof(((pointer_page*) NULL)->ppg_page);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// Compute the number of data pages per pointer page. Each data page
// requires a 32 bit pointer and a 2 bit control field.
2001-05-23 15:26:42 +02:00
2009-01-03 10:14:29 +01:00
dbb->dbb_dp_per_pp =
2008-12-22 10:00:05 +01:00
(dbb->dbb_page_size - OFFSETA(pointer_page*, ppg_page)) * 8 / (BITS_PER_LONG + 2);
2009-08-22 14:16:47 +02:00
// 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.
2004-02-02 12:02:12 +01:00
dbb->dbb_max_records = (dbb->dbb_page_size - sizeof(data_page)) /
(sizeof(data_page::dpg_repeat) + OFFSETA(rhd*, rhd_data));
2001-05-23 15:26:42 +02:00
// Artifically reduce density of records to test high bits of record number
// dbb->dbb_max_records = 32000;
2009-08-22 14:16:47 +02:00
// 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);
2009-08-22 14:16:47 +02:00
// 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)) /
2008-12-22 10:00:05 +01:00
(sizeof(index_root_page::irt_repeat) + (1 * (dbb->dbb_ods_version >= ODS_VERSION11) ?
2004-01-11 10:28:12 +01:00
sizeof(irtd) : sizeof(irtd_ods10)));
2009-08-22 14:16:47 +02:00
// 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
2001-05-23 15:26:42 +02:00
dbb->dbb_prefetch_sequence = PREFETCH_MAX_TRANSFER / dbb->dbb_page_size;
dbb->dbb_prefetch_pages = dbb->dbb_prefetch_sequence * 2;
#endif
2001-05-23 15:26:42 +02:00
}
void PAG_init2(thread_db* tdbb, USHORT shadow_number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ i n i t 2
*
**************************************
*
* Functional description
* Perform second phase of page initialization -- the eternal
* search for additional files.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
2003-12-31 06:36:12 +01:00
ISC_STATUS* status = tdbb->tdbb_status_vector;
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// 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.
2001-05-23 15:26:42 +02:00
SCHAR* const temp_buffer = FB_NEW(*getDefaultMemoryPool()) SCHAR[dbb->dbb_page_size + MIN_PAGE_SIZE];
2003-12-31 06:36:12 +01:00
SCHAR* temp_page =
2008-12-22 10:00:05 +01:00
(SCHAR*) (((U_IPTR) temp_buffer + MIN_PAGE_SIZE - 1) & ~((U_IPTR) MIN_PAGE_SIZE - 1));
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
jrd_file* file = pageSpace->file;
2009-08-23 09:31:48 +02:00
if (shadow_number)
{
Shadow* shadow = dbb->dbb_shadow;
2009-08-23 09:31:48 +02:00
for (; shadow; shadow = shadow->sdw_next)
{
if (shadow->sdw_number == shadow_number)
{
2001-05-23 15:26:42 +02:00
file = shadow->sdw_file;
break;
}
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
if (!shadow)
2009-08-22 14:16:47 +02:00
BUGCHECK(161); // msg 161 shadow block not found
2001-05-23 15:26:42 +02:00
}
2003-12-31 06:36:12 +01:00
USHORT sequence = 1;
2006-05-22 00:07:35 +02:00
WIN window(DB_PAGE_SPACE, -1);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
TEXT buf[MAXPATHLEN + 1];
2009-08-22 14:16:47 +02:00
// Loop thru files and header pages until everything is open
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
for (;;)
{
2003-12-31 06:36:12 +01:00
TEXT* file_name = NULL;
2001-05-23 15:26:42 +02:00
window.win_page = file->fil_min_page;
2003-12-31 06:36:12 +01:00
USHORT file_length = 0;
ULONG last_page = 0;
BufferDesc temp_bdb;
2003-12-31 06:36:12 +01:00
SLONG next_page = 0;
2001-05-23 15:26:42 +02:00
do {
2009-08-22 14:16:47 +02:00
// 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
2009-08-23 03:27:46 +02:00
// because the corresponding page in the main database file may not exist
2001-05-23 15:26:42 +02:00
if (!file->fil_min_page)
CCH_FETCH(tdbb, &window, LCK_read, pag_header);
header_page* header = (header_page*) temp_page;
2001-05-23 15:26:42 +02:00
temp_bdb.bdb_buffer = (PAG) header;
temp_bdb.bdb_page = window.win_page;
temp_bdb.bdb_dbb = dbb;
2009-08-22 14:16:47 +02:00
// Read the required page into the local buffer
2001-05-23 15:26:42 +02:00
PIO_read(file, &temp_bdb, (PAG) header, status);
2008-12-22 10:00:05 +01:00
if (shadow_number && !file->fil_min_page)
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
2007-09-20 12:04:43 +02:00
for (const UCHAR* p = header->hdr_data; *p != HDR_end; p += 2 + p[1])
2003-12-31 06:36:12 +01:00
{
2009-01-07 10:30:57 +01:00
switch (*p)
{
2001-05-23 15:26:42 +02:00
case HDR_file:
file_length = p[1];
file_name = buf;
2008-02-03 11:41:44 +01:00
memcpy(buf, p + 2, file_length);
2001-05-23 15:26:42 +02:00
break;
case HDR_last_page:
2008-02-03 11:41:44 +01:00
memcpy(&last_page, p + 2, sizeof(last_page));
2001-05-23 15:26:42 +02:00
break;
case HDR_sweep_interval:
2005-07-31 05:45:20 +02:00
// CVC: Let's copy it always.
//if (!(dbb->dbb_flags & DBB_read_only))
2008-02-03 11:41:44 +01:00
memcpy(&dbb->dbb_sweep_interval, p + 2, sizeof(SLONG));
2001-05-23 15:26:42 +02:00
break;
}
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
next_page = header->hdr_next_page;
2008-12-22 10:00:05 +01:00
if (!shadow_number && !file->fil_min_page)
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
window.win_page = next_page;
2009-08-22 14:16:47 +02:00
// 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.
2001-05-23 15:26:42 +02:00
} while (next_page);
if (file->fil_min_page)
file->fil_fudge = 1;
if (!file_name)
break;
2009-08-22 14:16:47 +02:00
// Verify database file path against DatabaseAccess entry of firebird.conf
2003-10-01 17:27:00 +02:00
file_name[file_length] = 0;
2009-08-23 09:31:48 +02:00
if (!JRD_verify_database_access(file_name))
{
2008-08-31 03:10:41 +02:00
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("additional database file") <<
Arg::Str(file_name));
2003-10-01 17:27:00 +02:00
}
file->fil_next = PIO_open(dbb, file_name, file_name, false);
2001-05-23 15:26:42 +02:00
file->fil_max_page = last_page;
file = file->fil_next;
if (dbb->dbb_flags & (DBB_force_write | DBB_no_fs_cache))
2007-07-26 03:23:18 +02:00
{
2008-12-22 10:00:05 +01:00
PIO_force_write(file, dbb->dbb_flags & DBB_force_write, dbb->dbb_flags & DBB_no_fs_cache);
2007-07-26 03:23:18 +02:00
}
2001-05-23 15:26:42 +02:00
file->fil_min_page = last_page + 1;
file->fil_sequence = sequence++;
}
delete[] temp_buffer;
2001-12-24 03:51:06 +01:00
} // try
2009-08-23 09:31:48 +02:00
catch (const Firebird::Exception&)
{
delete[] temp_buffer;
throw;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
SLONG PAG_last_page(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-05-22 00:07:35 +02:00
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);
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// Find the last page allocated
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
ULONG relative_bit = 0;
USHORT sequence;
2009-01-07 10:30:57 +01:00
for (sequence = 0; true; ++sequence)
{
2008-12-22 10:00:05 +01:00
window.win_page = (!sequence) ? pageSpace->ppFirst : sequence * pages_per_pip - 1;
const page_inv_page* page = (page_inv_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_pages);
const UCHAR* bits = page->pip_bits + (pages_per_pip >> 3) - 1;
while (*bits == (UCHAR) - 1)
--bits;
2003-12-31 06:36:12 +01:00
SSHORT bit;
2009-08-23 09:31:48 +02:00
for (bit = 7; bit >= 0; --bit)
{
2001-05-23 15:26:42 +02:00
if (!(*bits & (1 << bit)))
break;
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
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(thread_db* tdbb, const PageNumber& number, const PageNumber& prior_page)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ r e l e a s e _ p a g e
*
**************************************
*
* Functional description
* Release a page to the free page page.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
#ifdef VIO_DEBUG
if (debug_flag > DEBUG_WRITES_INFO)
2008-12-03 16:17:58 +01:00
printf("\tPAG_release_page: about to release page %"SLONGFORMAT"\n", number.getPageNum());
2001-05-23 15:26:42 +02:00
#endif
2006-05-22 00:07:35 +02:00
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;
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN pip_window(number.getPageSpaceID(), (sequence == 0) ?
pageSpace->ppFirst : sequence * pageMgr.pagesPerPIP - 1);
2001-05-23 15:26:42 +02:00
2008-12-22 10:00:05 +01:00
page_inv_page* pages = (page_inv_page*) CCH_FETCH(tdbb, &pip_window, LCK_write, pag_pages);
2001-05-23 15:26:42 +02:00
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);
2006-05-22 00:07:35 +02:00
pageSpace->pipHighWater = MIN(pageSpace->pipHighWater, sequence);
2001-05-23 15:26:42 +02:00
}
void PAG_set_force_write(thread_db* tdbb, bool flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ s e t _ f o r c e _ w r i t e
*
**************************************
*
* Functional description
* Turn on/off force write.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, &window);
2009-08-23 09:31:48 +02:00
if (flag)
{
2001-05-23 15:26:42 +02:00
header->hdr_flags |= hdr_force_write;
dbb->dbb_flags |= DBB_force_write;
}
2009-08-23 09:31:48 +02:00
else
{
2001-05-23 15:26:42 +02:00
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);
2006-05-22 00:07:35 +02:00
for (jrd_file* file = pageSpace->file; file; file = file->fil_next) {
PIO_force_write(file, flag, dbb->dbb_flags & DBB_no_fs_cache);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
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, dbb->dbb_flags & DBB_no_fs_cache);
}
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
}
void PAG_set_no_reserve(thread_db* tdbb, bool flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ s e t _ n o _ r e s e r v e
*
**************************************
*
* Functional description
* Turn on/off reserving space for versions
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, &window);
2009-08-23 09:31:48 +02:00
if (flag)
{
2001-05-23 15:26:42 +02:00
header->hdr_flags |= hdr_no_reserve;
dbb->dbb_flags |= DBB_no_reserve;
}
2009-08-23 09:31:48 +02:00
else
{
2001-05-23 15:26:42 +02:00
header->hdr_flags &= ~hdr_no_reserve;
dbb->dbb_flags &= ~DBB_no_reserve;
}
CCH_RELEASE(tdbb, &window);
}
void PAG_set_db_readonly(thread_db* tdbb, bool flag)
2001-05-23 15:26:42 +02:00
{
/*********************************************
*
* 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
*
*********************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (!flag)
{
2009-08-22 14:16:47 +02:00
// 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
2001-05-23 15:26:42 +02:00
header->hdr_flags &= ~hdr_read_only;
dbb->dbb_flags &= ~DBB_read_only;
}
CCH_MARK_MUST_WRITE(tdbb, &window);
2009-08-23 09:31:48 +02:00
if (flag)
{
2001-05-23 15:26:42 +02:00
header->hdr_flags |= hdr_read_only;
dbb->dbb_flags |= DBB_read_only;
}
CCH_RELEASE(tdbb, &window);
}
void PAG_set_db_SQL_dialect(thread_db* tdbb, SSHORT flag)
2001-05-23 15:26:42 +02:00
{
/*********************************************
*
* 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
*
*********************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
const USHORT major_version = dbb->dbb_ods_version;
const USHORT minor_original = dbb->dbb_minor_original;
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
if (flag && (ENCODE_ODS(major_version, minor_original) >= ODS_10_0))
{
switch (flag)
{
2001-05-23 15:26:42 +02:00
case SQL_DIALECT_V5:
2008-12-22 10:00:05 +01:00
if (dbb->dbb_flags & DBB_DB_SQL_dialect_3 || header->hdr_flags & hdr_SQL_dialect_3)
2003-12-31 06:36:12 +01:00
{
// Check the returned value here!
ERR_post_warning(Arg::Warning(isc_dialect_reset_warning));
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
dbb->dbb_flags &= ~DBB_DB_SQL_dialect_3; // set to 0
header->hdr_flags &= ~hdr_SQL_dialect_3; // set to 0
2001-05-23 15:26:42 +02:00
break;
case SQL_DIALECT_V6:
2009-08-22 14:16:47 +02:00
dbb->dbb_flags |= DBB_DB_SQL_dialect_3; // set to dialect 3
header->hdr_flags |= hdr_SQL_dialect_3; // set to dialect 3
2001-05-23 15:26:42 +02:00
break;
default:
CCH_RELEASE(tdbb, &window);
ERR_post(Arg::Gds(isc_inv_dialect_specified) << Arg::Num(flag) <<
Arg::Gds(isc_valid_db_dialects) << Arg::Str("1 and 3") <<
Arg::Gds(isc_dialect_not_changed));
2001-05-23 15:26:42 +02:00
break;
}
}
CCH_MARK_MUST_WRITE(tdbb, &window);
CCH_RELEASE(tdbb, &window);
}
void PAG_set_page_buffers(thread_db* tdbb, ULONG buffers)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2006-04-23 10:25:10 +02:00
err_post_if_database_is_readonly(dbb);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window(HEADER_PAGE_NUMBER);
header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, &window);
header->hdr_page_buffers = buffers;
CCH_RELEASE(tdbb, &window);
}
void PAG_sweep_interval(thread_db* tdbb, SLONG interval)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P A G _ s w e e p _ i n t e r v a l
*
**************************************
*
* Functional description
* Set sweep interval.
*
**************************************/
SET_TDBB(tdbb);
PAG_add_clump(tdbb, HEADER_PAGE, HDR_sweep_interval, sizeof(SLONG),
2009-01-07 10:30:57 +01:00
(UCHAR*) &interval, CLUMP_REPLACE); //, true
2001-05-23 15:26:42 +02:00
}
static int blocking_ast_attachment(void* ast_object)
{
Jrd::Attachment* const attachment = static_cast<Jrd::Attachment*>(ast_object);
try
{
Database* const dbb = attachment->att_database;
Database::SyncGuard dsGuard(dbb, true);
ThreadContextHolder tdbb;
tdbb->setDatabase(dbb);
tdbb->setAttachment(attachment);
Jrd::ContextPoolHolder context(tdbb, dbb->dbb_permanent);
attachment->att_flags |= ATT_shutdown;
attachment->cancelExternalConnection(tdbb);
JRD_shutdown_attachments(dbb);
LCK_release(tdbb, attachment->att_id_lock);
}
catch (const Firebird::Exception&)
{} // no-op
return 0;
}
static void find_clump_space(thread_db* tdbb,
SLONG page_num,
WIN* window,
PAG* ppage,
2001-05-23 15:26:42 +02:00
USHORT type,
USHORT len,
2009-01-07 10:30:57 +01:00
const UCHAR* entry) //USHORT must_write
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* 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.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2003-12-31 06:36:12 +01:00
pag* page = *ppage;
header_page* header = 0; // used after the loop
2003-12-31 06:36:12 +01:00
log_info_page* logp = 0; // used after the loop
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
while (true)
{
2003-12-31 06:36:12 +01:00
SLONG next_page, free_space;
USHORT* end_addr;
UCHAR* p;
2009-08-23 09:31:48 +02:00
if (page_num == HEADER_PAGE)
{
header = (header_page*) page;
2001-05-23 15:26:42 +02:00
next_page = header->hdr_next_page;
free_space = dbb->dbb_page_size - header->hdr_end;
end_addr = &header->hdr_end;
2009-08-23 13:04:36 +02:00
p = (UCHAR*) header + header->hdr_end;
2001-05-23 15:26:42 +02:00
}
2009-08-23 09:31:48 +02:00
else
{
logp = (log_info_page*) page;
2001-05-23 15:26:42 +02:00
next_page = logp->log_next_page;
free_space = dbb->dbb_page_size - logp->log_end;
end_addr = &logp->log_end;
2009-08-23 13:04:36 +02:00
p = (UCHAR*) logp + logp->log_end;
2001-05-23 15:26:42 +02:00
}
2009-01-07 10:30:57 +01:00
if (free_space > (2 + len))
{
//if (must_write)
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, window);
2009-01-07 10:30:57 +01:00
//else
// CCH_MARK(tdbb, window);
2001-05-23 15:26:42 +02:00
2003-11-04 00:59:24 +01:00
fb_assert(type <= MAX_UCHAR);
fb_assert(len <= MAX_UCHAR);
*p++ = static_cast<UCHAR>(type);
*p++ = static_cast<UCHAR>(len);
2001-05-23 15:26:42 +02:00
if (len)
{
memcpy(p, entry, len);
p += len;
}
2001-05-23 15:26:42 +02:00
*p = HDR_end;
2009-08-23 13:04:36 +02:00
*end_addr = (USHORT) (p - (UCHAR*) page);
2001-05-23 15:26:42 +02:00
return;
}
if (!next_page)
break;
2009-08-22 14:16:47 +02:00
// Follow chain of header pages
2001-05-23 15:26:42 +02:00
if (page_num == HEADER_PAGE)
2008-12-22 10:00:05 +01:00
*ppage = page = CCH_HANDOFF(tdbb, window, next_page, LCK_write, pag_header);
2001-05-23 15:26:42 +02:00
else
2008-12-22 10:00:05 +01:00
*ppage = page = CCH_HANDOFF(tdbb, window, next_page, LCK_write, pag_log);
2001-05-23 15:26:42 +02:00
}
2006-05-22 00:07:35 +02:00
WIN new_window(DB_PAGE_SPACE, -1);
2003-12-31 06:36:12 +01:00
pag* new_page = (PAG) DPM_allocate(tdbb, &new_window);
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
//if (must_write)
2001-05-23 15:26:42 +02:00
CCH_MARK_MUST_WRITE(tdbb, &new_window);
2009-01-07 10:30:57 +01:00
//else
// CCH_MARK(tdbb, &new_window);
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
header_page* new_header = 0;
2003-12-31 06:36:12 +01:00
log_info_page* new_logp = 0;
SLONG next_page;
USHORT* end_addr;
UCHAR* p;
2009-08-23 09:31:48 +02:00
if (page_num == HEADER_PAGE)
{
new_header = (header_page*) new_page;
new_header->hdr_header.pag_type = pag_header;
2001-05-23 15:26:42 +02:00
new_header->hdr_end = HDR_SIZE;
new_header->hdr_page_size = dbb->dbb_page_size;
new_header->hdr_data[0] = HDR_end;
2006-05-22 00:07:35 +02:00
next_page = new_window.win_page.getPageNum();
2001-05-23 15:26:42 +02:00
end_addr = &new_header->hdr_end;
p = new_header->hdr_data;
}
2009-08-23 09:31:48 +02:00
else
{
new_logp = (log_info_page*) new_page;
new_logp->log_header.pag_type = pag_log;
2001-05-23 15:26:42 +02:00
new_logp->log_data[0] = LOG_end;
new_logp->log_end = LIP_SIZE;
2006-05-22 00:07:35 +02:00
next_page = new_window.win_page.getPageNum();
2001-05-23 15:26:42 +02:00
end_addr = &new_logp->log_end;
p = new_logp->log_data;
}
2003-11-04 00:59:24 +01:00
fb_assert(type <= MAX_UCHAR);
fb_assert(len <= MAX_UCHAR);
*p++ = static_cast<UCHAR>(type);
*p++ = static_cast<UCHAR>(len);
2001-05-23 15:26:42 +02:00
if (len)
{
memcpy(p, entry, len);
p += len;
2001-05-23 15:26:42 +02:00
}
*p = HDR_end;
2009-08-23 13:04:36 +02:00
*end_addr = (USHORT) (p - (UCHAR*) new_page);
2001-05-23 15:26:42 +02:00
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(thread_db* tdbb,
SLONG page_num,
2006-12-04 22:29:01 +01:00
WIN* window,
PAG* ppage,
USHORT lock,
USHORT type,
UCHAR** entry_p,
const UCHAR** clump_end)
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* 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.
2003-12-31 06:36:12 +01:00
* true - Found it
* false - Not present
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
2009-01-07 10:30:57 +01:00
while (true)
{
header_page* header = 0;
2003-12-31 06:36:12 +01:00
log_info_page* logp = 0;
UCHAR* p;
SLONG next_page;
2009-08-23 09:31:48 +02:00
if (page_num == HEADER_PAGE)
{
header = (header_page*) (*ppage);
2001-05-23 15:26:42 +02:00
p = header->hdr_data;
next_page = header->hdr_next_page;
}
2009-08-23 09:31:48 +02:00
else
{
logp = (log_info_page*) (*ppage);
2001-05-23 15:26:42 +02:00
p = logp->log_data;
next_page = logp->log_next_page;
}
2003-12-31 06:36:12 +01:00
UCHAR* q = 0;
2009-08-23 09:31:48 +02:00
for (; (*p != HDR_end); p += 2 + p[1])
{
2001-05-23 15:26:42 +02:00
if (*p == type)
q = p;
}
2009-08-23 09:31:48 +02:00
if (q)
{
2001-05-23 15:26:42 +02:00
*entry_p = q;
*clump_end = p;
2003-12-31 06:36:12 +01:00
return true;
2001-05-23 15:26:42 +02:00
}
2009-08-22 14:16:47 +02:00
// Follow chain of pages
2001-05-23 15:26:42 +02:00
2009-08-23 09:31:48 +02:00
if (next_page)
{
2003-12-31 06:36:12 +01:00
if (page_num == HEADER_PAGE) {
2008-12-22 10:00:05 +01:00
*ppage = CCH_HANDOFF(tdbb, window, next_page, lock, pag_header);
2003-12-31 06:36:12 +01:00
}
else {
2001-05-23 15:26:42 +02:00
*ppage = CCH_HANDOFF(tdbb, window, next_page, lock, pag_log);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
}
else
2003-12-31 06:36:12 +01:00
return false;
2001-05-23 15:26:42 +02:00
}
}
2006-05-22 00:07:35 +02:00
PageSpace::~PageSpace()
{
2009-08-23 09:31:48 +02:00
if (file)
{
2006-05-22 00:07:35 +02:00
PIO_close(file);
while (file)
{
jrd_file* next = file->fil_next;
delete file;
file = next;
}
2006-05-22 00:07:35 +02:00
}
}
ULONG PageSpace::actAlloc(const USHORT pageSize)
{
/**************************************
*
* Functional description
* Compute actual number of physically allocated pages of database.
*
**************************************/
2008-12-05 02:20:14 +01:00
// Traverse the linked list of files and add up the
// number of pages in each file
ULONG tot_pages = 0;
2007-05-02 08:57:18 +02:00
for (const jrd_file* f = file; f != NULL; f = f->fil_next) {
tot_pages += PIO_get_number_of_pages(f, pageSize);
}
return tot_pages;
}
2007-07-02 12:28:50 +02:00
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.
*
**************************************/
2007-05-02 08:57:18 +02:00
const jrd_file* f = file;
while (f->fil_next) {
f = f->fil_next;
}
2008-12-22 10:00:05 +01:00
const ULONG nPages = f->fil_min_page - f->fil_fudge + PIO_get_number_of_pages(f, pageSize);
if (maxPageNumber < nPages)
maxPageNumber = nPages;
return nPages;
}
2007-07-02 12:28:50 +02:00
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
2008-12-05 02:20:14 +01:00
* 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).
2008-12-05 02:20:14 +01:00
*
* 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;
Database* dbb = tdbb->getDatabase();
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)
2007-04-27 04:28:57 +02:00
{
2008-12-05 02:20:14 +01:00
try
{
PIO_extend(dbb, file, extPages, dbb->dbb_page_size);
break;
}
2008-12-05 02:20:14 +01:00
catch (Firebird::status_exception)
{
2008-12-05 02:20:14 +01:00
if (extPages > reqPages)
{
extPages = MAX(reqPages, extPages / 2);
fb_utils::init_status(tdbb->tdbb_status_vector);
}
2008-12-05 02:20:14 +01:00
else
{
2008-12-05 02:20:14 +01:00
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;
}
}
2007-04-27 04:28:57 +02:00
}
2008-12-05 02:20:14 +01:00
maxPageNumber = 0;
}
return true;
}
2006-05-22 00:07:35 +02:00
PageSpace* PageManager::addPageSpace(const USHORT pageSpaceID)
{
PageSpace* newPageSpace = findPageSpace(pageSpaceID);
if (!newPageSpace)
{
newPageSpace = FB_NEW(pool) PageSpace(pageSpaceID);
pageSpaces.add(newPageSpace);
}
return newPageSpace;
}
2007-07-02 12:28:50 +02:00
PageSpace* PageManager::findPageSpace(const USHORT pageSpace) const
2006-05-22 00:07:35 +02:00
{
size_t pos;
if (pageSpaces.find(pageSpace, pos)) {
return pageSpaces[pos];
}
2006-09-05 10:17:26 +02:00
return 0;
2006-05-22 00:07:35 +02:00
}
void PageManager::delPageSpace(const USHORT pageSpace)
{
size_t pos;
if (pageSpaces.find(pageSpace, pos))
{
2006-09-01 12:51:57 +02:00
PageSpace* pageSpaceToDelete = pageSpaces[pos];
2006-05-22 00:07:35 +02:00
pageSpaces.remove(pos);
2006-09-01 12:51:57 +02:00
delete pageSpaceToDelete;
2006-05-22 00:07:35 +02:00
}
}
void PageManager::closeAll()
{
2006-05-23 05:37:00 +02:00
for (size_t i = 0; i < pageSpaces.getCount(); i++)
2009-01-07 10:30:57 +01:00
{
2006-05-22 00:07:35 +02:00
if (pageSpaces[i]->file) {
PIO_close(pageSpaces[i]->file);
}
2009-01-07 10:30:57 +01:00
}
2006-05-22 00:07:35 +02:00
}
void PageManager::releaseLocks()
{
#ifdef WIN_NT
for (size_t i = 0; i < pageSpaces.getCount(); i++)
2007-09-19 03:46:45 +02:00
{
2009-08-23 09:31:48 +02:00
if (pageSpaces[i]->file && pageSpaces[i]->file->fil_ext_lock)
{
delete pageSpaces[i]->file->fil_ext_lock;
pageSpaces[i]->file->fil_ext_lock = NULL;
}
2007-09-19 03:46:45 +02:00
}
#endif
}
2006-05-22 00:07:35 +02:00
USHORT PageManager::getTempPageSpaceID(thread_db* tdbb)
{
USHORT result;
2006-05-22 00:07:35 +02:00
#ifdef SUPERSERVER
result = TEMP_PAGE_SPACE;
2006-05-22 00:07:35 +02:00
#else
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
Jrd::Attachment* att = tdbb->getAttachment();
2006-05-22 00:07:35 +02:00
if (!att->att_temp_pg_lock)
{
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock();
2006-05-22 00:07:35 +02:00
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(SLONG);
2006-05-22 00:07:35 +02:00
lock->lck_dbb = dbb;
2007-02-26 02:01:17 +01:00
PAG_attachment_id(tdbb);
2006-05-23 05:37:00 +02:00
while (true)
2006-05-22 00:07:35 +02:00
{
const double tmp = rand() * (MAX_USHORT - TEMP_PAGE_SPACE - 1.0) / (RAND_MAX + 1.0);
2007-04-11 18:05:40 +02:00
lock->lck_key.lck_long = static_cast<SLONG>(tmp) + TEMP_PAGE_SPACE + 1;
2006-05-22 00:07:35 +02:00
if (LCK_lock(tdbb, lock, LCK_write, LCK_NO_WAIT))
break;
}
att->att_temp_pg_lock = lock;
}
2008-12-05 02:20:14 +01:00
result = (USHORT) att->att_temp_pg_lock->lck_key.lck_long;
2006-05-22 00:07:35 +02:00
#endif
if (!this->findPageSpace(result)) {
PAG_attach_temp_pages(tdbb, result);
}
return result;
2006-05-22 00:07:35 +02:00
}
ULONG PAG_page_count(Database* database, PageCountCallback* cb)
{
/*********************************************
*
* P A G _ p a g e _ c o u n t
*
*********************************************
*
* Functional description
* Count pages, used by database
*
*********************************************/
fb_assert(cb);
2009-01-03 10:14:29 +01:00
const bool isODS11_x =
2008-12-22 10:00:05 +01:00
(database->dbb_ods_version == ODS_VERSION11 && database->dbb_minor_version >= 1);
if (!isODS11_x) {
return 0;
}
Firebird::Array<BYTE> temp;
2008-03-15 18:55:27 +01:00
page_inv_page* pip = (Ods::page_inv_page*) // can't reinterpret_cast<> here
FB_ALIGN((IPTR) temp.getBuffer(database->dbb_page_size + MIN_PAGE_SIZE), MIN_PAGE_SIZE);
PageSpace* pageSpace = database->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
fb_assert(pageSpace);
2008-03-15 18:55:27 +01:00
ULONG pageNo = pageSpace->ppFirst;
const ULONG pagesPerPip = database->dbb_page_manager.pagesPerPIP;
2008-12-22 10:00:05 +01:00
for (ULONG sequence = 0; true; pageNo = (pagesPerPip * ++sequence) - 1)
{
cb->newPage(pageNo, &pip->pip_header);
fb_assert(pip->pip_header.pag_type == pag_pages);
2008-12-05 02:20:14 +01:00
if (pip->pip_header.reserved == pagesPerPip)
{
// this is not last page, continue search
continue;
}
return pip->pip_header.reserved + pageNo + (sequence ? 1 : -1);
}
// compiler warnings silencer
return 0;
}