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

Contents of /upstream/current/src/jrd/idx.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: 45992 byte(s)
Import current upstream CVS
1 /*
2 * PROGRAM: JRD Access Method
3 * MODULE: idx.cpp
4 * DESCRIPTION: Index 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 * 2003.03.04 Dmitry Yemanov: Added support for NULLs in unique indices.
24 * Done in two stages:
25 * 1. Restored old behaviour of having only _one_
26 * NULL key allowed (i.e. two NULLs are considered
27 * duplicates). idx_e_nullunique error was removed.
28 * 2. Changed algorithms in IDX_create_index() and
29 * check_duplicates() to ignore NULL key duplicates.
30 */
31
32 #include "firebird.h"
33 #include <string.h>
34 #include "../jrd/common.h"
35 #include "../jrd/jrd.h"
36 #include "../jrd/val.h"
37 #include "../jrd/intl.h"
38 #include "../jrd/req.h"
39 #include "../jrd/ods.h"
40 #include "../jrd/btr.h"
41 #include "../jrd/sort.h"
42 #include "../jrd/lls.h"
43 #include "../jrd/tra.h"
44 #include "gen/iberror.h"
45 #include "../jrd/sbm.h"
46 #include "../jrd/exe.h"
47 #include "../jrd/scl.h"
48 #include "../jrd/lck.h"
49 #include "../jrd/rse.h"
50 #include "../jrd/cch.h"
51 #include "../jrd/gdsassert.h"
52 #include "../jrd/btr_proto.h"
53 #include "../jrd/cch_proto.h"
54 #include "../jrd/cmp_proto.h"
55 #include "../jrd/dpm_proto.h"
56 #include "../jrd/err_proto.h"
57 #include "../jrd/evl_proto.h"
58 #include "../jrd/gds_proto.h"
59 #include "../jrd/idx_proto.h"
60 #include "../jrd/intl_proto.h"
61 #include "../jrd/jrd_proto.h"
62 #include "../jrd/lck_proto.h"
63 #include "../jrd/met_proto.h"
64 #include "../jrd/mov_proto.h"
65 #include "../jrd/sort_proto.h"
66 #include "../jrd/thd.h"
67 #include "../jrd/vio_proto.h"
68 #include "../jrd/tra_proto.h"
69
70
71 using namespace Jrd;
72 using namespace Ods;
73
74 /* Data to be passed to index fast load duplicates routine */
75
76 struct index_fast_load {
77 SLONG ifl_duplicates;
78 USHORT ifl_key_length;
79 };
80
81 static IDX_E check_duplicates(thread_db*, Record*, index_desc*, index_insertion*, jrd_rel*);
82 static IDX_E check_foreign_key(thread_db*, Record*, jrd_rel*, jrd_tra*, index_desc*, jrd_rel**, USHORT *);
83 static IDX_E check_partner_index(thread_db*, jrd_rel*, Record*, jrd_tra*, index_desc*, jrd_rel*, SSHORT);
84 static bool duplicate_key(const UCHAR*, const UCHAR*, void*);
85 static PageNumber get_root_page(thread_db*, jrd_rel*);
86 static int index_block_flush(void*);
87 static IDX_E insert_key(thread_db*, jrd_rel*, Record*, jrd_tra*, WIN *, index_insertion*, jrd_rel**, USHORT *);
88 static bool key_equal(const temporary_key*, const temporary_key*);
89 static void signal_index_deletion(thread_db*, jrd_rel*, USHORT);
90
91
92 void IDX_check_access(thread_db* tdbb, CompilerScratch* csb, jrd_rel* view, jrd_rel* relation,
93 jrd_fld* field)
94 {
95 /**************************************
96 *
97 * I D X _ c h e c k _ a c c e s s
98 *
99 **************************************
100 *
101 * Functional description
102 * Check the various indices in a relation
103 * to see if we need REFERENCES access to fields
104 * in the primary key. Don't call this routine for
105 * views or external relations, since the mechanism
106 * ain't there.
107 *
108 **************************************/
109 SET_TDBB(tdbb);
110
111 index_desc idx;
112 idx.idx_id = (USHORT) -1;
113 RelationPages* relPages = relation->getPages(tdbb);
114 WIN window(relPages->rel_pg_space_id, -1);
115 WIN referenced_window(relPages->rel_pg_space_id, -1);
116
117 while (BTR_next_index(tdbb, relation, 0, &idx, &window))
118 if (idx.idx_flags & idx_foreign) {
119 /* find the corresponding primary key index */
120
121 if (!MET_lookup_partner(tdbb, relation, &idx, 0)) {
122 continue;
123 }
124 jrd_rel* referenced_relation =
125 MET_relation(tdbb, idx.idx_primary_relation);
126 MET_scan_relation(tdbb, referenced_relation);
127 const USHORT index_id = (USHORT) idx.idx_primary_index;
128
129 /* get the description of the primary key index */
130
131 referenced_window.win_page =
132 get_root_page(tdbb, referenced_relation);
133 referenced_window.win_flags = 0;
134 index_root_page* referenced_root =
135 (index_root_page*) CCH_FETCH(tdbb, &referenced_window, LCK_read, pag_root);
136 index_desc referenced_idx;
137 if (!BTR_description(tdbb, referenced_relation, referenced_root,
138 &referenced_idx, index_id))
139 {
140 BUGCHECK(173); /* msg 173 referenced index description not found */
141 }
142
143 /* post references access to each field in the index */
144
145 const index_desc::idx_repeat* idx_desc = referenced_idx.idx_rpt;
146 for (USHORT i = 0; i < referenced_idx.idx_count; i++, idx_desc++) {
147 const jrd_fld* referenced_field =
148 MET_get_field(referenced_relation, idx_desc->idx_field);
149 CMP_post_access(tdbb, csb,
150 referenced_relation->rel_security_name,
151 (view ? view->rel_id : 0),
152 SCL_sql_references, "TABLE",
153 referenced_relation->rel_name);
154 CMP_post_access(tdbb, csb,
155 referenced_field->fld_security_name, 0,
156 SCL_sql_references, "COLUMN",
157 referenced_field->fld_name);
158 }
159
160 CCH_RELEASE(tdbb, &referenced_window);
161 }
162 }
163
164
165 bool IDX_check_master_types (thread_db* tdbb, index_desc& idx, jrd_rel* partner_relation, int& bad_segment)
166 {
167 /**********************************************
168 *
169 * I D X _ c h e c k _ m a s t e r _ t y p e s
170 *
171 **********************************************
172 *
173 * Functional description
174 * Check if both indices of foreign key constraint
175 * has compatible data types in appropriate segments.
176 * Called when detail index is created after idx_itype
177 * was assigned
178 *
179 **********************************************/
180
181 SET_TDBB(tdbb);
182
183 index_desc partner_idx;
184
185 // get the index root page for the partner relation
186 WIN window(get_root_page(tdbb, partner_relation));
187 index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root);
188
189 // get the description of the partner index
190 if (!BTR_description(tdbb, partner_relation, root, &partner_idx, idx.idx_primary_index))
191 BUGCHECK(175); /* msg 175 partner index description not found */
192
193 CCH_RELEASE(tdbb, &window);
194
195 // make sure partner index have the same segment count as our
196 fb_assert(idx.idx_count == partner_idx.idx_count);
197
198 int i;
199 for (i = 0; i < idx.idx_count; i++)
200 if (idx.idx_rpt[i].idx_itype != partner_idx.idx_rpt[i].idx_itype)
201 {
202 bad_segment = i;
203 return false;
204 }
205
206 return true;
207 }
208
209
210 void IDX_create_index(
211 thread_db* tdbb,
212 jrd_rel* relation,
213 index_desc* idx,
214 const TEXT* index_name,
215 USHORT* index_id,
216 jrd_tra* transaction,
217 SelectivityList& selectivity)
218 {
219 /**************************************
220 *
221 * I D X _ c r e a t e _ i n d e x
222 *
223 **************************************
224 *
225 * Functional description
226 * Create and populate index.
227 *
228 **************************************/
229 IDX_E result = idx_e_ok;
230
231 SET_TDBB(tdbb);
232 Database* dbb = tdbb->tdbb_database;
233
234 if (relation->rel_file) {
235 ERR_post(isc_no_meta_update, isc_arg_gds, isc_extfile_uns_op,
236 isc_arg_string, ERR_cstring(relation->rel_name), 0);
237 }
238 else if (relation->isVirtual()) {
239 ERR_post(isc_no_meta_update, isc_arg_gds, isc_wish_list, 0);
240 }
241
242 get_root_page(tdbb, relation);
243
244 BTR_reserve_slot(tdbb, relation, transaction, idx);
245
246 if (index_id) {
247 *index_id = idx->idx_id;
248 }
249
250 record_param primary, secondary;
251 secondary.rpb_relation = relation;
252 primary.rpb_relation = relation;
253 primary.rpb_number.setValue(BOF_NUMBER);
254 //primary.getWindow(tdbb).win_flags = secondary.getWindow(tdbb).win_flags = 0; redundant
255
256 const bool isODS11 = (dbb->dbb_ods_version >= ODS_VERSION11);
257 const bool isDescending = (idx->idx_flags & idx_descending);
258 const bool isPrimary = (idx->idx_flags & idx_primary);
259
260 // hvlad: in ODS11 empty string and NULL values can have the same binary
261 // representation in index keys. BTR can distinguish it by the key_length
262 // but SORT module currently don't take it into account. Therefore add to
263 // the index key one byte prefix with 0 for NULL value and 1 for not-NULL
264 // value to produce right sorting.
265 // BTR\fast_load will remove this one byte prefix from the index key.
266 // Note that this is necessary only for single-segment ascending indexes
267 // and only for ODS11 and higher.
268
269 const int nullIndLen = isODS11 && !isDescending && (idx->idx_count == 1) ? 1 : 0;
270 const USHORT key_length = ROUNDUP(BTR_key_length(tdbb, relation, idx) + nullIndLen, sizeof(SINT64));
271
272 const USHORT max_key_size =
273 isODS11 ? MAX_KEY_LIMIT : MAX_KEY_PRE_ODS11;
274
275 if (key_length >= max_key_size) {
276 ERR_post(isc_no_meta_update,
277 isc_arg_gds,
278 isc_keytoobig,
279 isc_arg_string,
280 ERR_cstring(index_name), 0);
281 }
282
283 RecordStack stack;
284 const UCHAR pad = isDescending ? -1 : 0;
285
286 index_fast_load ifl_data;
287 ifl_data.ifl_duplicates = 0;
288 ifl_data.ifl_key_length = key_length;
289
290 bool key_is_null = false;
291
292 sort_key_def key_desc[2];
293 // Key sort description
294 key_desc[0].skd_dtype = SKD_bytes;
295 key_desc[0].skd_flags = SKD_ascending;
296 key_desc[0].skd_length = key_length;
297 key_desc[0].skd_offset = 0;
298 key_desc[0].skd_vary_offset = 0;
299 // RecordNumber sort description
300 key_desc[1].skd_dtype = SKD_int64;
301 key_desc[1].skd_flags = SKD_ascending;
302 key_desc[1].skd_length = sizeof(RecordNumber);
303 key_desc[1].skd_offset = key_length;
304 key_desc[1].skd_vary_offset = 0;
305
306 FPTR_REJECT_DUP_CALLBACK callback =
307 (idx->idx_flags & idx_unique) ? duplicate_key : NULL;
308 void* callback_arg =
309 (idx->idx_flags & idx_unique) ? &ifl_data : NULL;
310
311 sort_context* sort_handle =
312 SORT_init(tdbb, key_length + sizeof(index_sort_record),
313 2, 1, key_desc, callback, callback_arg, 0);
314
315 try {
316
317 jrd_rel* partner_relation = 0;
318 USHORT partner_index_id = 0;
319 if (idx->idx_flags & idx_foreign) {
320 if (!MET_lookup_partner(tdbb, relation, idx, index_name)) {
321 BUGCHECK(173); /* msg 173 referenced index description not found */
322 }
323 partner_relation = MET_relation(tdbb, idx->idx_primary_relation);
324 partner_index_id = (USHORT) idx->idx_primary_index;
325 }
326
327 /* Checkout a garbage collect record block for fetching data. */
328
329 Record* gc_record = VIO_gc_record(tdbb, relation);
330
331 /* Unless this is the only attachment or a database restore, worry about
332 preserving the page working sets of other attachments. */
333 Attachment* attachment = tdbb->tdbb_attachment;
334 if ((attachment) &&
335 (attachment != dbb->dbb_attachments || attachment->att_next))
336 {
337 if (attachment->att_flags & ATT_gbak_attachment ||
338 DPM_data_pages(tdbb, relation) > (SLONG) dbb->dbb_bcb->bcb_count)
339 {
340 primary.getWindow(tdbb).win_flags = secondary.getWindow(tdbb).win_flags =
341 WIN_large_scan;
342 primary.rpb_org_scans = secondary.rpb_org_scans =
343 relation->rel_scan_count++;
344 }
345 }
346
347 /* Loop thru the relation computing index keys. If there are old versions,
348 find them, too. */
349 temporary_key key;
350 while (DPM_next(tdbb, &primary, LCK_read, false, false)) {
351 if (transaction && !VIO_garbage_collect(tdbb, &primary, transaction))
352 continue;
353 if (primary.rpb_flags & rpb_deleted)
354 CCH_RELEASE(tdbb, &primary.getWindow(tdbb));
355 else {
356 primary.rpb_record = gc_record;
357 VIO_data(tdbb, &primary, dbb->dbb_permanent);
358 gc_record = primary.rpb_record;
359 stack.push(primary.rpb_record);
360 }
361 secondary.rpb_page = primary.rpb_b_page;
362 secondary.rpb_line = primary.rpb_b_line;
363 secondary.rpb_prior = primary.rpb_prior;
364 while (secondary.rpb_page) {
365 if (!DPM_fetch(tdbb, &secondary, LCK_read))
366 break; /* must be garbage collected */
367 secondary.rpb_record = NULL;
368 VIO_data(tdbb, &secondary, tdbb->getDefaultPool());
369 stack.push(secondary.rpb_record);
370 secondary.rpb_page = secondary.rpb_b_page;
371 secondary.rpb_line = secondary.rpb_b_line;
372 }
373
374 while (stack.hasData())
375 {
376 Record* record = stack.pop();
377
378 /* If foreign key index is being defined, make sure foreign
379 key definition will not be violated */
380
381 if (idx->idx_flags & idx_foreign)
382 {
383 idx_null_state null_state;
384 /* find out if there is a null segment by faking uniqueness --
385 if there is one, don't bother to check the primary key */
386
387 if (!(idx->idx_flags & idx_unique)) {
388 idx->idx_flags |= idx_unique;
389 result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
390 idx->idx_flags &= ~idx_unique;
391 }
392 else {
393 result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
394 }
395 if (null_state != idx_nulls_none) {
396 result = idx_e_ok;
397 }
398 else {
399 result =
400 check_partner_index(tdbb, relation, record,
401 transaction, idx,
402 partner_relation,
403 partner_index_id);
404 }
405 }
406
407 if (result == idx_e_ok) {
408 idx_null_state null_state;
409 BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
410 key_is_null = (null_state == idx_nulls_all);
411
412 if (isPrimary && null_state != idx_nulls_none)
413 {
414 fb_assert(key.key_null_segment < idx->idx_count);
415
416 const USHORT bad_id = idx->idx_rpt[key.key_null_segment].idx_field;
417 const jrd_fld *bad_fld = MET_get_field(relation, bad_id);
418
419 ERR_post(isc_not_valid,
420 isc_arg_string, bad_fld->fld_name.c_str(),
421 isc_arg_string, NULL_STRING_MARK, 0);
422 }
423 }
424 else {
425 do {
426 if (record != gc_record)
427 delete record;
428 } while (stack.hasData() && (record = stack.pop()));
429 gc_record->rec_flags &= ~REC_gc_active;
430 if (primary.getWindow(tdbb).win_flags & WIN_large_scan)
431 --relation->rel_scan_count;
432 ERR_duplicate_error(result, partner_relation,
433 partner_index_id);
434 }
435
436 if (key.key_length > key_length) {
437 do {
438 if (record != gc_record)
439 delete record;
440 } while (stack.hasData() && (record = stack.pop()));
441 gc_record->rec_flags &= ~REC_gc_active;
442 if (primary.getWindow(tdbb).win_flags & WIN_large_scan)
443 --relation->rel_scan_count;
444 ERR_post(isc_key_too_big, 0);
445 }
446
447 UCHAR* p;
448 SORT_put(tdbb, sort_handle, reinterpret_cast<ULONG**>(&p));
449
450 /* try to catch duplicates early */
451
452 if (ifl_data.ifl_duplicates > 0) {
453 do {
454 if (record != gc_record)
455 delete record;
456 } while (stack.hasData() && (record = stack.pop()));
457 gc_record->rec_flags &= ~REC_gc_active;
458 if (primary.getWindow(tdbb).win_flags & WIN_large_scan)
459 --relation->rel_scan_count;
460 ERR_post(isc_no_dup, isc_arg_string,
461 ERR_cstring(index_name), 0);
462 }
463
464 USHORT l = key.key_length;
465
466 if (nullIndLen) {
467 *p++ = (key.key_length == 0) ? 0 : 1;
468 }
469 if (l > 0) {
470 memcpy(p, key.key_data, l);
471 p += l;
472 }
473 if ( (l = key_length - nullIndLen - key.key_length) ) {
474 memset(p, pad, l);
475 p += l;
476 }
477 index_sort_record* isr = (index_sort_record*) p;
478 isr->isr_record_number = primary.rpb_number.getValue();
479 isr->isr_key_length = key.key_length;
480 isr->isr_flags = (stack.hasData() ? ISR_secondary : 0) | (key_is_null ? ISR_null : 0);
481 if (record != gc_record)
482 delete record;
483 }
484
485 if (--tdbb->tdbb_quantum < 0)
486 JRD_reschedule(tdbb, 0, true);
487 }
488
489 gc_record->rec_flags &= ~REC_gc_active;
490 if (primary.getWindow(tdbb).win_flags & WIN_large_scan)
491 --relation->rel_scan_count;
492
493 SORT_sort(tdbb, sort_handle);
494
495 if (ifl_data.ifl_duplicates > 0) {
496 ERR_post(isc_no_dup, isc_arg_string,
497 ERR_cstring(index_name), 0);
498 }
499
500 }
501 catch (const Firebird::Exception& ex) {
502 Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
503 SORT_fini(sort_handle, tdbb->tdbb_attachment);
504 ERR_punt();
505 }
506
507 BTR_create(tdbb, relation, idx, key_length, sort_handle, selectivity);
508
509 if (ifl_data.ifl_duplicates > 0) {
510 // we don't need SORT_fini() here, as it's called inside BTR_create()
511 ERR_post(isc_no_dup, isc_arg_string,
512 ERR_cstring(index_name), 0);
513 }
514 }
515
516
517 IndexBlock* IDX_create_index_block(thread_db* tdbb, jrd_rel* relation, USHORT id)
518 {
519 /**************************************
520 *
521 * I D X _ c r e a t e _ i n d e x _ b l o c k
522 *
523 **************************************
524 *
525 * Functional description
526 * Create an index block and an associated
527 * lock block for the specified index.
528 *
529 **************************************/
530 SET_TDBB(tdbb);
531 Database* dbb = tdbb->tdbb_database;
532 CHECK_DBB(dbb);
533
534 IndexBlock* index_block = FB_NEW(*dbb->dbb_permanent) IndexBlock();
535 index_block->idb_id = id;
536
537 /* link the block in with the relation linked list */
538
539 index_block->idb_next = relation->rel_index_blocks;
540 relation->rel_index_blocks = index_block;
541
542 /* create a shared lock for the index, to coordinate
543 any modification to the index so that the cached information
544 about the index will be discarded */
545
546 Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
547 index_block->idb_lock = lock;
548 lock->lck_parent = dbb->dbb_lock;
549 lock->lck_dbb = dbb;
550 lock->lck_key.lck_long = (relation->rel_id << 16) | index_block->idb_id;
551 lock->lck_length = sizeof(lock->lck_key.lck_long);
552 lock->lck_type = LCK_expression;
553 lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
554 lock->lck_ast = index_block_flush;
555 lock->lck_object = index_block;
556
557 return index_block;
558 }
559
560
561 void IDX_delete_index(thread_db* tdbb, jrd_rel* relation, USHORT id)
562 {
563 /**************************************
564 *
565 * I D X _ d e l e t e _ i n d e x
566 *
567 **************************************
568 *
569 * Functional description
570 * Delete a single index.
571 *
572 **************************************/
573 SET_TDBB(tdbb);
574
575 signal_index_deletion(tdbb, relation, id);
576
577 RelationPages* relPages = relation->getPages(tdbb);
578 WIN window(relPages->rel_pg_space_id, relPages->rel_index_root);
579 CCH_FETCH(tdbb, &window, LCK_write, pag_root);
580
581 BTR_delete_index(tdbb, &window, id);
582 }
583
584
585 void IDX_delete_indices(thread_db* tdbb, jrd_rel* relation, RelationPages* relPages)
586 {
587 /**************************************
588 *
589 * I D X _ d e l e t e _ i n d i c e s
590 *
591 **************************************
592 *
593 * Functional description
594 * Delete all known indices in preparation for deleting a
595 * complete relation.
596 *
597 **************************************/
598 SSHORT i;
599
600 SET_TDBB(tdbb);
601
602 WIN window(relPages->rel_pg_space_id, relPages->rel_index_root);
603 index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root);
604
605 for (i = 0; i < root->irt_count; i++) {
606 BTR_delete_index(tdbb, &window, i);
607 root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_root);
608 }
609
610 CCH_RELEASE(tdbb, &window);
611 }
612
613
614 IDX_E IDX_erase(thread_db* tdbb,
615 record_param* rpb,
616 jrd_tra* transaction, jrd_rel** bad_relation, USHORT * bad_index)
617 {
618 /**************************************
619 *
620 * I D X _ e r a s e
621 *
622 **************************************
623 *
624 * Functional description
625 * Check the various indices prior to an ERASE operation.
626 * If one is a primary key, check its partner for
627 * a duplicate record.
628 *
629 **************************************/
630 index_desc idx;
631
632 SET_TDBB(tdbb);
633
634 IDX_E error_code = idx_e_ok;
635 idx.idx_id = (USHORT) -1;
636 RelationPages* relPages = rpb->rpb_relation->getPages(tdbb);
637 WIN window(relPages->rel_pg_space_id, -1);
638
639 while (BTR_next_index(tdbb, rpb->rpb_relation, transaction, &idx, &window))
640 if (idx.idx_flags & (idx_primary | idx_unique)) {
641 error_code = check_foreign_key(tdbb, rpb->rpb_record,
642 rpb->rpb_relation, transaction,
643 &idx, bad_relation, bad_index);
644 if (idx_e_ok != error_code) {
645 CCH_RELEASE(tdbb, &window);
646 break;
647 }
648 }
649
650 return error_code;
651 }
652
653
654 void IDX_garbage_collect(thread_db* tdbb,
655 record_param* rpb,
656 RecordStack& going,
657 RecordStack& staying)
658 {
659 /**************************************
660 *
661 * I D X _ g a r b a g e _ c o l l e c t
662 *
663 **************************************
664 *
665 * Functional description
666 * Perform garbage collection for a bunch of records. Scan
667 * through the indices defined for a relation. Garbage collect
668 * each.
669 *
670 **************************************/
671 index_desc idx;
672 temporary_key key1, key2;
673
674 SET_TDBB(tdbb);
675
676 index_insertion insertion;
677 insertion.iib_descriptor = &idx;
678 insertion.iib_number = rpb->rpb_number;
679 insertion.iib_relation = rpb->rpb_relation;
680 insertion.iib_key = &key1;
681
682 RelationPages* relPages = rpb->rpb_relation->getPages(tdbb);
683 WIN window(relPages->rel_pg_space_id, relPages->rel_index_root);
684
685 index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root);
686
687 for (USHORT i = 0; i < root->irt_count; i++) {
688 if (BTR_description(tdbb, rpb->rpb_relation, root, &idx, i)) {
689 for (RecordStack::iterator stack1(going); stack1.hasData(); ++stack1) {
690 Record* rec1 = stack1.object();
691 BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, 0, false);
692
693 /* Cancel index if there are duplicates in the remaining records */
694
695
696 RecordStack::iterator stack2(stack1);
697 for (++stack2; stack2.hasData(); ++stack2)
698 {
699 Record* rec2 = stack2.object();
700 if (rec2->rec_number == rec1->rec_number) {
701 BTR_key(tdbb, rpb->rpb_relation, rec2, &idx, &key2, 0, false);
702 if (key_equal(&key1, &key2))
703 break;
704 }
705 }
706 if (stack2.hasData())
707 continue;
708
709
710 /* Make sure the index doesn't exist in any record remaining */
711
712 RecordStack::iterator stack3(staying);
713 for (; stack3.hasData(); ++stack3) {
714 Record* rec3 = stack3.object();
715 BTR_key(tdbb, rpb->rpb_relation, rec3, &idx, &key2, 0, false);
716 if (key_equal(&key1, &key2))
717 break;
718 }
719 if (stack3.hasData())
720 continue;
721
722 /* Get rid of index node */
723
724 BTR_remove(tdbb, &window, &insertion);
725 root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root);
726 if (stack1.hasMore(1))
727 {
728 BTR_description(tdbb, rpb->rpb_relation, root, &idx, i);
729 }
730 }
731 }
732 }
733
734 CCH_RELEASE(tdbb, &window);
735 }
736
737
738 IDX_E IDX_modify(thread_db* tdbb,
739 record_param* org_rpb,
740 record_param* new_rpb,
741 jrd_tra* transaction, jrd_rel** bad_relation, USHORT * bad_index)
742 {
743 /**************************************
744 *
745 * I D X _ m o d i f y
746 *
747 **************************************
748 *
749 * Functional description
750 * Update the various indices after a MODIFY operation. If a duplicate
751 * index is violated, return the index number. If successful, return
752 * -1.
753 *
754 **************************************/
755 SET_TDBB(tdbb);
756
757 temporary_key key1;
758 index_desc idx;
759
760 index_insertion insertion;
761 insertion.iib_relation = org_rpb->rpb_relation;
762 insertion.iib_number = org_rpb->rpb_number;
763 insertion.iib_key = &key1;
764 insertion.iib_descriptor = &idx;
765 insertion.iib_transaction = transaction;
766 IDX_E error_code = idx_e_ok;
767 idx.idx_id = (USHORT) -1;
768
769 RelationPages* relPages = org_rpb->rpb_relation->getPages(tdbb);
770 WIN window(relPages->rel_pg_space_id, -1);
771
772 temporary_key key2;
773
774 while (BTR_next_index
775 (tdbb, org_rpb->rpb_relation, transaction, &idx, &window))
776 {
777 *bad_index = idx.idx_id;
778 *bad_relation = new_rpb->rpb_relation;
779 if ( (error_code =
780 BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx,
781 &key1, 0, false)) )
782 {
783 CCH_RELEASE(tdbb, &window);
784 break;
785 }
786 BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record, &idx,
787 &key2, 0, false);
788 if (!key_equal(&key1, &key2)) {
789 if (( error_code =
790 insert_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record,
791 transaction, &window, &insertion, bad_relation,
792 bad_index)) )
793 {
794 return error_code;
795 }
796 }
797 }
798
799 return error_code;
800 }
801
802
803 IDX_E IDX_modify_check_constraints(thread_db* tdbb,
804 record_param* org_rpb,
805 record_param* new_rpb,
806 jrd_tra* transaction,
807 jrd_rel** bad_relation, USHORT * bad_index)
808 {
809 /**************************************
810 *
811 * I D X _ m o d i f y _ c h e c k _ c o n s t r a i n t
812 *
813 **************************************
814 *
815 * Functional description
816 * Check for foreign key constraint after a modify statement
817 *
818 **************************************/
819 temporary_key key1, key2;
820
821 SET_TDBB(tdbb);
822
823 IDX_E error_code = idx_e_ok;
824 index_desc idx;
825 idx.idx_id = (USHORT) -1;
826
827 RelationPages* relPages = org_rpb->rpb_relation->getPages(tdbb);
828 WIN window(relPages->rel_pg_space_id, -1);
829
830 /* If relation's primary/unique keys have no dependencies by other
831 relations' foreign keys then don't bother cycling thru all index
832 descriptions. */
833
834 if (!(org_rpb->rpb_relation->rel_flags & REL_check_partners) &&
835 !(org_rpb->rpb_relation->rel_primary_dpnds.prim_reference_ids))
836 {
837 return error_code;
838 }
839
840 /* Now check all the foreign key constraints. Referential integrity relation
841 could be established by primary key/foreign key or unique key/foreign key */
842
843 while (BTR_next_index
844 (tdbb, org_rpb->rpb_relation, transaction, &idx, &window))
845 {
846 if (!(idx.idx_flags & (idx_primary | idx_unique))
847 || !MET_lookup_partner(tdbb, org_rpb->rpb_relation, &idx, 0))
848 {
849 continue;
850 }
851 *bad_index = idx.idx_id;
852 *bad_relation = new_rpb->rpb_relation;
853 if (
854 (error_code =
855 BTR_key(tdbb, new_rpb->rpb_relation, new_rpb->rpb_record, &idx,
856 &key1, 0, false))
857 || (error_code =
858 BTR_key(tdbb, org_rpb->rpb_relation, org_rpb->rpb_record,
859 &idx, &key2, 0, false)))
860 {
861 CCH_RELEASE(tdbb, &window);
862 break;
863 }
864 if (!key_equal(&key1, &key2)) {
865 error_code =
866 check_foreign_key(tdbb, org_rpb->rpb_record,
867 org_rpb->rpb_relation, transaction, &idx,
868 bad_relation, bad_index);
869 if (idx_e_ok != error_code) {
870 CCH_RELEASE(tdbb, &window);
871 return error_code;
872 }
873 }
874 }
875
876 return error_code;
877 }
878
879
880 void IDX_statistics(thread_db* tdbb, jrd_rel* relation, USHORT id,
881 SelectivityList& selectivity)
882 {
883 /**************************************
884 *
885 * I D X _ s t a t i s t i c s
886 *
887 **************************************
888 *
889 * Functional description
890 * Scan index pages recomputing
891 * selectivity.
892 *
893 **************************************/
894
895 SET_TDBB(tdbb);
896
897 BTR_selectivity(tdbb, relation, id, selectivity);
898 }
899
900
901 IDX_E IDX_store(thread_db* tdbb,
902 record_param* rpb,
903 jrd_tra* transaction, jrd_rel** bad_relation, USHORT * bad_index)
904 {
905 /**************************************
906 *
907 * I D X _ s t o r e
908 *
909 **************************************
910 *
911 * Functional description
912 * Update the various indices after a STORE operation. If a duplicate
913 * index is violated, return the index number. If successful, return
914 * -1.
915 *
916 **************************************/
917 SET_TDBB(tdbb);
918
919 temporary_key key;
920 index_desc idx;
921
922 index_insertion insertion;
923 insertion.iib_relation = rpb->rpb_relation;
924 insertion.iib_number = rpb->rpb_number;
925 insertion.iib_key = &key;
926 insertion.iib_descriptor = &idx;
927 insertion.iib_transaction = transaction;
928
929 IDX_E error_code = idx_e_ok;
930 idx.idx_id = (USHORT) -1;
931
932 RelationPages* relPages = rpb->rpb_relation->getPages(tdbb);
933 WIN window(relPages->rel_pg_space_id, -1);
934
935 while (BTR_next_index
936 (tdbb, rpb->rpb_relation, transaction, &idx, &window))
937 {
938 *bad_index = idx.idx_id;
939 *bad_relation = rpb->rpb_relation;
940 if ( (error_code =
941 BTR_key(tdbb, rpb->rpb_relation, rpb->rpb_record, &idx, &key, 0, false)) )
942 {
943 CCH_RELEASE(tdbb, &window);
944 break;
945 }
946 if ( (error_code =
947 insert_key(tdbb, rpb->rpb_relation, rpb->rpb_record, transaction,
948 &window, &insertion, bad_relation, bad_index)) )
949 {
950 return error_code;
951 }
952 }
953
954 return error_code;
955 }
956
957
958 static IDX_E check_duplicates(
959 thread_db* tdbb,
960 Record* record,
961 index_desc* record_idx,
962 index_insertion* insertion, jrd_rel* relation_2)
963 {
964 /**************************************
965 *
966 * c h e c k _ d u p l i c a t e s
967 *
968 **************************************
969 *
970 * Functional description
971 * Make sure there aren't any active duplicates for
972 * a unique index or a foreign key.
973 *
974 **************************************/
975 DSC desc1, desc2;
976
977 SET_TDBB(tdbb);
978
979 IDX_E result = idx_e_ok;
980 index_desc* insertion_idx = insertion->iib_descriptor;
981 record_param rpb, old_rpb;
982 rpb.rpb_relation = insertion->iib_relation;
983 rpb.rpb_record = NULL;
984 // rpb.getWindow(tdbb).win_flags = 0; redundant.
985
986 old_rpb.rpb_relation = insertion->iib_relation;
987 old_rpb.rpb_record = NULL;
988
989 jrd_rel* relation_1 = insertion->iib_relation;
990 Firebird::HalfStaticArray<UCHAR, 256> tmp;
991 RecordBitmap::Accessor accessor(insertion->iib_duplicates);
992
993 ISC_STATUS* const original_status = tdbb->tdbb_status_vector;
994 ISC_STATUS_ARRAY local_status;
995 memset(local_status, 0, sizeof(ISC_STATUS_ARRAY));
996 tdbb->tdbb_status_vector = local_status;
997
998 if (accessor.getFirst())
999 do {
1000 bool has_old_values;
1001 const bool is_fk = (record_idx->idx_flags & idx_foreign) != 0;
1002
1003 rpb.rpb_number.setValue(accessor.current());
1004
1005 if (rpb.rpb_number != insertion->iib_number
1006 && VIO_get_current(tdbb, &old_rpb, &rpb, insertion->iib_transaction,
1007 tdbb->getDefaultPool(),
1008 is_fk,
1009 has_old_values) )
1010 {
1011 // dimitr: we shouldn't ignore status exceptions which take place
1012 // inside the lock manager. Namely, they are: isc_deadlock,
1013 // isc_lock_conflict, isc_lock_timeout. Otherwise we may
1014 // have logically corrupted database as a result. If any
1015 // of the mentioned errors appeared, it means that there's
1016 // an active transaction out there which has modified our
1017 // record. For "nowait" transaction, it means we have
1018 // an update conflict.
1019 //
1020 // was: if (rpb.rpb_flags & rpb_deleted) {
1021 //
1022 // P.S. I think the check for a status vector should be enough,
1023 // but for sure let's keep the old one as well.
1024 // 2003.05.27
1025 // /*
1026 const bool lock_error =
1027 (tdbb->tdbb_status_vector[1] == isc_deadlock ||
1028 tdbb->tdbb_status_vector[1] == isc_lock_conflict ||
1029 tdbb->tdbb_status_vector[1] == isc_lock_timeout);
1030 // the above errors are not thrown but returned silently
1031
1032 if (rpb.rpb_flags & rpb_deleted || lock_error) {
1033 result = idx_e_duplicate;
1034 break;
1035 }
1036 // */
1037 const bool has_cur_values = !(rpb.rpb_flags & rpb_deleted);
1038 if (!has_cur_values && !has_old_values) {
1039 result = idx_e_duplicate;
1040 break;
1041 }
1042
1043 /* check the values of the fields in the record being inserted with the
1044 record retrieved -- for unique indexes the insertion index and the
1045 record index are the same, but for foreign keys they are different */
1046
1047 if (record_idx->idx_flags & idx_expressn)
1048 {
1049 bool flag_idx;
1050 const dsc* desc_idx = BTR_eval_expression(tdbb, record_idx, record, flag_idx);
1051
1052 /* hvlad: BTR_eval_expression call EVL_expr which returns impure->vlu_desc.
1053 Since record_idx and insertion_idx are the same indexes second call to
1054 BTR_eval_expression will overwrite value from first call. So we must
1055 save first result into another dsc
1056 */
1057
1058 desc1 = *desc_idx;
1059 const USHORT idx_dsc_length = record_idx->idx_expression_desc.dsc_length;
1060 desc1.dsc_address = tmp.getBuffer(idx_dsc_length);
1061 fb_assert(desc_idx->dsc_length <= idx_dsc_length);
1062 memmove(desc1.dsc_address, desc_idx->dsc_address, desc_idx->dsc_length);
1063
1064 bool flag_rec = false;
1065 const dsc* desc_rec = has_cur_values ?
1066 BTR_eval_expression(tdbb, insertion_idx, rpb.rpb_record, flag_rec) : NULL;
1067
1068 const bool equal_cur = has_cur_values && flag_rec && flag_idx &&
1069 (MOV_compare(desc_rec, &desc1) == 0);
1070
1071 if (!is_fk && equal_cur) {
1072 result = idx_e_duplicate;
1073 break;
1074 }
1075
1076 if (has_old_values)
1077 {
1078 desc_rec = BTR_eval_expression(tdbb, insertion_idx, old_rpb.rpb_record, flag_rec);
1079
1080 const bool equal_old = flag_rec && flag_idx &&
1081 (MOV_compare(desc_rec, &desc1) == 0);
1082
1083 if (is_fk) {
1084 if (equal_cur && equal_old) {
1085 result = idx_e_duplicate;
1086 break;
1087 }
1088 }
1089 else {
1090 if (equal_cur || equal_old) {
1091 result = idx_e_duplicate;
1092 break;
1093 }
1094 }
1095 }
1096 }
1097 else
1098 {
1099 bool all_nulls = true;
1100 USHORT i;
1101 for (i = 0; i < insertion_idx->idx_count; i++)
1102 {
1103 bool flag_cur = false;
1104 USHORT field_id = record_idx->idx_rpt[i].idx_field;
1105 const bool flag_idx = EVL_field(relation_2, record, field_id, &desc2);
1106
1107 if (has_cur_values)
1108 {
1109 field_id = insertion_idx->idx_rpt[i].idx_field;
1110 /* In order to "map a null to a default" value (in EVL_field()),
1111 * the relation block is referenced.
1112 * Reference: Bug 10116, 10424
1113 */
1114 flag_cur = EVL_field(relation_1, rpb.rpb_record, field_id, &desc1);
1115 }
1116
1117 const bool not_equal_cur = !has_cur_values ||
1118 has_cur_values && ( (flag_cur != flag_idx) || (MOV_compare(&desc1, &desc2) != 0) );
1119
1120 if ((is_fk || !has_old_values) && not_equal_cur)
1121 break;
1122
1123 if (has_old_values)
1124 {
1125 field_id = insertion_idx->idx_rpt[i].idx_field;
1126 const bool flag_old = EVL_field(relation_1, old_rpb.rpb_record, field_id, &desc1);
1127
1128 const bool not_equal_old = (flag_old != flag_idx || MOV_compare(&desc1, &desc2) != 0);
1129
1130 if (is_fk) {
1131 if (not_equal_cur || not_equal_old)
1132 break;
1133 }
1134 else {
1135 if (not_equal_cur && not_equal_old)
1136 break;
1137 }
1138 }
1139
1140 all_nulls = all_nulls && !flag_cur && !flag_idx;
1141 }
1142
1143 if (i >= insertion_idx->idx_count && !all_nulls) {
1144 result = idx_e_duplicate;
1145 break;
1146 }
1147 }
1148 }
1149 } while (accessor.getNext());
1150
1151 if (rpb.rpb_record)
1152 delete rpb.rpb_record;
1153
1154 if (old_rpb.rpb_record)
1155 delete old_rpb.rpb_record;
1156
1157 if (local_status[1]) {
1158 memcpy(original_status, local_status, sizeof(ISC_STATUS_ARRAY));
1159 }
1160 tdbb->tdbb_status_vector = original_status;
1161
1162 return result;
1163 }
1164
1165
1166 static IDX_E check_foreign_key(
1167 thread_db* tdbb,
1168 Record* record,
1169 jrd_rel* relation,
1170 jrd_tra* transaction,
1171 index_desc* idx,
1172 jrd_rel** bad_relation, USHORT* bad_index)
1173 {
1174 /**************************************
1175 *
1176 * c h e c k _ f o r e i g n _ k e y
1177 *
1178 **************************************
1179 *
1180 * Functional description
1181 * The passed index participates in a foreign key.
1182 * Check the passed record to see if a corresponding
1183 * record appears in the partner index.
1184 *
1185 **************************************/
1186 SET_TDBB(tdbb);
1187
1188 IDX_E result = idx_e_ok;
1189
1190 if (!MET_lookup_partner(tdbb, relation, idx, 0)) {
1191 return result;
1192 }
1193
1194 jrd_rel* partner_relation;
1195 USHORT index_id = 0;
1196 if (idx->idx_flags & idx_foreign)
1197 {
1198 partner_relation = MET_relation(tdbb, idx->idx_primary_relation);
1199 index_id = (USHORT) idx->idx_primary_index;
1200 result =
1201 check_partner_index(tdbb, relation, record, transaction, idx,
1202 partner_relation, index_id);
1203 }
1204 else if (idx->idx_flags & (idx_primary | idx_unique))
1205 {
1206 for (int index_number = 0;
1207 index_number < (int) idx->idx_foreign_primaries->count();
1208 index_number++)
1209 {
1210 if (idx->idx_id != (*idx->idx_foreign_primaries)[index_number])
1211 {
1212 continue;
1213 }
1214 partner_relation =
1215 MET_relation(tdbb, (*idx->idx_foreign_relations)[index_number]);
1216
1217 index_id = (*idx->idx_foreign_indexes)[index_number];
1218
1219 if ((relation->rel_flags & REL_temp_conn) &&
1220 (partner_relation->rel_flags & REL_temp_tran))
1221 {
1222 jrd_rel::RelPagesSnapshot pagesSnapshot(tdbb, partner_relation);
1223 partner_relation->fillPagesSnapshot(pagesSnapshot, true);
1224
1225 for (size_t i = 0; i < pagesSnapshot.getCount(); i++)
1226 {
1227 RelationPages* partnerPages = pagesSnapshot[i];
1228 tdbb->tdbb_temp_traid = partnerPages->rel_instance_id;
1229 if ( (result = check_partner_index(tdbb, relation, record,
1230 transaction, idx, partner_relation, index_id)) )
1231 {
1232 break;
1233 }
1234 }
1235
1236 tdbb->tdbb_temp_traid = 0;
1237 if (result)
1238 break;
1239 }
1240 else
1241 {
1242 if ( (result = check_partner_index(tdbb, relation, record,
1243 transaction, idx, partner_relation, index_id)) )
1244 {
1245 break;
1246 }
1247 }
1248 }
1249 }
1250
1251 if (result) {
1252 if (idx->idx_flags & idx_foreign) {
1253 *bad_relation = relation;
1254 *bad_index = idx->idx_id;
1255 }
1256 else {
1257 *bad_relation = partner_relation;
1258 *bad_index = index_id;
1259 }
1260 }
1261
1262 return result;
1263 }
1264
1265
1266 static IDX_E check_partner_index(
1267 thread_db* tdbb,
1268 jrd_rel* relation,
1269 Record* record,
1270 jrd_tra* transaction,
1271 index_desc* idx,
1272 jrd_rel* partner_relation, SSHORT index_id)
1273 {
1274 /**************************************
1275 *
1276 * c h e c k _ p a r t n e r _ i n d e x
1277 *
1278 **************************************
1279 *
1280 * Functional description
1281 * The passed index participates in a foreign key.
1282 * Check the passed record to see if a corresponding
1283 * record appears in the partner index.
1284 *
1285 **************************************/
1286 SET_TDBB(tdbb);
1287
1288 IDX_E result = idx_e_ok;
1289
1290 /* get the index root page for the partner relation */
1291
1292 WIN window(get_root_page(tdbb, partner_relation));
1293 index_root_page* root = (index_root_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_root);
1294
1295 /* get the description of the partner index */
1296
1297 index_desc partner_idx;
1298 if (!BTR_description(tdbb, partner_relation, root, &partner_idx, index_id))
1299 BUGCHECK(175); /* msg 175 partner index description not found */
1300
1301 bool fuzzy = false;
1302
1303 const index_desc::idx_repeat* idx_desc = idx->idx_rpt;
1304 for (USHORT i = 0; i < idx->idx_count; i++, idx_desc++)
1305 {
1306 if (idx_desc->idx_itype >= idx_first_intl_string)
1307 {
1308 TextType* textType = INTL_texttype_lookup(tdbb, INTL_INDEX_TO_TEXT(idx_desc->idx_itype));
1309
1310 if (textType->getFlags() & TEXTTYPE_SEPARATE_UNIQUE)
1311 {
1312 fuzzy = true;
1313 break;
1314 }
1315 }
1316 }
1317
1318 /* get the key in the original index */
1319 // AB: Fake the index to be an unique index, because the INTL makes
1320 // different keys depending on unique index or not.
1321 // The key build should be exactly the same as stored in the
1322 // unique index, because a comparison is done on both keys.
1323 index_desc tmpIndex = *idx;
1324 tmpIndex.idx_flags |= idx_unique;
1325 temporary_key key;
1326 result = BTR_key(tdbb, relation, record, &tmpIndex, &key, 0, fuzzy);
1327 CCH_RELEASE(tdbb, &window);
1328
1329 /* now check for current duplicates */
1330
1331 if (result == idx_e_ok) {
1332 /* fill out a retrieval block for the purpose of
1333 generating a bitmap of duplicate records */
1334
1335 IndexRetrieval retrieval;
1336 MOVE_CLEAR(&retrieval, sizeof(IndexRetrieval));
1337 //retrieval.blk_type = type_irb;
1338 retrieval.irb_index = partner_idx.idx_id;
1339 MOVE_FAST(&partner_idx, &retrieval.irb_desc,
1340 sizeof(retrieval.irb_desc));
1341 retrieval.irb_generic = irb_equality | (fuzzy ? irb_starting : 0);
1342 retrieval.irb_relation = partner_relation;
1343 retrieval.irb_key = &key;
1344 retrieval.irb_upper_count = retrieval.irb_lower_count = idx->idx_count;
1345 if (partner_idx.idx_flags & idx_descending) {
1346 retrieval.irb_generic |= irb_descending;
1347 }
1348 if ((idx->idx_flags & idx_descending) !=
1349 (partner_idx.idx_flags & idx_descending))
1350 {
1351 BTR_complement_key(&key);
1352 }
1353
1354 RecordBitmap* bitmap = NULL;
1355 BTR_evaluate(tdbb, &retrieval, &bitmap, NULL);
1356
1357 /* if there is a bitmap, it means duplicates were found */
1358
1359 if (bitmap) {
1360 index_insertion insertion;
1361 insertion.iib_descriptor = &partner_idx;
1362 insertion.iib_relation = partner_relation;
1363 insertion.iib_number.setValue(BOF_NUMBER);
1364 insertion.iib_duplicates = bitmap;
1365 insertion.iib_transaction = transaction;
1366 result =
1367 check_duplicates(tdbb, record, idx, &insertion, relation);
1368 if (idx->idx_flags & (idx_primary | idx_unique))
1369 result = result ? idx_e_foreign_references_present : idx_e_ok;
1370 if (idx->idx_flags & idx_foreign)
1371 result = result ? idx_e_ok : idx_e_foreign_target_doesnt_exist;
1372 delete bitmap;
1373 }
1374 else if (idx->idx_flags & idx_foreign) {
1375 result = idx_e_foreign_target_doesnt_exist;
1376 }
1377 }
1378
1379 return result;
1380 }
1381
1382
1383 static bool duplicate_key(const UCHAR* record1, const UCHAR* record2, void* ifl_void)
1384 {
1385 /**************************************
1386 *
1387 * d u p l i c a t e _ k e y
1388 *
1389 **************************************
1390 *
1391 * Functional description
1392 * Callback routine for duplicate keys during index creation. Just
1393 * bump a counter.
1394 *
1395 **************************************/
1396 index_fast_load* ifl_data = static_cast<index_fast_load*>(ifl_void);
1397 const index_sort_record* rec1 =
1398 (index_sort_record*) (record1 + ifl_data->ifl_key_length);
1399 const index_sort_record* rec2 =
1400 (index_sort_record*) (record2 + ifl_data->ifl_key_length);
1401
1402 if (!(rec1->isr_flags & (ISR_secondary | ISR_null)) &&
1403 !(rec2->isr_flags & (ISR_secondary | ISR_null)))
1404 {
1405 ++ifl_data->ifl_duplicates;
1406 }
1407
1408 return false;
1409 }
1410
1411
1412 static PageNumber get_root_page(thread_db* tdbb, jrd_rel* relation)
1413 {
1414 /**************************************
1415 *
1416 * g e t _ r o o t _ p a g e
1417 *
1418 **************************************
1419 *
1420 * Functional description
1421 * Find the root page for a relation.
1422 *
1423 **************************************/
1424 SET_TDBB(tdbb);
1425
1426 RelationPages* relPages = relation->getPages(tdbb);
1427 SLONG page = relPages->rel_index_root;
1428 if (!page) {
1429 DPM_scan_pages(tdbb);
1430 page = relPages->rel_index_root;
1431 }
1432
1433 return PageNumber(relPages->rel_pg_space_id, page);
1434 }
1435
1436
1437 static int index_block_flush(void* ast_object)
1438 {
1439 /**************************************
1440 *
1441 * i n d e x _ b l o c k _ f l u s h
1442 *
1443 **************************************
1444 *
1445 * Functional description
1446 * An exclusive lock has been requested on the
1447 * index block. The information in the cached
1448 * index block is no longer valid, so clear it
1449 * out and release the lock.
1450 *
1451 **************************************/
1452 IndexBlock* index_block = static_cast<IndexBlock*>(ast_object);
1453 thread_db thd_context, *tdbb;
1454
1455 /* Since this routine will be called asynchronously, we must establish
1456 a thread context. */
1457
1458 JRD_set_thread_data(tdbb, thd_context);
1459
1460 Lock* lock = index_block->idb_lock;
1461
1462 if (lock->lck_attachment) {
1463 tdbb->tdbb_database = lock->lck_attachment->att_database;
1464 }
1465 tdbb->tdbb_attachment = lock->lck_attachment;
1466 tdbb->tdbb_quantum = QUANTUM;
1467 tdbb->tdbb_request = NULL;
1468 tdbb->tdbb_transaction = NULL;
1469
1470 /* release the index expression request, which also has
1471 the effect of releasing the expression tree */
1472
1473 if (index_block->idb_expression_request) {
1474 CMP_release(tdbb, index_block->idb_expression_request);
1475 }
1476
1477 index_block->idb_expression_request = NULL;
1478 index_block->idb_expression = NULL;
1479 MOVE_CLEAR(&index_block->idb_expression_desc, sizeof(struct dsc));
1480
1481 LCK_release(tdbb, lock);
1482
1483 /* Restore the prior thread context */
1484
1485 JRD_restore_thread_data();
1486
1487 return 0;
1488 }
1489
1490
1491 static IDX_E insert_key(
1492 thread_db* tdbb,
1493 jrd_rel* relation,
1494 Record* record,
1495 jrd_tra* transaction,
1496 WIN * window_ptr,
1497 index_insertion* insertion,
1498 jrd_rel** bad_relation,
1499 USHORT * bad_index)
1500 {
1501 /**************************************
1502 *
1503 * i n s e r t _ k e y
1504 *
1505 **************************************
1506 *
1507 * Functional description
1508 * Insert a key in the index.
1509 * If this is a unique index, check for active duplicates.
1510 * If this is a foreign key, check for duplicates in the
1511 * primary key index.
1512 *
1513 **************************************/
1514 SET_TDBB(tdbb);
1515
1516 IDX_E result = idx_e_ok;
1517 index_desc* idx = insertion->iib_descriptor;
1518
1519 /* Insert the key into the index. If the index is unique, btr
1520 will keep track of duplicates. */
1521
1522 insertion->iib_duplicates = NULL;
1523 BTR_insert(tdbb, window_ptr, insertion);
1524
1525 if (insertion->iib_duplicates) {
1526 result = check_duplicates(tdbb, record, idx, insertion, NULL);
1527 delete insertion->iib_duplicates;
1528 insertion->iib_duplicates = 0;
1529 }
1530
1531 if (result != idx_e_ok) {
1532 return result;
1533 }
1534
1535 /* if we are dealing with a foreign key index,
1536 check for an insert into the corresponding
1537 primary key index */
1538 if (idx->idx_flags & idx_foreign) {
1539 /* find out if there is a null segment by faking uniqueness --
1540 if there is one, don't bother to check the primary key */
1541
1542 idx->idx_flags |= idx_unique;
1543 CCH_FETCH(tdbb, window_ptr, LCK_read, pag_root);
1544 temporary_key key;
1545 idx_null_state null_state;
1546 result = BTR_key(tdbb, relation, record, idx, &key, &null_state, false);
1547 CCH_RELEASE(tdbb, window_ptr);
1548 idx->idx_flags &= ~idx_unique;
1549 if (null_state == idx_nulls_none) {
1550 result =
1551 check_foreign_key(tdbb, record, insertion->iib_relation,
1552 transaction, idx, bad_relation, bad_index);
1553 }
1554 }
1555
1556 return result;
1557 }
1558
1559
1560 static bool key_equal(const temporary_key* key1, const temporary_key* key2)
1561 {
1562 /**************************************
1563 *
1564 * k e y _ e q u a l
1565 *
1566 **************************************
1567 *
1568 * Functional description
1569 * Compare two keys for equality.
1570 *
1571 **************************************/
1572 USHORT l = key1->key_length;
1573 return (l == key2->key_length && !memcmp(key1->key_data, key2->key_data, l));
1574 }
1575
1576
1577 static void signal_index_deletion(thread_db* tdbb, jrd_rel* relation, USHORT id)
1578 {
1579 /**************************************
1580 *
1581 * s i g n a l _ i n d e x _ d e l e t i o n
1582 *
1583 **************************************
1584 *
1585 * Functional description
1586 * On delete of an index, force all
1587 * processes to get rid of index info.
1588 *
1589 **************************************/
1590 IndexBlock* index_block;
1591 Lock* lock = NULL;
1592
1593 SET_TDBB(tdbb);
1594
1595 /* get an exclusive lock on the associated index
1596 block (if it exists) to make sure that all other
1597 processes flush their cached information about
1598 this index */
1599
1600 for (index_block = relation->rel_index_blocks; index_block;
1601 index_block = index_block->idb_next)
1602 {
1603 if (index_block->idb_id == id) {
1604 lock = index_block->idb_lock;
1605 break;
1606 }
1607 }
1608
1609 /* if one didn't exist, create it */
1610
1611 if (!index_block) {
1612 index_block = IDX_create_index_block(tdbb, relation, id);
1613 lock = index_block->idb_lock;
1614 }
1615
1616 /* signal other processes to clear out the index block */
1617
1618 if (lock->lck_physical == LCK_SR) {
1619 LCK_convert_non_blocking(tdbb, lock, LCK_EX, LCK_WAIT);
1620 }
1621 else {
1622 LCK_lock_non_blocking(tdbb, lock, LCK_EX, LCK_WAIT);
1623 }
1624
1625 /* and clear out our index block as well */
1626
1627 index_block_flush(index_block);
1628 }

  ViewVC Help
Powered by ViewVC 1.1.5