| 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 |
}
|