/[pkg-firebird]/upstream/current/src/jrd/pag.cpp
ViewVC logotype

Contents of /upstream/current/src/jrd/pag.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1141 - (show annotations) (download)
Sun Apr 1 15:48:28 2007 UTC (6 years, 2 months ago) by dmn
File size: 61683 byte(s)
Import current upstream CVS
1 /*
2 * PROGRAM: JRD Access Method
3 * MODULE: pag.cpp
4 * DESCRIPTION: Page level ods manager
5 *
6 * The contents of this file are subject to the Interbase Public
7 * License Version 1.0 (the "License"); you may not use this file
8 * except in compliance with the License. You may obtain a copy
9 * of the License at http://www.Inprise.com/IPL.html
10 *
11 * Software distributed under the License is distributed on an
12 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
13 * or implied. See the License for the specific language governing
14 * rights and limitations under the License.
15 *
16 * The Original Code was created by Inprise Corporation
17 * and its predecessors. Portions created by Inprise Corporation are
18 * Copyright (C) Inprise Corporation.
19 *
20 * All Rights Reserved.
21 * Contributor(s): ______________________________________.
22 *
23 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete ports:
24 * - EPSON, DELTA, IMP, NCR3000, M88K
25 * - HP9000 s300 and Apollo
26 *
27 * 2002.10.27 Sean Leyne - Completed removal of obsolete "DG_X86" port
28 * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "UNIXWARE" port
29 * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix" port
30 * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix/MIPS" port
31 *
32 * 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
33 * 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
34 * 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
35 * 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "SGI" port
36 *
37 * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
38 *
39 */
40
41 /*
42 * Modified by: Patrick J. P. Griffin
43 * Date: 11/29/2000
44 * Problem: Bug 116733 Too many generators corrupt database.
45 * DPM_gen_id was not calculating page and offset correctly.
46 * Change: Caculate pgc_gpg, number of generators per page,
47 * for use in DPM_gen_id.
48 */
49
50 /* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
51 * conditionals, as the engine now fully supports
52 * readonly databases.
53 *
54 * 2001.08.07 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
55 * conditionals, second attempt
56 *
57 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "MAC" and "MAC_CP" defines
58 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "Apollo" port
59 *
60 */
61
62 #include "firebird.h"
63 #include "../jrd/common.h"
64 #include <stdio.h>
65 #include <string.h>
66
67 #ifdef WIN_NT
68 #include <process.h>
69 #endif
70
71 #include "../jrd/fil.h"
72 #include "../jrd/jrd.h"
73 #include "../jrd/pag.h"
74 #include "../jrd/ods.h"
75 #include "../jrd/os/pio.h"
76 #include "../jrd/os/path_utils.h"
77 #include "../jrd/ibase.h"
78 #include "../jrd/gdsassert.h"
79 #include "../jrd/lck.h"
80 #include "../jrd/sdw.h"
81 #include "../jrd/cch.h"
82 #include "../jrd/tra.h"
83 #ifdef VIO_DEBUG
84 #include "../jrd/vio_debug.h"
85 #include "../jrd/all.h"
86 #endif
87 #include "../jrd/cch_proto.h"
88 #include "../jrd/dpm_proto.h"
89 #include "../jrd/err_proto.h"
90 #include "../jrd/gds_proto.h"
91 #include "../jrd/lck_proto.h"
92 #include "../jrd/met_proto.h"
93 #include "../jrd/mov_proto.h"
94 #include "../jrd/ods_proto.h"
95 #include "../jrd/pag_proto.h"
96 #include "../jrd/os/pio_proto.h"
97 #include "../jrd/thd.h"
98 #include "../jrd/isc_f_proto.h"
99 #include "../jrd/TempSpace.h"
100
101 using namespace Jrd;
102 using namespace Ods;
103
104 static void find_clump_space(SLONG, WIN*, pag**, USHORT, SSHORT, const UCHAR*,
105 USHORT);
106 static bool find_type(SLONG, WIN*, pag**, USHORT, USHORT, UCHAR**,
107 const UCHAR**);
108
109 inline void err_post_if_database_is_readonly(const Database* dbb)
110 {
111 if (dbb->dbb_flags & DBB_read_only)
112 ERR_post(isc_read_only_database, 0);
113 }
114
115 // Class definitions (obsolete platforms are commented out)
116 // Class constant name consists of OS platform and CPU architecture.
117 //
118 // For ports created before Firebird 2.0 release 64-bit and 32-bit
119 // sub-architectures of the same CPU should use different classes.
120 // For 64-bit ports first created after or as a part of Firebird 2.0
121 // release CPU architecture may be the same for both variants.
122
123 static const int CLASS_UNKNOWN = 0;
124 //static const CLASS_APOLLO_68K = 1; // Apollo 68K, Dn 10K
125 static const int CLASS_SOLARIS_SPARC = 2; // Sun 68k, Sun Sparc, HP 9000/300, MAC AUX, IMP, DELTA, NeXT, UNIXWARE, DG_X86
126 static const int CLASS_SOLARIS_I386 = 3; // Sun 386i
127 //static const CLASS_VMS_VAX = 4; // VMS/VAX
128 //static const CLASS_ULTRIX_VAX = 5; // Ultrix/VAX
129 //static const CLASS_ULTRIX_MIPS = 6; // Ultrix/MIPS
130 static const int CLASS_HPUX_PA = 7; // HP-UX on PA-RISC (was: HP 900/800 (precision))
131 static const int CLASS_NETWARE_I386 = 8; // NetWare
132 //static const CLASS_MAC_OS = 9; // MAC-OS
133 static const int CLASS_AIX_PPC = 10; // AIX on PowerPC platform (was: IBM RS/6000)
134 //static const CLASS_DG_AVIION = 11; // DG AViiON
135 //static const CLASS_MPE_XL = 12; // MPE/XL
136 static const int CLASS_IRIX_MIPS = 13; // Silicon Graphics/IRIS
137 //static const int CLASS_CRAY = 14; // Cray
138 static const int CLASS_TRU64_ALPHA = 15; // Tru64 Unix running on Alpha (was: Dec OSF/1)
139 static const int CLASS_WINDOWS_I386 = 16; // NT -- post 4.0 (delivered 4.0 as class 8)
140 //static const CLASS_OS2 = 17; // OS/2
141 //static const CLASS_WIN16 = 18; // Windows 16 bit
142 static const int CLASS_LINUX_I386 = 19; // LINUX on Intel series
143 static const int CLASS_LINUX_SPARC = 20; // LINUX on sparc systems
144 static const int CLASS_FREEBSD_I386 = 21; // FreeBSD/i386
145 static const int CLASS_NETBSD_I386 = 22; // NetBSD/i386
146 static const int CLASS_DARWIN_PPC = 23; // Darwin/PowerPC
147 static const int CLASS_LINUX_AMD64 = 24; // LINUX on AMD64 systems
148 static const int CLASS_FREEBSD_AMD64 = 25;// FreeBSD/amd64
149 static const int CLASS_WINDOWS_AMD64 = 26;// Windows/amd64
150 static const int CLASS_LINUX_PPC = 27; // LINUX/PowerPC
151 static const int CLASS_DARWIN_I386 = 28; //Darwin/Intel
152 static const int CLASS_LINUX_MIPSEL = 29; // LINUX/MIPS
153 static const int CLASS_MAX10 = CLASS_LINUX_AMD64; // This should not be changed, no new ports with ODS10
154 static const int CLASS_MAX = CLASS_LINUX_MIPSEL;
155
156 // ARCHITECTURE COMPATIBILITY CLASSES
157
158 // For ODS10 and earlier things which normally define ODS compatibility are:
159 // 1) endianness (big-endian/little-endian)
160 // 2) alignment (32-bit or 64-bit), matters for record formats
161 // 3) pointer size (32-bit or 64-bit), also matters for record formats
162 //
163 // For ODS11 pointers are not stored in database and alignment is always 64-bit.
164 // So the only thing which normally matters for ODS11 is endiannes, but if
165 // endianness is wrong we are going to notice it during ODS version check,
166 // before architecture compatibility is tested. But we distinguish them here too,
167 // for consistency.
168
169 enum ArchitectureType {
170 archUnknown, // Unknown architecture, allow opening database only if CLASS matches exactly
171 archIntel86, // Little-endian platform with 32-bit pointers and 32-bit alignment (ODS10)
172 archLittleEndian, // Any little-endian platform with standard layout of data
173 archBigEndian // Any big-endian platform with standard layout of data
174 };
175
176 // Note that Sparc, HP and PowerPC disk structures should be compatible in theory,
177 // but in practice alignment on these platforms varies and actually depends on the
178 // compiler used to produce the build. Yes, some 32-bit RISC builds use 64-bit alignment.
179 // This is why we declare all such builds "Unknown" for ODS10.
180
181 static ArchitectureType archMatrix10[CLASS_MAX10 + 1] = {
182 archUnknown, // CLASS_UNKNOWN
183 archUnknown, // CLASS_APOLLO_68K
184 archUnknown, // CLASS_SOLARIS_SPARC
185 archIntel86, // CLASS_SOLARIS_I386
186 archUnknown, // CLASS_VMS_VAX
187 archUnknown, // CLASS_ULTRIX_VAX
188 archUnknown, // CLASS_ULTRIX_MIPS
189 archUnknown, // CLASS_HPUX_PA
190 archUnknown, // CLASS_NETWARE_I386
191 archUnknown, // CLASS_MAC_OS
192 archUnknown, // CLASS_AIX_PPC
193 archUnknown, // CLASS_DG_AVIION
194 archUnknown, // CLASS_MPE_XL
195 archUnknown, // CLASS_IRIX_MIPS
196 archUnknown, // CLASS_CRAY
197 archUnknown, // CLASS_TRU64_ALPHA
198 archIntel86, // CLASS_WINDOWS_I386
199 archUnknown, // CLASS_OS2
200 archUnknown, // CLASS_WIN16
201 archIntel86, // CLASS_LINUX_I386
202 archUnknown, // CLASS_LINUX_SPARC
203 archIntel86, // CLASS_FREEBSD_I386
204 archIntel86, // CLASS_NETBSD_I386
205 archUnknown, // CLASS_DARWIN_PPC
206 archUnknown // CLASS_LINUX_AMD64
207 };
208
209 static ArchitectureType archMatrix[CLASS_MAX + 1] = {
210 archUnknown, // CLASS_UNKNOWN
211 archUnknown, // CLASS_APOLLO_68K
212 archBigEndian, // CLASS_SOLARIS_SPARC
213 archLittleEndian, // CLASS_SOLARIS_I386
214 archUnknown, // CLASS_VMS_VAX
215 archUnknown, // CLASS_ULTRIX_VAX
216 archUnknown, // CLASS_ULTRIX_MIPS
217 archBigEndian, // CLASS_HPUX_PA
218 archUnknown, // CLASS_NETWARE_I386
219 archUnknown, // CLASS_MAC_OS
220 archBigEndian, // CLASS_AIX_PPC
221 archUnknown, // CLASS_DG_AVIION
222 archUnknown, // CLASS_MPE_XL
223 archBigEndian, // CLASS_IRIX_MIPS
224 archUnknown, // CLASS_CRAY
225 archBigEndian, // CLASS_TRU64_ALPHA
226 archLittleEndian, // CLASS_WINDOWS_I386
227 archUnknown, // CLASS_OS2
228 archUnknown, // CLASS_WIN16
229 archLittleEndian, // CLASS_LINUX_I386
230 archBigEndian, // CLASS_LINUX_SPARC
231 archLittleEndian, // CLASS_FREEBSD_I386
232 archLittleEndian, // CLASS_NETBSD_I386
233 archBigEndian, // CLASS_DARWIN_PPC
234 archLittleEndian, // CLASS_LINUX_AMD64
235 archLittleEndian, // CLASS_FREEBSD_AMD64
236 archLittleEndian, // CLASS_WINDOWS_AMD64
237 archBigEndian, // CLASS_LINUX_PPC
238 archLittleEndian, // CLASS_DARWIN_I386
239 archLittleEndian // CLASS_LINUX_MIPSEL
240 };
241
242 #ifdef sun
243 #ifdef i386
244 const SSHORT CLASS = CLASS_SOLARIS_I386;
245 #else
246 const SSHORT CLASS = CLASS_SOLARIS_SPARC;
247 #endif
248 #endif
249
250 #ifdef hpux
251 const SSHORT CLASS = CLASS_HPUX_PA;
252 #endif
253
254 #ifdef VMS
255 const SSHORT CLASS = CLASS_VMS_VAX;
256 #endif
257
258 #ifdef AIX
259 const SSHORT CLASS = CLASS_AIX_PPC;
260 #endif
261
262 #ifdef AIX_PPC
263 const SSHORT CLASS = CLASS_AIX_PPC;
264 #endif
265
266 #ifdef WIN_NT
267 #if defined(I386)
268 const SSHORT CLASS = CLASS_WINDOWS_I386;
269 #elif defined(AMD64)
270 const SSHORT CLASS = CLASS_WINDOWS_AMD64;
271 #else
272 #error no support on other hardware for Windows
273 #endif
274 #endif // WIN_NT
275
276 #ifdef SINIXZ
277 const SSHORT CLASS = CLASS_LINUX_I386;
278 #endif
279
280 #ifdef LINUX
281 #if defined(i386) || defined(i586)
282 const SSHORT CLASS = CLASS_LINUX_I386;
283 #elif defined(sparc)
284 const SSHORT CLASS = CLASS_LINUX_SPARC;
285 #elif defined(AMD64)
286 const SSHORT CLASS = CLASS_LINUX_AMD64;
287 #elif defined(PPC)
288 const SSHORT CLASS = CLASS_LINUX_PPC;
289 #elif defined(MIPSEL)
290 const SSHORT CLASS = CLASS_LINUX_MIPSEL;
291 #else
292 #error no support on other hardware for Linux
293 #endif
294 #endif // LINUX
295
296 #ifdef FREEBSD
297 #if defined(i386)
298 const SSHORT CLASS = CLASS_FREEBSD_I386;
299 #elif defined(AMD64)
300 const SSHORT CLASS = CLASS_FREEBSD_AMD64;
301 #else
302 #error no support on other hardware for FreeBSD
303 #endif
304 #endif
305
306 #ifdef NETBSD
307 const SSHORT CLASS = CLASS_NETBSD_I386;
308 #endif
309
310 #ifdef DARWIN
311 #ifdef i386
312 const SSHORT CLASS = CLASS_DARWIN_I386;
313 #endif
314 #ifdef powerpc
315 const SSHORT CLASS = CLASS_DARWIN_PPC;
316 #endif
317 #endif
318 static const char* const SCRATCH = "fb_table_";
319
320 // CVC: Since nobody checks the result from this function (strange!), I changed
321 // bool to void as the return type but left the result returned as comment.
322 void PAG_add_clump(
323 SLONG page_num,
324 USHORT type,
325 USHORT len, const UCHAR* entry, USHORT mode, USHORT must_write)
326 {
327 /***********************************************
328 *
329 * P A G _ a d d _ c l u m p
330 *
331 ***********************************************
332 *
333 * Functional description
334 * Adds a clump to log/header page.
335 * mode
336 * 0 - add CLUMP_ADD
337 * 1 - replace CLUMP_REPLACE
338 * 2 - replace only! CLUMP_REPLACE_ONLY
339 * returns
340 * true - modified page
341 * false - nothing done => nobody checks this function's result.
342 *
343 **************************************/
344 thread_db* tdbb = JRD_get_thread_data();
345 Database* dbb = tdbb->tdbb_database;
346 CHECK_DBB(dbb);
347
348 err_post_if_database_is_readonly(dbb);
349
350 pag* page;
351 header_page* header = 0;
352 log_info_page* logp = 0;
353 USHORT* end_addr;
354 WIN window(DB_PAGE_SPACE, page_num);
355 if (page_num == HEADER_PAGE) {
356 page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
357 header = (header_page*) page;
358 end_addr = &header->hdr_end;
359 }
360 else {
361 page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
362 logp = (log_info_page*) page;
363 end_addr = &logp->log_end;
364 }
365
366 UCHAR* entry_p;
367 const UCHAR* clump_end;
368 while (mode != CLUMP_ADD) {
369 const bool found =
370 find_type(page_num, &window, &page, LCK_write, type,
371 &entry_p, &clump_end);
372
373 /* If we did'nt find it and it is REPLACE_ONLY, return */
374
375 if ((!found) && (mode == CLUMP_REPLACE_ONLY)) {
376 CCH_RELEASE(tdbb, &window);
377 return; //false;
378 }
379
380 /* If not found, just go and add the entry */
381
382 if (!found)
383 break;
384
385 /* if same size, overwrite it */
386
387 if (entry_p[1] == len) {
388 entry_p += 2;
389 const UCHAR* r = entry;
390 USHORT l = len;
391 if (l) {
392 if (must_write)
393 CCH_MARK_MUST_WRITE(tdbb, &window);
394 else
395 CCH_MARK(tdbb, &window);
396 do {
397 *entry_p++ = *r++;
398 } while (--l);
399
400 }
401 CCH_RELEASE(tdbb, &window);
402 return; // true;
403 }
404
405 /* delete the entry
406
407 * Page is marked must write because of precedence problems. Later
408 * on we may allocate a new page and set up a precedence relationship.
409 * This may be the lower precedence page and so it cannot be dirty
410 */
411
412 CCH_MARK_MUST_WRITE(tdbb, &window);
413
414 *end_addr -= (2 + entry_p[1]);
415
416 const UCHAR* r = entry_p + 2 + entry_p[1];
417 USHORT l = clump_end - r + 1;
418
419 if (l) {
420 do {
421 *entry_p++ = *r++;
422 } while (--l);
423 }
424
425 CCH_RELEASE(tdbb, &window);
426
427 /* refetch the page */
428
429 window.win_page = page_num;
430 if (page_num == HEADER_PAGE) {
431 page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
432 header = (header_page*) page;
433 end_addr = &header->hdr_end;
434 }
435 else {
436 page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
437 logp = (log_info_page*) page;
438 end_addr = &logp->log_end;
439 }
440 break;
441 }
442
443 /* Add the entry */
444
445 find_clump_space(page_num, &window, &page, type, len, entry, must_write);
446
447 CCH_RELEASE(tdbb, &window);
448 return; // true;
449 }
450
451
452 USHORT PAG_add_file(const TEXT* file_name, SLONG start)
453 {
454 /**************************************
455 *
456 * P A G _ a d d _ f i l e
457 *
458 **************************************
459 *
460 * Functional description
461 * Add a file to the current database. Return the sequence
462 * number for the new file.
463 *
464 **************************************/
465 thread_db* tdbb = JRD_get_thread_data();
466 Database* dbb = tdbb->tdbb_database;
467 CHECK_DBB(dbb);
468
469 err_post_if_database_is_readonly(dbb);
470
471 /* Find current last file */
472
473 PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
474 jrd_file* file = pageSpace->file;
475 while (file->fil_next) {
476 file = file->fil_next;
477 }
478
479 // Verify database file path against DatabaseAccess entry of firebird.conf
480 if (!ISC_verify_database_access(file_name)) {
481 ERR_post(isc_conf_access_denied,
482 isc_arg_string, "additional database file",
483 isc_arg_string, ERR_cstring(file_name),
484 isc_arg_end);
485 }
486
487 /* Create the file. If the sequence number comes back zero, it didn't
488 work, so punt */
489
490 const USHORT sequence = PIO_add_file(dbb, pageSpace->file, file_name, start);
491 if (!sequence)
492 return 0;
493
494 /* Create header page for new file */
495
496 jrd_file* next = file->fil_next;
497
498 if (dbb->dbb_flags & DBB_force_write)
499 PIO_force_write(next, true);
500
501 WIN window(DB_PAGE_SPACE, next->fil_min_page);
502 header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
503 header->hdr_header.pag_type = pag_header;
504 header->hdr_sequence = sequence;
505 header->hdr_page_size = dbb->dbb_page_size;
506 header->hdr_data[0] = HDR_end;
507 header->hdr_end = HDR_SIZE;
508 next->fil_sequence = sequence;
509
510 #ifdef SUPPORT_RAW_DEVICES
511 /* The following lines (taken from PAG_format_header) are needed to identify
512 this file in raw_devices_validate_database as a valid database attachment. */
513 *(ISC_TIMESTAMP*)header->hdr_creation_date = Firebird::TimeStamp().value();
514 // should we include milliseconds or not?
515 //Firebird::TimeStamp::round_time(header->hdr_creation_date->timestamp_time, 0);
516
517 header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG;
518 header->hdr_implementation = CLASS;
519 header->hdr_ods_minor = ODS_CURRENT;
520 header->hdr_ods_minor_original = ODS_CURRENT;
521 if (dbb->dbb_flags & DBB_DB_SQL_dialect_3)
522 header->hdr_flags |= hdr_SQL_dialect_3;
523 #endif
524
525 header->hdr_header.pag_checksum = CCH_checksum(window.win_bdb);
526 PIO_write(pageSpace->file, window.win_bdb, window.win_buffer,
527 tdbb->tdbb_status_vector);
528 CCH_RELEASE(tdbb, &window);
529 next->fil_fudge = 1;
530
531 /* Update the previous header page to point to new file */
532
533 file->fil_fudge = 0;
534 window.win_page = file->fil_min_page;
535 header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
536 if (!file->fil_min_page)
537 CCH_MARK_MUST_WRITE(tdbb, &window);
538 else
539 CCH_MARK(tdbb, &window);
540
541 --start;
542
543 if (file->fil_min_page) {
544 PAG_add_header_entry(header, HDR_file, strlen(file_name),
545 reinterpret_cast<const UCHAR*>(file_name));
546 PAG_add_header_entry(header, HDR_last_page, sizeof(SLONG),
547 (UCHAR *) & start);
548 }
549 else {
550 PAG_add_clump(HEADER_PAGE, HDR_file, strlen(file_name),
551 reinterpret_cast<const UCHAR*>(file_name), CLUMP_REPLACE, 1);
552 PAG_add_clump(HEADER_PAGE, HDR_last_page, sizeof(SLONG),
553 (UCHAR *) & start, CLUMP_REPLACE, 1);
554 }
555
556 header->hdr_header.pag_checksum = CCH_checksum(window.win_bdb);
557 PIO_write(pageSpace->file, window.win_bdb, window.win_buffer,
558 tdbb->tdbb_status_vector);
559 CCH_RELEASE(tdbb, &window);
560 if (file->fil_min_page)
561 file->fil_fudge = 1;
562
563 return sequence;
564 }
565
566
567 int PAG_add_header_entry(header_page* header, USHORT type, SSHORT len, const UCHAR* entry)
568 {
569 /***********************************************
570 *
571 * P A G _ a d d _ h e a d e r _ e n t r y
572 *
573 ***********************************************
574 *
575 * Functional description
576 * Add an entry to header page.
577 * This will be used mainly for the shadow header page and adding
578 * secondary files.
579 * Will not follow to hdr_next_page
580 * Will not journal changes made to page. => obsolete
581 * RETURNS
582 * TRUE - modified page
583 * FALSE - nothing done
584 *
585 **************************************/
586 thread_db* tdbb = JRD_get_thread_data();
587 Database* dbb = tdbb->tdbb_database;
588 CHECK_DBB(dbb);
589
590 err_post_if_database_is_readonly(dbb);
591
592 const UCHAR* q = entry;
593
594 UCHAR* p;
595 for (p = header->hdr_data; ((*p != HDR_end) && (*p != type));
596 p += 2 + p[1]);
597
598 if (*p != HDR_end)
599 return FALSE;
600
601 /* We are at HDR_end, add the entry */
602
603 const int free_space = dbb->dbb_page_size - header->hdr_end;
604
605 if (free_space > (2 + len)) {
606 fb_assert(type <= MAX_UCHAR);
607 fb_assert(len <= MAX_UCHAR);
608 *p++ = static_cast<UCHAR>(type);
609 *p++ = static_cast<UCHAR>(len);
610
611 if (len) {
612 if (q) {
613 do {
614 *p++ = *q++;
615 } while (--len);
616 }
617 else {
618 do {
619 *p++ = 0;
620 } while (--len);
621 }
622 }
623
624 *p = HDR_end;
625
626 header->hdr_end = p - (UCHAR *) header;
627
628 return TRUE;
629 }
630
631 BUGCHECK(251);
632 return FALSE; /* Added to remove compiler warning */
633 }
634
635
636 void PAG_attach_temp_pages(thread_db* tdbb, USHORT pageSpaceID)
637 {
638 /***********************************************
639 *
640 * P A G _ a t t a c h _ t e m p _ p a g e s
641 *
642 ***********************************************
643 *
644 * Functional description
645 * Attach a temporary page space
646 *
647 **************************************/
648 SET_TDBB(tdbb);
649 Database* dbb = tdbb->tdbb_database;
650 CHECK_DBB(dbb);
651
652 PageSpace* pageSpaceTemp = dbb->dbb_page_manager.addPageSpace(pageSpaceID);
653 if (!pageSpaceTemp->file)
654 {
655 Firebird::PathName file_name = TempFile::create(SCRATCH);
656 pageSpaceTemp->file = PIO_create(dbb, file_name, true, true, false);
657 PAG_format_pip(tdbb, *pageSpaceTemp);
658 }
659 }
660
661
662 int PAG_replace_entry_first(header_page* header, USHORT type, SSHORT len, const UCHAR* entry)
663 {
664 /***********************************************
665 *
666 * P A G _ r e p l a c e _ e n t r y _ f i r s t
667 *
668 ***********************************************
669 *
670 * Functional description
671 * Replace an entry in the header page so it will become first entry
672 * This will be used mainly for the clumplets used for backup purposes
673 * because they are needed to be read without page lock
674 * Will not follow to hdr_next_page
675 * Will not journal changes made to page. => obsolete
676 * RETURNS
677 * TRUE - modified page
678 * FALSE - nothing done
679 *
680 **************************************/
681 thread_db* tdbb = JRD_get_thread_data();
682 Database* dbb = tdbb->tdbb_database;
683 CHECK_DBB(dbb);
684
685 err_post_if_database_is_readonly(dbb);
686
687 const UCHAR* q = entry;
688
689 UCHAR* p = header->hdr_data;
690 while ((*p != HDR_end) && (*p != type)) {
691 p += 2 + p[1];
692 }
693
694 // Remove item if found it somewhere
695 if (*p != HDR_end) {
696 UCHAR l = p[1] + 2;
697 memmove(p, p + l,
698 header->hdr_end - (p - (UCHAR*)header) - l + 1/*to preserve HDR_end*/);
699 header->hdr_end -= l;
700 }
701
702
703 if (!entry) {
704 return FALSE; // We were asked just to remove item. We finished.
705 }
706
707 // Check if we got enough space
708 if (dbb->dbb_page_size - header->hdr_end <= len + 2) {
709 BUGCHECK(251);
710 }
711
712 // Actually add the item
713 memmove(header->hdr_data + len + 2, header->hdr_data, header->hdr_end - HDR_SIZE + 1);
714 header->hdr_data[0] = type;
715 header->hdr_data[1] = len;
716 memcpy(header->hdr_data + 2, entry, len);
717 header->hdr_end += len + 2;
718
719 return TRUE;
720 }
721
722 PAG PAG_allocate(WIN * window)
723 {
724 /**************************************
725 *
726 * P A G _ a l l o c a t e
727 *
728 **************************************
729 *
730 * Functional description
731 * Allocate a page and fake a read with a write lock. This is
732 * the universal sequence when allocating pages.
733 *
734 **************************************/
735 thread_db* tdbb = JRD_get_thread_data();
736 Database* dbb = tdbb->tdbb_database;
737 CHECK_DBB(dbb);
738
739 PageManager& pageMgr = dbb->dbb_page_manager;
740 PageSpace* pageSpace = pageMgr.findPageSpace(
741 window->win_page.getPageSpaceID());
742 fb_assert(pageSpace);
743
744 // Not sure if this can be moved inside the loop. Maybe some data members
745 // should persist across iterations?
746 WIN pip_window(pageSpace->pageSpaceID, -1);
747 // CVC: Not sure of the initial value. Notice bytes and bit are used after the loop.
748 UCHAR* bytes = 0;
749 UCHAR bit = 0;
750
751 pag* new_page = 0; // NULL before the search for a new page.
752
753 /* Find an allocation page with something on it */
754
755 SLONG relative_bit = -1;
756 SLONG sequence;
757 for (sequence = pageSpace->pipHighWater;; sequence++) {
758 pip_window.win_page = (sequence == 0) ?
759 pageSpace->ppFirst : sequence * dbb->dbb_page_manager.pagesPerPIP - 1;
760 page_inv_page* pip_page =
761 (page_inv_page*) CCH_FETCH(tdbb, &pip_window, LCK_write, pag_pages);
762 const UCHAR* end = (UCHAR*) pip_page + dbb->dbb_page_size;
763 for (bytes = &pip_page->pip_bits[pip_page->pip_min >> 3]; bytes < end;
764 bytes++)
765 {
766 if (*bytes != 0) {
767 /* 'byte' is not zero, so it describes at least one free page. */
768 bit = 1;
769 for (SLONG i = 0; i < 8; i++, bit <<= 1) {
770 if (bit & *bytes) {
771 relative_bit =
772 ((bytes - pip_page->pip_bits) << 3) + i;
773 window->win_page =
774 relative_bit + sequence * pageMgr.pagesPerPIP;
775 new_page = CCH_fake(tdbb, window, 0); /* don't wait on latch */
776 if (new_page)
777 break; /* Found a page and successfully fake-ed it */
778 }
779 }
780 }
781 if (new_page)
782 break; /* Found a page and successfully fake-ed it */
783 }
784 if (new_page)
785 break; /* Found a page and successfully fake-ed it */
786 CCH_RELEASE(tdbb, &pip_window);
787 }
788
789 pageSpace->pipHighWater = sequence;
790
791 CCH_MARK(tdbb, &pip_window);
792 *bytes &= ~bit;
793
794 if (relative_bit != pageMgr.pagesPerPIP - 1) {
795 CCH_RELEASE(tdbb, &pip_window);
796 CCH_precedence(tdbb, window, pip_window.win_page);
797 #ifdef VIO_DEBUG
798 if (debug_flag > DEBUG_WRITES_INFO)
799 printf("\tPAG_allocate: allocated page %"SLONGFORMAT"\n",
800 window->win_page);
801 #endif
802 return new_page;
803 }
804
805 /* We've allocated the last page on the space management page. Rather
806 than returning it, format it as a page inventory page, and recurse. */
807
808 page_inv_page* new_pip_page = (page_inv_page*) new_page;
809 new_pip_page->pip_header.pag_type = pag_pages;
810 // CVC: If some tips on web sites are true, this can be improved by
811 // a pointer to ULONG setting memory to 0xffffffff.
812 const UCHAR* end = (UCHAR *) new_pip_page + dbb->dbb_page_size;
813 for (bytes = new_pip_page->pip_bits; bytes < end;)
814 *bytes++ = 0xff;
815
816 CCH_must_write(window);
817 CCH_RELEASE(tdbb, window);
818 CCH_must_write(&pip_window);
819 CCH_RELEASE(tdbb, &pip_window);
820
821 return PAG_allocate(window);
822 }
823
824
825 SLONG PAG_attachment_id(thread_db* tdbb)
826 {
827 /******************************************
828 *
829 * P A G _ a t t a c h m e n t _ i d
830 *
831 ******************************************
832 *
833 * Functional description
834 * Get attachment id. If don't have one, get one. As a side
835 * effect, get a lock on it as well.
836 *
837 ******************************************/
838 SET_TDBB(tdbb);
839 Database* dbb = tdbb->tdbb_database;
840
841 Attachment* attachment = tdbb->tdbb_attachment;
842 WIN window(DB_PAGE_SPACE, -1);
843
844 /* If we've been here before just return the id */
845
846 if (attachment->att_id_lock)
847 return attachment->att_attachment_id;
848
849 /* Get new attachment id */
850
851 if (dbb->dbb_flags & DBB_read_only) {
852 attachment->att_attachment_id = ++dbb->dbb_attachment_id;
853 }
854 else {
855 window.win_page = HEADER_PAGE_NUMBER;
856 header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
857 CCH_MARK(tdbb, &window);
858 attachment->att_attachment_id = ++header->hdr_attachment_id;
859
860 CCH_RELEASE(tdbb, &window);
861 }
862
863 /* Take out lock on attachment id */
864
865 Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(SLONG)) Lock();
866 attachment->att_id_lock = lock;
867 lock->lck_type = LCK_attachment;
868 lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
869 lock->lck_parent = dbb->dbb_lock;
870 lock->lck_length = sizeof(SLONG);
871 lock->lck_key.lck_long = attachment->att_attachment_id;
872 lock->lck_dbb = dbb;
873 LCK_lock(tdbb, lock, LCK_write, LCK_WAIT);
874
875 return attachment->att_attachment_id;
876 }
877
878
879 int PAG_delete_clump_entry(SLONG page_num, USHORT type)
880 {
881 /***********************************************
882 *
883 * P A G _ d e l e t e _ c l u m p _ e n t r y
884 *
885 ***********************************************
886 *
887 * Functional description
888 * Gets rid on the entry 'type' from page.
889 *
890 **************************************/
891 thread_db* tdbb = JRD_get_thread_data();
892 Database* dbb = tdbb->tdbb_database;
893 CHECK_DBB(dbb);
894
895 err_post_if_database_is_readonly(dbb);
896
897 WIN window(DB_PAGE_SPACE, page_num);
898
899 pag* page;
900 if (page_num == HEADER_PAGE)
901 page = CCH_FETCH(tdbb, &window, LCK_write, pag_header);
902 else
903 page = CCH_FETCH(tdbb, &window, LCK_write, pag_log);
904
905 UCHAR* entry_p;
906 const UCHAR* clump_end;
907 if (!find_type
908 (page_num, &window, &page, LCK_write, type, &entry_p, &clump_end))
909 {
910 CCH_RELEASE(tdbb, &window);
911 return FALSE;
912 }
913 CCH_MARK(tdbb, &window);
914
915 header_page* header = 0;
916 log_info_page* logp = 0;
917 USHORT* end_addr;
918 if (page_num == HEADER_PAGE) {
919 header = (header_page*) page;
920 end_addr = &header->hdr_end;
921 }
922 else {
923 logp = (log_info_page*) page;
924 end_addr = &logp->log_end;
925 }
926
927 *end_addr -= (2 + entry_p[1]);
928
929 const UCHAR* r = entry_p + 2 + entry_p[1];
930 USHORT l = clump_end - r + 1;
931
932 if (l) {
933 do {
934 *entry_p++ = *r++;
935 } while (--l);
936 }
937
938 CCH_RELEASE(tdbb, &window);
939
940 return TRUE;
941 }
942
943
944 void PAG_format_header()
945 {
946 /**************************************
947 *
948 * P A G _ f o r m a t _ h e a d e r
949 *
950 **************************************
951 *
952 * Functional description
953 * Create the header page for a new file.
954 *
955 **************************************/
956 thread_db* tdbb = JRD_get_thread_data();
957 Database* dbb = tdbb->tdbb_database;
958 CHECK_DBB(dbb);
959
960 /* Initialize header page */
961
962 WIN window(HEADER_PAGE_NUMBER);
963 header_page* header = (header_page*) CCH_fake(tdbb, &window, 1);
964 header->hdr_header.pag_scn = 0;
965 *(ISC_TIMESTAMP*)header->hdr_creation_date = Firebird::TimeStamp().value();
966 // should we include milliseconds or not?
967 //Firebird::TimeStamp::round_time(header->hdr_creation_date->timestamp_time, 0);
968 header->hdr_header.pag_type = pag_header;
969 header->hdr_page_size = dbb->dbb_page_size;
970 header->hdr_ods_version = ODS_VERSION | ODS_FIREBIRD_FLAG;
971 header->hdr_implementation = CLASS;
972 header->hdr_ods_minor = ODS_CURRENT;
973 header->hdr_ods_minor_original = ODS_CURRENT;
974 header->hdr_oldest_transaction = 1;
975 header->hdr_bumped_transaction = 1;
976 header->hdr_end = HDR_SIZE;
977 header->hdr_data[0] = HDR_end;
978 #ifdef SYNC_WRITE_DEFAULT
979 header->hdr_flags |= hdr_force_write;
980 #endif
981
982 if (dbb->dbb_flags & DBB_DB_SQL_dialect_3) {
983 header->hdr_flags |= hdr_SQL_dialect_3;
984 }
985
986 dbb->dbb_ods_version = header->hdr_ods_version & ~ODS_FIREBIRD_FLAG;
987 dbb->dbb_minor_version = header->hdr_ods_minor;
988 dbb->dbb_minor_original = header->hdr_ods_minor_original;
989
990 CCH_RELEASE(tdbb, &window);
991 }
992
993
994 // CVC: This function is mostly obsolete. Ann requested to keep it and the code that calls it.
995 // We won't read the log, anyway.
996 void PAG_format_log()
997 {
998 /***********************************************
999 *
1000 * P A G _ f o r m a t _ l o g
1001 *
1002 ***********************************************
1003 *
1004 * Functional description
1005 * Initialize log page.
1006 * Set all parameters to 0
1007 *
1008 **************************************/
1009 thread_db* tdbb = JRD_get_thread_data();
1010
1011 WIN window(LOG_PAGE_NUMBER);
1012 log_info_page* logp = (log_info_page*) CCH_fake(tdbb, &window, 1);
1013 logp->log_header.pag_type = pag_log;
1014
1015 CCH_RELEASE(tdbb, &window);
1016 }
1017
1018
1019 void PAG_format_pip(thread_db* tdbb, PageSpace& pageSpace)
1020 {
1021 /**************************************
1022 *
1023 * P A G _ f o r m a t _ p i p
1024 *
1025 **************************************
1026 *
1027 * Functional description
1028 * Create a page inventory page to
1029 * complete the formatting of a new file
1030 * into a rudimentary database.
1031 *
1032 **************************************/
1033 SET_TDBB(tdbb);
1034 Database* dbb = tdbb->tdbb_database;
1035 CHECK_DBB(dbb);
1036
1037 /* Initialize Page Inventory Page */
1038
1039 WIN window(pageSpace.pageSpaceID, 1);
1040 pageSpace.ppFirst = 1;
1041 page_inv_page* pages = (page_inv_page*) CCH_fake(tdbb, &window, 1);
1042
1043 pages->pip_header.pag_type = pag_pages;
1044 pages->pip_min = 4;
1045 UCHAR* p = pages->pip_bits;
1046 int i = dbb->dbb_page_size - OFFSETA(page_inv_page*, pip_bits);
1047
1048 while (i--) {
1049 *p++ = 0xff;
1050 }
1051
1052 pages->pip_bits[0] &= ~(1 | 2 | 4);
1053
1054 CCH_RELEASE(tdbb, &window);
1055 }
1056
1057
1058 bool PAG_get_clump(SLONG page_num, USHORT type, USHORT* len, UCHAR* entry)
1059 {
1060 /***********************************************
1061 *
1062 * P A G _ g e t _ c l u m p
1063 *
1064 ***********************************************
1065 *
1066 * Functional description
1067 * Find 'type' clump in page_num
1068 * true - Found it
1069 * false - Not present
1070 * RETURNS
1071 * value of clump in entry
1072 * length in len
1073 *
1074 **************************************/
1075 thread_db* tdbb = JRD_get_thread_data();
1076
1077 *len = 0;
1078 WIN window(DB_PAGE_SPACE, page_num);
1079
1080 pag* page;
1081 if (page_num == HEADER_PAGE)
1082 page = CCH_FETCH(tdbb, &window, LCK_read, pag_header);
1083 else
1084 page = CCH_FETCH(tdbb, &window, LCK_read, pag_log);
1085
1086 UCHAR* entry_p;
1087 const UCHAR* dummy;
1088 if (!find_type(page_num, &window, &page, LCK_read, type, &entry_p, &dummy)) {
1089 CCH_RELEASE(tdbb, &window);
1090 return false;
1091 }
1092
1093 USHORT l = entry_p[1];
1094 *len = l;
1095 entry_p += 2;
1096
1097 UCHAR* q = entry;
1098 if (l) {
1099 do {
1100 *q++ = *entry_p++;
1101 } while (--l);
1102 }
1103
1104 CCH_RELEASE(tdbb, &window);
1105
1106 return true;
1107 }
1108
1109
1110 void PAG_header(bool info)
1111 {
1112 /**************************************
1113 *
1114 * P A G _ h e a d e r
1115 *
1116 **************************************
1117 *
1118 * Functional description
1119 * Checkout database header page.
1120 * Done through the page cache.
1121 *
1122 **************************************/
1123 thread_db* tdbb = JRD_get_thread_data();
1124 Database* dbb = tdbb->tdbb_database;
1125
1126 Attachment* attachment = tdbb->tdbb_attachment;
1127 fb_assert(attachment);
1128
1129 WIN window(HEADER_PAGE_NUMBER);
1130 header_page* header =
1131 (header_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_header);
1132
1133 try {
1134
1135 if (header->hdr_next_transaction) {
1136 if (header->hdr_oldest_active > header->hdr_next_transaction)
1137 BUGCHECK(266); /*next transaction older than oldest active */
1138
1139 if (header->hdr_oldest_transaction > header->hdr_next_transaction)
1140 BUGCHECK(267); /* next transaction older than oldest transaction */
1141 }
1142
1143 if (header->hdr_flags & hdr_SQL_dialect_3)
1144 dbb->dbb_flags |= DBB_DB_SQL_dialect_3;
1145
1146 jrd_rel* relation = MET_relation(tdbb, 0);
1147 RelationPages* relPages = relation->getBasePages();
1148 if (!relPages->rel_pages) {
1149 // 21-Dec-2003 Nickolay Samofatov
1150 // No need to re-set first page for RDB$PAGES relation since
1151 // current code cannot change its location after database creation.
1152 // Currently, this change only affects isc_database_info call,
1153 // the only call which may call PAG_header multiple times.
1154 // In fact, this isc_database_info behavior seems dangerous to me,
1155 // but let somebody else fix that problem, I just fix the memory leak.
1156 vcl* vector = vcl::newVector(*dbb->dbb_permanent, 1);
1157 relPages->rel_pages = vector;
1158 (*vector)[0] = header->hdr_PAGES;
1159 }
1160
1161 dbb->dbb_next_transaction = header->hdr_next_transaction;
1162
1163 if (!info || dbb->dbb_oldest_transaction < header->hdr_oldest_transaction) {
1164 dbb->dbb_oldest_transaction = header->hdr_oldest_transaction;
1165 }
1166 if (!info || dbb->dbb_oldest_active < header->hdr_oldest_active) {
1167 dbb->dbb_oldest_active = header->hdr_oldest_active;
1168 }
1169 if (!info || dbb->dbb_oldest_snapshot < header->hdr_oldest_snapshot) {
1170 dbb->dbb_oldest_snapshot = header->hdr_oldest_snapshot;
1171 }
1172
1173 dbb->dbb_attachment_id = header->hdr_attachment_id;
1174 dbb->dbb_creation_date = *(ISC_TIMESTAMP*) header->hdr_creation_date;
1175
1176 if (header->hdr_flags & hdr_read_only) {
1177 /* If Header Page flag says the database is ReadOnly, gladly accept it. */
1178 dbb->dbb_flags &= ~DBB_being_opened_read_only;
1179 dbb->dbb_flags |= DBB_read_only;
1180 }
1181
1182 /* If hdr_read_only is not set... */
1183 if (!(header->hdr_flags & hdr_read_only)
1184 && (dbb->dbb_flags & DBB_being_opened_read_only))
1185 {
1186 /* Looks like the Header page says, it is NOT ReadOnly!! But the database
1187 * file system permission gives only ReadOnly access. Punt out with
1188 * isc_no_priv error (no privileges)
1189 */
1190 ERR_post(isc_no_priv,
1191 isc_arg_string, "read-write",
1192 isc_arg_string, "database",
1193 isc_arg_string, ERR_string(attachment->att_filename),
1194 0);
1195 }
1196
1197 if (header->hdr_flags & hdr_force_write) {
1198 dbb->dbb_flags |= DBB_force_write;
1199
1200 PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
1201 if (!(header->hdr_flags & hdr_read_only))
1202 PIO_force_write(pageSpace->file, true);
1203 }
1204
1205 if (header->hdr_flags & hdr_no_reserve)
1206 dbb->dbb_flags |= DBB_no_reserve;
1207
1208 const USHORT sd_flags = header->hdr_flags & hdr_shutdown_mask;
1209 if (sd_flags) {
1210 dbb->dbb_ast_flags |= DBB_shutdown;
1211 if (sd_flags == hdr_shutdown_full)
1212 dbb->dbb_ast_flags |= DBB_shutdown_full;
1213 else if (sd_flags == hdr_shutdown_single)
1214 dbb->dbb_ast_flags |= DBB_shutdown_single;
1215 }
1216
1217 } // try
1218 catch (const Firebird::Exception&) {
1219 CCH_RELEASE(tdbb, &window);
1220 throw;
1221 }
1222
1223 CCH_RELEASE(tdbb, &window);
1224 }
1225
1226
1227 void PAG_header_init()
1228 {
1229 /**************************************
1230 *
1231 * P A G _ h e a d e r _ i n i t
1232 *
1233 **************************************
1234 *
1235 * Functional description
1236 * Checkout the core part of the database header page.
1237 * It includes the fields required to setup the I/O layer:
1238 * ODS version, page size, page buffers.
1239 * Done using a physical page read.
1240 *
1241 **************************************/
1242 thread_db* tdbb = JRD_get_thread_data();
1243 Database* dbb = tdbb->tdbb_database;
1244
1245 Attachment* attachment = tdbb->tdbb_attachment;
1246 fb_assert(attachment);
1247
1248 // Allocate a spare buffer which is large enough,
1249 // and set up to release it in case of error; note
1250 // that dbb_page_size has not been set yet, so we
1251 // can't depend on this.
1252 //
1253 // Make sure that buffer is aligned on a page boundary
1254 // and unit of transfer is a multiple of physical disk
1255 // sector for raw disk access.
1256
1257 SCHAR temp_buffer[2 * MIN_PAGE_SIZE];
1258 SCHAR* temp_page = (SCHAR *) FB_ALIGN((IPTR) temp_buffer, MIN_PAGE_SIZE);
1259
1260 header_page* header = (header_page*) temp_page;
1261 PIO_header(dbb, temp_page, MIN_PAGE_SIZE);
1262
1263 if (header->hdr_header.pag_type != pag_header || header->hdr_sequence) {
1264 ERR_post(isc_bad_db_format,
1265 isc_arg_string, ERR_string(attachment->att_filename),
1266 0);
1267 }
1268
1269 const USHORT ods_version = header->hdr_ods_version & ~ODS_FIREBIRD_FLAG;
1270
1271 if (!Ods::isSupported(header->hdr_ods_version, header->hdr_ods_minor))
1272 {
1273 ERR_post(isc_wrong_ods,
1274 isc_arg_string, ERR_string(attachment->att_filename),
1275 isc_arg_number, (SLONG) ods_version,
1276 isc_arg_number, (SLONG) header->hdr_ods_minor,
1277 isc_arg_number, (SLONG) ODS_VERSION,
1278 isc_arg_number, (SLONG) ODS_CURRENT,
1279 0);
1280 }
1281
1282 // Note that if this check is turned on, it should be recoded in order that
1283 // the Intel platforms can share databases. At present (Feb 95) it is possible
1284 // to share databases between Windows and NT, but not with NetWare. Sharing
1285 // databases with OS/2 is unknown and needs to be investigated. The CLASS was
1286 // initially 8 for all Intel platforms, but was changed after 4.0 was released
1287 // in order to allow differentiation between databases created on various
1288 // platforms. This should allow us in future to identify where databases were
1289 // created. Even when we get to the stage where databases created on PC platforms
1290 // are sharable between all platforms, it would be useful to identify where they
1291 // were created for debugging purposes. - Deej 2/6/95
1292 //
1293 // Re-enable and recode the check to avoid BUGCHECK messages when database
1294 // is accessed with engine built for another architecture. - Nickolay 9-Feb-2005
1295
1296 if (header->hdr_implementation != CLASS &&
1297 ods_version < ODS_VERSION11 ?
1298 (header->hdr_implementation < 0 || header->hdr_implementation > CLASS_MAX10 ||
1299 archMatrix10[header->hdr_implementation] == archUnknown ||
1300 archMatrix10[header->hdr_implementation] != archMatrix10[CLASS])
1301 :
1302 (header->hdr_implementation < 0 || header->hdr_implementation > CLASS_MAX ||
1303 archMatrix[header->hdr_implementation] == archUnknown ||
1304 archMatrix[header->hdr_implementation] != archMatrix[CLASS])
1305 )
1306 {
1307 ERR_post(isc_bad_db_format,
1308 isc_arg_string, ERR_string(attachment->att_filename),
1309 0);
1310 }
1311
1312 if (header->hdr_page_size < MIN_PAGE_SIZE ||
1313 header->hdr_page_size > MAX_PAGE_SIZE)
1314 {
1315 ERR_post(isc_bad_db_format,
1316 isc_arg_string, ERR_string(attachment->att_filename),
1317 0);
1318 }
1319
1320 dbb->dbb_ods_version = ods_version;
1321 dbb->dbb_minor_version = header->hdr_ods_minor;
1322 dbb->dbb_minor_original = header->hdr_ods_minor_original;
1323
1324 dbb->dbb_page_size = header->hdr_page_size;
1325 dbb->dbb_page_buffers = header->hdr_page_buffers;
1326 }
1327
1328
1329 void PAG_init()
1330 {
1331 /**************************************
1332 *
1333 * P A G _ i n i t
1334 *
1335 **************************************
1336 *
1337 * Functional description
1338 * Initialize stuff for page handling.
1339 *
1340 **************************************/
1341 thread_db* tdbb = JRD_get_thread_data();
1342 Database* dbb = tdbb->tdbb_database;
1343 CHECK_DBB(dbb);
1344
1345 PageManager& pageMgr = dbb->dbb_page_manager;
1346 PageSpace* pageSpace = pageMgr.findPageSpace(DB_PAGE_SPACE);
1347 fb_assert(pageSpace);
1348
1349 pageMgr.bytesBitPIP = dbb->dbb_page_size - OFFSETA(page_inv_page*, pip_bits);
1350 pageMgr.pagesPerPIP = pageMgr.bytesBitPIP * 8;
1351 pageMgr.transPerTIP =
1352 (dbb->dbb_page_size - OFFSETA(tx_inv_page*, tip_transactions)) * 4;
1353 pageSpace->ppFirst = 1;
1354 /* dbb_ods_version can be 0 when a new database is being created */
1355 if ((dbb->dbb_ods_version == 0)
1356 || (dbb->dbb_ods_version >= ODS_VERSION10))
1357 {
1358 pageMgr.gensPerPage =
1359 (dbb->dbb_page_size -
1360 OFFSETA(generator_page*, gpg_values)) / sizeof(((generator_page*) NULL)->gpg_values);
1361 }
1362 else {
1363 pageMgr.gensPerPage =
1364 (dbb->dbb_page_size -
1365 OFFSETA(pointer_page*, ppg_page)) / sizeof(((pointer_page*) NULL)->ppg_page);
1366 }
1367
1368
1369 /* Compute the number of data pages per pointer page. Each data page
1370 requires a 32 bit pointer and a 2 bit control field. */
1371
1372 dbb->dbb_dp_per_pp = (dbb->dbb_page_size - OFFSETA(pointer_page*, ppg_page)) * 8 /
1373 (BITS_PER_LONG + 2);
1374
1375 /* Compute the number of records that can fit on a page using the
1376 size of the record index (dpb_repeat) and a record header. This
1377 gives an artificially high number, reducing the density of db_keys. */
1378
1379 dbb->dbb_max_records = (dbb->dbb_page_size - sizeof(data_page)) /
1380 (sizeof(data_page::dpg_repeat) + OFFSETA(rhd*, rhd_data));
1381
1382 // Artifically reduce density of records to test high bits of record number
1383 // dbb->dbb_max_records = 32000;
1384
1385 /* Optimize record numbers for new 64-bit sparse bitmap implementation
1386 We need to measure if it is beneficial from performance point of view.
1387 Price is slightly reduced density of record numbers, but for
1388 ODS11 it doesn't matter because record numbers are 40-bit.
1389 Benefit is ~1.5 times smaller sparse bitmaps on average and
1390 faster bitmap iteration. */
1391 // if (dbb->dbb_ods_version >= ODS_VERSION11)
1392 // dbb->dbb_max_records = FB_ALIGN(dbb->dbb_max_records, 64);
1393
1394 /* Compute the number of index roots that will fit on an index root page,
1395 assuming that each index has only one key */
1396
1397 dbb->dbb_max_idx = (dbb->dbb_page_size - OFFSETA(index_root_page*, irt_rpt)) /
1398 (sizeof(index_root_page::irt_repeat) +
1399 (1 * (dbb->dbb_ods_version >= ODS_VERSION11) ?
1400 sizeof(irtd) : sizeof(irtd_ods10)));
1401
1402 /* Compute prefetch constants from database page size and maximum prefetch
1403 transfer size. Double pages per prefetch request so that cache reader
1404 can overlap prefetch I/O with database computation over previously
1405 prefetched pages. */
1406 #ifdef SUPERSERVER_V2
1407 dbb->dbb_prefetch_sequence = PREFETCH_MAX_TRANSFER / dbb->dbb_page_size;
1408 dbb->dbb_prefetch_pages = dbb->dbb_prefetch_sequence * 2;
1409 #endif
1410 }
1411
1412
1413 void PAG_init2(USHORT shadow_number)
1414 {
1415 /**************************************
1416 *
1417 * P A G _ i n i t 2
1418 *
1419 **************************************
1420 *
1421 * Functional description
1422 * Perform second phase of page initialization -- the eternal
1423 * search for additional files.
1424 *
1425 **************************************/
1426 thread_db* tdbb = JRD_get_thread_data();
1427 Database* dbb = tdbb->tdbb_database;
1428 ISC_STATUS* status = tdbb->tdbb_status_vector;
1429
1430 /* allocate a spare buffer which is large enough,
1431 and set up to release it in case of error. Align
1432 the temporary page buffer for raw disk access. */
1433
1434 SCHAR* const temp_buffer = FB_NEW(*getDefaultMemoryPool()) SCHAR[dbb->dbb_page_size + MIN_PAGE_SIZE];
1435 SCHAR* temp_page =
1436 (SCHAR *) (((U_IPTR) temp_buffer + MIN_PAGE_SIZE - 1) &
1437 ~((U_IPTR) MIN_PAGE_SIZE - 1));
1438
1439 try {
1440
1441 PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
1442 jrd_file* file = pageSpace->file;
1443 if (shadow_number) {
1444 Shadow* shadow = dbb->dbb_shadow;
1445 for (; shadow; shadow = shadow->sdw_next) {
1446 if (shadow->sdw_number == shadow_number) {
1447 file = shadow->sdw_file;
1448 break;
1449 }
1450 }
1451 if (!shadow)
1452 BUGCHECK(161); /* msg 161 shadow block not found */
1453 }
1454
1455 USHORT sequence = 1;
1456 WIN window(DB_PAGE_SPACE, -1);
1457
1458 TEXT buf[MAXPATHLEN + 1];
1459
1460 /* Loop thru files and header pages until everything is open */
1461
1462 for (;;)
1463 {
1464 TEXT* file_name = NULL;
1465 window.win_page = file->fil_min_page;
1466 USHORT file_length = 0;
1467 ULONG last_page = 0;
1468 BufferDesc temp_bdb;
1469 SLONG next_page = 0;
1470 do {
1471 /* note that we do not have to get a read lock on
1472 the header page (except for header page 0) because
1473 the only time it will be modified is when adding a file,
1474 which must be done with an exclusive lock on the database --
1475 if this changes, this policy will have to be reevaluated;
1476 at any rate there is a problem with getting a read lock
1477 because the corresponding page in the main database file
1478 may not exist */
1479
1480 if (!file->fil_min_page)
1481 CCH_FETCH(tdbb, &window, LCK_read, pag_header);
1482
1483 header_page* header = (header_page*) temp_page;
1484 temp_bdb.bdb_buffer = (PAG) header;
1485 temp_bdb.bdb_page = window.win_page;
1486 temp_bdb.bdb_dbb = dbb;
1487
1488 /* Read the required page into the local buffer */
1489 PIO_read(file, &temp_bdb, (PAG) header, status);
1490
1491 if ((shadow_number) && (!file->fil_min_page))
1492 CCH_RELEASE(tdbb, &window);
1493
1494 for (const TEXT* p = reinterpret_cast<TEXT*>(header->hdr_data);
1495 *p != HDR_end;
1496 p += 2 + p[1])
1497 {
1498 switch (*p) {
1499 case HDR_file:
1500 file_length = p[1];
1501 file_name = buf;
1502 MOVE_FAST(p + 2, buf, file_length);
1503 break;
1504
1505 case HDR_last_page:
1506 MOVE_FAST(p + 2, &last_page, sizeof(last_page));
1507 break;
1508
1509 case HDR_sweep_interval:
1510 // CVC: Let's copy it always.
1511 //if (!(dbb->dbb_flags & DBB_read_only))
1512 MOVE_FAST(p + 2, &dbb->dbb_sweep_interval,
1513 sizeof(SLONG));
1514 break;
1515 }
1516 }
1517
1518 next_page = header->hdr_next_page;
1519
1520 if ((!shadow_number) && (!file->fil_min_page))
1521 CCH_RELEASE(tdbb, &window);
1522
1523 window.win_page = next_page;
1524
1525 /*
1526 * Make sure the header page and all the overflow header
1527 * pages are traversed. For V4.0, only the header page for
1528 * the primary database page will have overflow pages.
1529 */
1530
1531 } while (next_page);
1532
1533 if (file->fil_min_page)
1534 file->fil_fudge = 1;
1535 if (!file_name)
1536 break;
1537
1538 // Verify database file path against DatabaseAccess entry of firebird.conf
1539 file_name[file_length] = 0;
1540 if (!ISC_verify_database_access(file_name)) {
1541 ERR_post(isc_conf_access_denied,
1542 isc_arg_string, "additional database file",
1543 isc_arg_string, ERR_cstring(file_name),
1544 isc_arg_end);
1545 }
1546
1547 file->fil_next = PIO_open(dbb, file_name, false, file_name, false);
1548 file->fil_max_page = last_page;
1549 file = file->fil_next;
1550 if (dbb->dbb_flags & DBB_force_write)
1551 PIO_force_write(file, true);
1552 file->fil_min_page = last_page + 1;
1553 file->fil_sequence = sequence++;
1554 }
1555
1556 if (temp_buffer)
1557 delete[] temp_buffer;
1558 } // try
1559 catch (const Firebird::Exception&) {
1560 if (temp_buffer)
1561 delete[] temp_buffer;
1562 throw;
1563 }
1564 }
1565
1566
1567 SLONG PAG_last_page()
1568 {
1569 /**************************************
1570 *
1571 * P A G _ l a s t _ p a g e
1572 *
1573 **************************************
1574 *
1575 * Functional description
1576 * Compute the highest page allocated. This is called by the
1577 * shadow stuff to dump a database.
1578 *
1579 **************************************/
1580 thread_db* tdbb = JRD_get_thread_data();
1581 Database* dbb = tdbb->tdbb_database;
1582 CHECK_DBB(dbb);
1583
1584 PageManager& pageMgr = dbb->dbb_page_manager;
1585 PageSpace* pageSpace = pageMgr.findPageSpace(DB_PAGE_SPACE);
1586 fb_assert(pageSpace);
1587
1588 const ULONG pages_per_pip = pageMgr.pagesPerPIP;
1589 WIN window(DB_PAGE_SPACE, -1);
1590
1591 /* Find the last page allocated */
1592
1593 ULONG relative_bit = 0;
1594 USHORT sequence;
1595 for (sequence = 0;; ++sequence) {
1596 window.win_page =
1597 (!sequence) ? pageSpace->ppFirst : sequence *
1598 pages_per_pip - 1;
1599 page_inv_page* page = (page_inv_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_pages);
1600 UCHAR* bits;
1601 for (bits = page->pip_bits + (pages_per_pip >> 3) - 1;
1602 *bits == (UCHAR) - 1; --bits); // null loop body???
1603 SSHORT bit;
1604 for (bit = 7; bit >= 0; --bit) {
1605 if (!(*bits & (1 << bit)))
1606 break;
1607 }
1608 relative_bit = (bits - page->pip_bits) * 8 + bit;
1609 CCH_RELEASE(tdbb, &window);
1610 if (relative_bit != pages_per_pip - 1)
1611 break;
1612 }
1613
1614 return sequence * pages_per_pip + relative_bit;
1615 }
1616
1617
1618 void PAG_release_page(const PageNumber& number, const PageNumber& prior_page)
1619 {
1620 /**************************************
1621 *
1622 * P A G _ r e l e a s e _ p a g e
1623 *
1624 **************************************
1625 *
1626 * Functional description
1627 * Release a page to the free page page.
1628 *
1629 **************************************/
1630 thread_db* tdbb = JRD_get_thread_data();
1631 Database* dbb = tdbb->tdbb_database;
1632 CHECK_DBB(dbb);
1633
1634 #ifdef VIO_DEBUG
1635 if (debug_flag > DEBUG_WRITES_INFO)
1636 printf("\tPAG_release_page: about to release page %"SLONGFORMAT"\n", number.getPageNumber());
1637 #endif
1638
1639 PageManager& pageMgr = dbb->dbb_page_manager;
1640 PageSpace* pageSpace = pageMgr.findPageSpace(number.getPageSpaceID());
1641 fb_assert(pageSpace);
1642
1643 const SLONG sequence = number.getPageNum() / pageMgr.pagesPerPIP;
1644 const SLONG relative_bit = number.getPageNum() % pageMgr.pagesPerPIP;
1645
1646 WIN pip_window(number.getPageSpaceID(), (sequence == 0) ?
1647 pageSpace->ppFirst : sequence * pageMgr.pagesPerPIP - 1);
1648
1649 page_inv_page* pages =
1650 (page_inv_page*) CCH_FETCH(tdbb, &pip_window, LCK_write, pag_pages);
1651 CCH_precedence(tdbb, &pip_window, prior_page);
1652 CCH_MARK(tdbb, &pip_window);
1653 pages->pip_bits[relative_bit >> 3] |= 1 << (relative_bit & 7);
1654 pages->pip_min = MIN(pages->pip_min, relative_bit);
1655
1656 CCH_RELEASE(tdbb, &pip_window);
1657
1658 pageSpace->pipHighWater = MIN(pageSpace->pipHighWater, sequence);
1659 }
1660
1661
1662 void PAG_set_force_write(Database* dbb, SSHORT flag)
1663 {
1664 /**************************************
1665 *
1666 * P A G _ s e t _ f o r c e _ w r i t e
1667 *
1668 **************************************
1669 *
1670 * Functional description
1671 * Turn on/off force write.
1672 * The value 2 for flag means set to default.
1673 *
1674 **************************************/
1675 thread_db* tdbb = JRD_get_thread_data();
1676
1677 err_post_if_database_is_readonly(dbb);
1678
1679 WIN window(HEADER_PAGE_NUMBER);
1680 header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
1681 CCH_MARK_MUST_WRITE(tdbb, &window);
1682
1683 if (flag == 2)
1684 /* Set force write to the default for the platform */
1685 #ifdef SYNC_WRITE_DEFAULT
1686 flag = 1;
1687 #else
1688 flag = 0;
1689 #endif
1690
1691 if (flag) {
1692 header->hdr_flags |= hdr_force_write;
1693 dbb->dbb_flags |= DBB_force_write;
1694 }
1695 else {
1696 header->hdr_flags &= ~hdr_force_write;
1697 dbb->dbb_flags &= ~DBB_force_write;
1698 }
1699
1700 CCH_RELEASE(tdbb, &window);
1701
1702 PageSpace* pageSpace =
1703 dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
1704 for (jrd_file* file = pageSpace->file; file; file = file->fil_next) {
1705 PIO_force_write(file, flag != 0);
1706 }
1707
1708 for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) {
1709 for (jrd_file* file = shadow->sdw_file; file; file = file->fil_next)
1710 PIO_force_write(file, flag != 0);
1711 }
1712 }
1713
1714
1715 void PAG_set_no_reserve(Database* dbb, USHORT flag)
1716 {
1717 /**************************************
1718 *
1719 * P A G _ s e t _ n o _ r e s e r v e
1720 *
1721 **************************************
1722 *
1723 * Functional description
1724 * Turn on/off reserving space for versions
1725 *
1726 **************************************/
1727 thread_db* tdbb = JRD_get_thread_data();
1728
1729 err_post_if_database_is_readonly(dbb);
1730
1731 WIN window(HEADER_PAGE_NUMBER);
1732 header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
1733 CCH_MARK_MUST_WRITE(tdbb, &window);
1734
1735 if (flag) {
1736 header->hdr_flags |= hdr_no_reserve;
1737 dbb->dbb_flags |= DBB_no_reserve;
1738 }
1739 else {
1740 header->hdr_flags &= ~hdr_no_reserve;
1741 dbb->dbb_flags &= ~DBB_no_reserve;
1742 }
1743
1744 CCH_RELEASE(tdbb, &window);
1745 }
1746
1747
1748 void PAG_set_db_readonly(Database* dbb, bool flag)
1749 {
1750 /*********************************************
1751 *
1752 * P A G _ s e t _ d b _ r e a d o n l y
1753 *
1754 *********************************************
1755 *
1756 * Functional description
1757 * Set database access mode to readonly OR readwrite
1758 *
1759 *********************************************/
1760 thread_db* tdbb = JRD_get_thread_data();
1761
1762 WIN window(HEADER_PAGE_NUMBER);
1763 header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
1764
1765 if (!flag) {
1766 /* If the database is transitioning from RO to RW, reset the
1767 * in-memory Database flag which indicates that the database is RO.
1768 * This will allow the CCH subsystem to allow pages to be MARK'ed
1769 * for WRITE operations
1770 */
1771 header->hdr_flags &= ~hdr_read_only;
1772 dbb->dbb_flags &= ~DBB_read_only;
1773 }
1774
1775 CCH_MARK_MUST_WRITE(tdbb, &window);
1776
1777 if (flag) {
1778 header->hdr_flags |= hdr_read_only;
1779 dbb->dbb_flags |= DBB_read_only;
1780 }
1781
1782 CCH_RELEASE(tdbb, &window);
1783 }
1784
1785
1786 void PAG_set_db_SQL_dialect(Database* dbb, SSHORT flag)
1787 {
1788 /*********************************************
1789 *
1790 * P A G _ s e t _ d b _ S Q L _ d i a l e c t
1791 *
1792 *********************************************
1793 *
1794 * Functional description
1795 * Set database SQL dialect to SQL_DIALECT_V5 or SQL_DIALECT_V6
1796 *
1797 *********************************************/
1798 thread_db* tdbb = JRD_get_thread_data();
1799
1800 const USHORT major_version = dbb->dbb_ods_version;
1801 const USHORT minor_original = dbb->dbb_minor_original;
1802
1803 WIN window(HEADER_PAGE_NUMBER);
1804 header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
1805
1806 if ((flag) && (ENCODE_ODS(major_version, minor_original) >= ODS_10_0)) {
1807 switch (flag) {
1808 case SQL_DIALECT_V5:
1809
1810 if (dbb->dbb_flags & DBB_DB_SQL_dialect_3 ||
1811 header->hdr_flags & hdr_SQL_dialect_3)
1812 {
1813 // Check the returned value here!
1814 ERR_post_warning(isc_dialect_reset_warning, 0);
1815 }
1816
1817 dbb->dbb_flags &= ~DBB_DB_SQL_dialect_3; /* set to 0 */
1818 header->hdr_flags &= ~hdr_SQL_dialect_3; /* set to 0 */
1819 break;
1820
1821 case SQL_DIALECT_V6:
1822 dbb->dbb_flags |= DBB_DB_SQL_dialect_3; /* set to dialect 3 */
1823 header->hdr_flags |= hdr_SQL_dialect_3; /* set to dialect 3 */
1824 break;
1825
1826 default:
1827 CCH_RELEASE(tdbb, &window);
1828 ERR_post(isc_inv_dialect_specified, isc_arg_number, flag,
1829 isc_arg_gds, isc_valid_db_dialects, isc_arg_string,
1830 "1 and 3", isc_arg_gds, isc_dialect_not_changed, 0);
1831 break;
1832 }
1833 }
1834
1835 CCH_MARK_MUST_WRITE(tdbb, &window);
1836
1837 CCH_RELEASE(tdbb, &window);
1838 }
1839
1840
1841 void PAG_set_page_buffers(ULONG buffers)
1842 {
1843 /**************************************
1844 *
1845 * P A G _ s e t _ p a g e _ b u f f e r s
1846 *
1847 **************************************
1848 *
1849 * Functional description
1850 * Set database-specific page buffer cache
1851 *
1852 **************************************/
1853 thread_db* tdbb = JRD_get_thread_data();
1854 Database* dbb = tdbb->tdbb_database;
1855 CHECK_DBB(dbb);
1856
1857 err_post_if_database_is_readonly(dbb);
1858
1859 WIN window(HEADER_PAGE_NUMBER);
1860 header_page* header = (header_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_header);
1861 CCH_MARK_MUST_WRITE(tdbb, &window);
1862 header->hdr_page_buffers = buffers;
1863 CCH_RELEASE(tdbb, &window);
1864 }
1865
1866
1867 void PAG_sweep_interval(SLONG interval)
1868 {
1869 /**************************************
1870 *
1871 * P A G _ s w e e p _ i n t e r v a l
1872 *
1873 **************************************
1874 *
1875 * Functional description
1876 * Set sweep interval.
1877 *
1878 **************************************/
1879
1880 PAG_add_clump(HEADER_PAGE, HDR_sweep_interval, sizeof(SLONG),
1881 (UCHAR *) & interval, CLUMP_REPLACE, 1);
1882 }
1883
1884
1885 /*
1886 int PAG_unlicensed()
1887 {
1888 // **************************************
1889 // *
1890 // * P A G _ u n l i c e n s e d
1891 // *
1892 // **************************************
1893 // *
1894 // * Functional description
1895 // * Log unlicensed activity. Return current count of this
1896 // * sort of non-sense.
1897 // *
1898 // **************************************
1899 thread_db* tdbb = JRD_get_thread_data();
1900
1901 WIN window(HEADER_PAGE);
1902 CCH_FETCH(tdbb, &window, LCK_write, pag_header);
1903 CCH_MARK_MUST_WRITE(tdbb, &window);
1904
1905 USHORT len;
1906 SLONG count;
1907 if (PAG_get_clump(HEADER_PAGE, HDR_unlicensed, &len, (UCHAR *) & count)) {
1908 count++;
1909 PAG_add_clump(HEADER_PAGE, HDR_unlicensed, sizeof(count),
1910 (UCHAR *) & count, CLUMP_REPLACE_ONLY, 1);
1911 }
1912 else {
1913 count = 1;
1914 PAG_add_clump(HEADER_PAGE, HDR_unlicensed, sizeof(count),
1915 (UCHAR *) & count, CLUMP_REPLACE, 1);
1916 }
1917 CCH_RELEASE(tdbb, &window);
1918
1919 return count;
1920 }
1921 */
1922
1923
1924 static void find_clump_space(SLONG page_num,
1925 WIN* window,
1926 PAG* ppage,
1927 USHORT type,
1928 SSHORT len,
1929 const UCHAR* entry,
1930 USHORT must_write)
1931 {
1932 /***********************************************
1933 *
1934 * f i n d _ c l u m p _ s p a c e
1935 *
1936 ***********************************************
1937 *
1938 * Functional description
1939 * Find space for the new clump.
1940 * Add the entry at the end of clumplet list.
1941 * Allocate a new page if required.
1942 *
1943 **************************************/
1944 thread_db* tdbb = JRD_get_thread_data();
1945 Database* dbb = tdbb->tdbb_database;
1946 CHECK_DBB(dbb);
1947
1948 const UCHAR* ptr = entry;
1949 pag* page = *ppage;
1950 header_page* header = 0; // used after the loop
1951 log_info_page* logp = 0; // used after the loop
1952
1953 while (true) {
1954 SLONG next_page, free_space;
1955 USHORT* end_addr;
1956 UCHAR* p;
1957
1958 if (page_num == HEADER_PAGE) {
1959 header = (header_page*) page;
1960 next_page = header->hdr_next_page;
1961 free_space = dbb->dbb_page_size - header->hdr_end;
1962 end_addr = &header->hdr_end;
1963 p = (UCHAR *) header + header->hdr_end;
1964 }
1965 else {
1966 logp = (log_info_page*) page;
1967 next_page = logp->log_next_page;
1968 free_space = dbb->dbb_page_size - logp->log_end;
1969 end_addr = &logp->log_end;
1970 p = (UCHAR *) logp + logp->log_end;
1971 }
1972
1973 if (free_space > (2 + len)) {
1974 if (must_write)
1975 CCH_MARK_MUST_WRITE(tdbb, window);
1976 else
1977 CCH_MARK(tdbb, window);
1978
1979 fb_assert(type <= MAX_UCHAR);
1980 fb_assert(len <= MAX_UCHAR);
1981 *p++ = static_cast<UCHAR>(type);
1982 *p++ = static_cast<UCHAR>(len);
1983
1984 if (len) {
1985 do {
1986 *p++ = *ptr++;
1987 } while (--len);
1988 }
1989
1990 *p = HDR_end;
1991
1992 *end_addr = (USHORT) (p - (UCHAR *) page);
1993 return;
1994 }
1995
1996 if (!next_page)
1997 break;
1998
1999 /* Follow chain of header pages */
2000
2001 if (page_num == HEADER_PAGE)
2002 *ppage = page =
2003 CCH_HANDOFF(tdbb, window, next_page, LCK_write, pag_header);
2004 else
2005 *ppage = page =
2006 CCH_HANDOFF(tdbb, window, next_page, LCK_write, pag_log);
2007 }
2008
2009 WIN new_window(DB_PAGE_SPACE, -1);
2010 pag* new_page = (PAG) DPM_allocate(tdbb, &new_window);
2011
2012 if (must_write)
2013 CCH_MARK_MUST_WRITE(tdbb, &new_window);
2014 else
2015 CCH_MARK(tdbb, &new_window);
2016
2017
2018 header_page* new_header = 0;
2019 log_info_page* new_logp = 0;
2020 SLONG next_page;
2021 USHORT* end_addr;
2022 UCHAR* p;
2023 if (page_num == HEADER_PAGE) {
2024 new_header = (header_page*) new_page;
2025 new_header->hdr_header.pag_type = pag_header;
2026 new_header->hdr_end = HDR_SIZE;
2027 new_header->hdr_page_size = dbb->dbb_page_size;
2028 new_header->hdr_data[0] = HDR_end;
2029 next_page = new_window.win_page.getPageNum();
2030 end_addr = &new_header->hdr_end;
2031 p = new_header->hdr_data;
2032 }
2033 else {
2034 new_logp = (log_info_page*) new_page;
2035 new_logp->log_header.pag_type = pag_log;
2036 new_logp->log_data[0] = LOG_end;
2037 new_logp->log_end = LIP_SIZE;
2038 next_page = new_window.win_page.getPageNum();
2039 end_addr = &new_logp->log_end;
2040 p = new_logp->log_data;
2041 }
2042
2043 fb_assert(type <= MAX_UCHAR);
2044 fb_assert(len <= MAX_UCHAR);
2045 *p++ = static_cast<UCHAR>(type);
2046 *p++ = static_cast<UCHAR>(len);
2047
2048 if (len) {
2049 do {
2050 *p++ = *ptr++;
2051 } while (--len);
2052 }
2053
2054 *p = HDR_end;
2055 *end_addr = (USHORT) (p - (UCHAR *) new_page);
2056
2057 CCH_RELEASE(tdbb, &new_window);
2058
2059 CCH_precedence(tdbb, window, next_page);
2060
2061 CCH_MARK(tdbb, window);
2062
2063 if (page_num == HEADER_PAGE)
2064 header->hdr_next_page = next_page;
2065 else
2066 logp->log_next_page = next_page;
2067 }
2068
2069
2070 static bool find_type(SLONG page_num,
2071 WIN* window,
2072 PAG* ppage,
2073 USHORT lock,
2074 USHORT type,
2075 UCHAR** entry_p,
2076 const UCHAR** clump_end)
2077 {
2078 /***********************************************
2079 *
2080 * f i n d _ t y p e
2081 *
2082 ***********************************************
2083 *
2084 * Functional description
2085 * Find the requested type in a page.
2086 * RETURNS
2087 * pointer to type, pointer to end of page, header.
2088 * true - Found it
2089 * false - Not present
2090 *
2091 **************************************/
2092 thread_db* tdbb = JRD_get_thread_data();
2093
2094 while (true) {
2095 header_page* header = 0;
2096 log_info_page* logp = 0;
2097 UCHAR* p;
2098 SLONG next_page;
2099 if (page_num == HEADER_PAGE) {
2100 header = (header_page*) (*ppage);
2101 p = header->hdr_data;
2102 next_page = header->hdr_next_page;
2103 }
2104 else {
2105 logp = (log_info_page*) (*ppage);
2106 p = logp->log_data;
2107 next_page = logp->log_next_page;
2108 }
2109
2110 UCHAR* q = 0;
2111 for (; (*p != HDR_end); p += 2 + p[1]) {
2112 if (*p == type)
2113 q = p;
2114 }
2115
2116 if (q) {
2117 *entry_p = q;
2118 *clump_end = p;
2119 return true;
2120 }
2121
2122 /* Follow chain of pages */
2123
2124 if (next_page) {
2125 if (page_num == HEADER_PAGE) {
2126 *ppage =
2127 CCH_HANDOFF(tdbb, window, next_page, lock, pag_header);
2128 }
2129 else {
2130 *ppage = CCH_HANDOFF(tdbb, window, next_page, lock, pag_log);
2131 }
2132 }
2133 else
2134 return false;
2135 }
2136 }
2137
2138 PageSpace::~PageSpace()
2139 {
2140 if (file) {
2141 PIO_close(file);
2142 }
2143 }
2144
2145 PageSpace* PageManager::addPageSpace(const USHORT pageSpaceID)
2146 {
2147 PageSpace* newPageSpace = findPageSpace(pageSpaceID);
2148 if (!newPageSpace)
2149 {
2150 newPageSpace = FB_NEW(pool) PageSpace(pageSpaceID);
2151 pageSpaces.add(newPageSpace);
2152 }
2153
2154 return newPageSpace;
2155 }
2156
2157 PageSpace* PageManager::findPageSpace(const USHORT pageSpace)
2158 {
2159 size_t pos;
2160 if (pageSpaces.find(pageSpace, pos)) {
2161 return pageSpaces[pos];
2162 }
2163
2164 return 0;
2165 }
2166
2167 void PageManager::delPageSpace(const USHORT pageSpace)
2168 {
2169 size_t pos;
2170 if (pageSpaces.find(pageSpace, pos))
2171 {
2172 PageSpace* pageSpaceToDelete = pageSpaces[pos];
2173 pageSpaces.remove(pos);
2174 delete pageSpaceToDelete;
2175 }
2176 }
2177
2178 void PageManager::closeAll()
2179 {
2180 for (size_t i = 0; i < pageSpaces.getCount(); i++)
2181 if (pageSpaces[i]->file) {
2182 PIO_close(pageSpaces[i]->file);
2183 }
2184 }
2185
2186 USHORT PageManager::getTempPageSpaceID(thread_db* tdbb)
2187 {
2188 #ifdef SUPERSERVER
2189 return TEMP_PAGE_SPACE;
2190 #else
2191 SET_TDBB(tdbb);
2192 Database* dbb = tdbb->tdbb_database;
2193 Attachment* att = tdbb->tdbb_attachment;
2194 if (!att->att_temp_pg_lock)
2195 {
2196 Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, sizeof(USHORT)) Lock();
2197 lock->lck_type = LCK_page_space;
2198 lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
2199 lock->lck_parent = dbb->dbb_lock;
2200 lock->lck_length = sizeof(USHORT);
2201 lock->lck_dbb = dbb;
2202
2203 PAG_attachment_id(tdbb);
2204
2205 while (true)
2206 {
2207 const double tmp = rand() * (MAX_USHORT - TEMP_PAGE_SPACE - 1.0) / (RAND_MAX + 1.0);
2208 lock->lck_key.lck_long = tmp + TEMP_PAGE_SPACE + 1;
2209 if (LCK_lock(tdbb, lock, LCK_write, LCK_NO_WAIT))
2210 break;
2211 }
2212
2213 att->att_temp_pg_lock = lock;
2214 }
2215
2216 return (USHORT) att->att_temp_pg_lock->lck_key.lck_long;
2217 #endif
2218 }

  ViewVC Help
Powered by ViewVC 1.1.5