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