diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 05893c7686..e11d60e484 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -420,6 +420,30 @@ void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relati value.assign(dsqlScratch->getBlrData()); } +void definePartial(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relation, + BoolSourceClause* clause, BlrDebugWriter::BlrData& value) +{ + // Get the table node and set up correct context. + dsqlScratch->resetContextStack(); + + PASS1_make_context(dsqlScratch, relation); + + const auto input = Node::doDsqlPass(dsqlScratch, clause->value); + + // Generate the blr expression. + + dsqlScratch->getBlrData().clear(); + dsqlScratch->getDebugData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + + GEN_expr(dsqlScratch, input); + dsqlScratch->appendUChar(blr_eoc); + + dsqlScratch->resetContextStack(); + + value.assign(dsqlScratch->getBlrData()); +} + // Delete a record from RDB$RELATION_CONSTRAINTS based on a constraint name. // // On deleting from RDB$RELATION_CONSTRAINTS, 2 system triggers fire: @@ -9794,6 +9818,31 @@ void CreateIndexNode::store(thread_db* tdbb, jrd_tra* transaction, MetaName& nam IDX.RDB$SEGMENT_COUNT = SSHORT(definition.columns.getCount()); } END_STORE + + if (!definition.conditionBlr.isEmpty() || !definition.conditionSource.isEmpty()) + { + AutoRequest request; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + IDX IN RDB$INDICES + WITH IDX.RDB$INDEX_NAME EQ name.c_str() + { + MODIFY IDX + if (!definition.conditionBlr.isEmpty()) + { + IDX.RDB$CONDITION_BLR.NULL = FALSE; + IDX.RDB$CONDITION_BLR = definition.conditionBlr; + } + + if (!definition.conditionSource.isEmpty()) + { + IDX.RDB$CONDITION_SOURCE.NULL = FALSE; + IDX.RDB$CONDITION_SOURCE = definition.conditionSource; + } + END_MODIFY + } + END_FOR + } } @@ -9860,6 +9909,21 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, attachment->storeBinaryBlob(tdbb, transaction, &definition.expressionBlr, computedValue); } + if (partial) + { + const auto dbb = tdbb->getDatabase(); + if (dbb->getEncodedOdsVersion() < ODS_13_1) + ERR_post(Arg::Gds(isc_wish_list)); + + BlrDebugWriter::BlrData partialValue; + definePartial(dsqlScratch, relation, partial, partialValue); + + attachment->storeMetaDataBlob(tdbb, transaction, + &definition.conditionSource, partial->source); + attachment->storeBinaryBlob(tdbb, transaction, + &definition.conditionBlr, partialValue); + } + store(tdbb, transaction, name, definition); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_INDEX, diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index a6752333f2..cfb9dffce6 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1719,6 +1719,8 @@ public: { expressionBlr.clear(); expressionSource.clear(); + conditionBlr.clear(); + conditionSource.clear(); } MetaName relation; @@ -1729,6 +1731,8 @@ public: SSHORT type; bid expressionBlr; bid expressionSource; + bid conditionBlr; + bid conditionSource; MetaName refRelation; Firebird::ObjectsArray refColumns; }; @@ -1739,15 +1743,16 @@ public: name(p, aName), unique(false), descending(false), - relation(NULL), - columns(NULL), - computed(NULL) + relation(nullptr), + columns(nullptr), + computed(nullptr), + partial(nullptr) { } public: static void store(thread_db* tdbb, jrd_tra* transaction, MetaName& name, - const Definition& definition, MetaName* referredIndexName = NULL); + const Definition& definition, MetaName* referredIndexName = nullptr); public: virtual Firebird::string internalPrint(NodePrinter& printer) const; @@ -1767,6 +1772,7 @@ public: NestConst relation; NestConst columns; NestConst computed; + NestConst partial; }; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index e2e04f8295..2b39e654f9 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -1635,6 +1635,14 @@ unique_opt %type index_definition() index_definition($createIndexNode) + : index_column_expr($createIndexNode) index_condition_opt + { + $createIndexNode->partial = $2; + } + ; + +%type index_column_expr() +index_column_expr($createIndexNode) : column_list { $createIndexNode->columns = $1; } | column_parens @@ -1647,6 +1655,18 @@ index_definition($createIndexNode) } ; +%type index_condition_opt +index_condition_opt + : /* nothing */ + { $$ = nullptr; } + | WHERE search_condition + { + auto clause = newNode(); + clause->value = $2; + clause->source = makeParseStr(YYPOSNARG(1), YYPOSNARG(2)); + $$ = clause; + } + ; // CREATE SHADOW %type shadow_clause diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index be08c3dc15..98b13156b4 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -339,6 +339,18 @@ Statement* Statement::makeStatement(thread_db* tdbb, CompilerScratch* csb, bool return statement; } +Statement* Statement::makeBoolExpression(thread_db* tdbb, BoolExprNode*& node, + CompilerScratch* csb, bool internalFlag) +{ + fb_assert(csb->csb_node->getKind() == DmlNode::KIND_BOOLEAN); + + return makeStatement(tdbb, csb, internalFlag, + [&] + { + node = static_cast(csb->csb_node); + }); +} + Statement* Statement::makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc, CompilerScratch* csb, bool internalFlag) { diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index 2c8cb07661..c4f679c6af 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -49,6 +49,9 @@ public: static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag, std::function beforeCsbRelease = nullptr); + static Statement* makeBoolExpression(thread_db* tdbb, BoolExprNode*& node, + CompilerScratch* csb, bool internalFlag); + static Statement* makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc, CompilerScratch* csb, bool internalFlag); diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index c58218e38d..f80f250f37 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -509,13 +509,15 @@ bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, idx->idx_count = irt_desc->irt_keys; idx->idx_flags = irt_desc->irt_flags; idx->idx_runtime_flags = 0; - idx->idx_foreign_primaries = NULL; - idx->idx_foreign_relations = NULL; - idx->idx_foreign_indexes = NULL; + idx->idx_foreign_primaries = nullptr; + idx->idx_foreign_relations = nullptr; + idx->idx_foreign_indexes = nullptr; idx->idx_primary_relation = 0; idx->idx_primary_index = 0; - idx->idx_expression = NULL; - idx->idx_expression_statement = NULL; + idx->idx_expression = nullptr; + idx->idx_expression_statement = nullptr; + idx->idx_condition = nullptr; + idx->idx_condition_statement = nullptr; // pick up field ids and type descriptions for each of the fields const UCHAR* ptr = (UCHAR*) root + irt_desc->irt_desc; @@ -530,31 +532,95 @@ bool BTR_description(thread_db* tdbb, jrd_rel* relation, index_root_page* root, } idx->idx_selectivity = idx_desc->idx_selectivity; - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { MET_lookup_index_expression(tdbb, relation, idx); - fb_assert(idx->idx_expression != NULL); + fb_assert(idx->idx_expression); + } + + if (idx->idx_flags & idx_condition) + { + MET_lookup_index_condition(tdbb, relation, idx); + fb_assert(idx->idx_condition); } return true; } +bool BTR_check_condition(Jrd::thread_db* tdbb, Jrd::index_desc* idx, Jrd::Record* record) +{ + if (!(idx->idx_flags & idx_condition)) + return true; + + fb_assert(idx->idx_condition); + + Request* const orgRequest = tdbb->getRequest(); + Request* const conditionRequest = idx->idx_condition_statement->findRequest(tdbb); + + fb_assert(conditionRequest != orgRequest); + + fb_assert(!conditionRequest->req_caller); + conditionRequest->req_caller = orgRequest; + + conditionRequest->req_flags &= req_in_use; + conditionRequest->req_flags |= req_active; + TRA_attach_request(tdbb->getTransaction(), conditionRequest); + tdbb->setRequest(conditionRequest); + + fb_assert(conditionRequest->req_transaction); + + conditionRequest->req_rpb[0].rpb_record = record; + conditionRequest->req_rpb[0].rpb_number.setValue(BOF_NUMBER); + conditionRequest->req_rpb[0].rpb_number.setValid(true); + conditionRequest->req_flags &= ~req_null; + + FbLocalStatus status; + bool result = false; + + try + { + Jrd::ContextPoolHolder context(tdbb, conditionRequest->req_pool); + + if (orgRequest) + conditionRequest->setGmtTimeStamp(orgRequest->getGmtTimeStamp()); + else + conditionRequest->validateTimeStamp(); + + result = idx->idx_condition->execute(tdbb, conditionRequest); + } + catch (const Exception& ex) + { + ex.stuffException(&status); + } + + EXE_unwind(tdbb, conditionRequest); + conditionRequest->req_flags &= ~req_in_use; + conditionRequest->req_attachment = nullptr; + + tdbb->setRequest(orgRequest); + + status.check(); + + return result; +} + + DSC* BTR_eval_expression(thread_db* tdbb, index_desc* idx, Record* record, bool& notNull) { SET_TDBB(tdbb); - fb_assert(idx->idx_expression != NULL); + fb_assert(idx->idx_expression); - // check for resursive expression evaluation + // check for recursive expression evaluation Request* const org_request = tdbb->getRequest(); Request* const expr_request = idx->idx_expression_statement->findRequest(tdbb, true); - if (expr_request == NULL) + if (!expr_request) ERR_post(Arg::Gds(isc_random) << "Attempt to evaluate index expression recursively"); fb_assert(expr_request != org_request); - fb_assert(expr_request->req_caller == NULL); + fb_assert(!expr_request->req_caller); expr_request->req_caller = org_request; expr_request->req_flags &= req_in_use; @@ -1241,7 +1307,7 @@ idx_e BTR_key(thread_db* tdbb, jrd_rel* relation, Record* record, index_desc* id { bool isNull; // for expression indices, compute the value of the expression - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { bool notNull; desc_ptr = BTR_eval_expression(tdbb, idx, record, notNull); @@ -1437,9 +1503,9 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx) break; default: - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { - fb_assert(idx->idx_expression != NULL); + fb_assert(idx->idx_expression); length = idx->idx_expression_desc.dsc_length; if (idx->idx_expression_desc.dsc_dtype == dtype_varying) { @@ -1756,7 +1822,7 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke const index_desc::idx_repeat* tail = idx->idx_rpt; // If the index is a single segment index, don't sweat the compound stuff - if ((idx->idx_count == 1) || (idx->idx_flags & idx_expressn)) + if ((idx->idx_count == 1) || (idx->idx_flags & idx_expression)) { compress(tdbb, &null_desc, key, tail->idx_itype, true, descending, INTL_KEY_SORT); } @@ -6144,7 +6210,7 @@ string print_key(thread_db* tdbb, jrd_rel* relation, index_desc* idx, Record* re try { - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { bool notNull = false; const dsc* const desc = BTR_eval_expression(tdbb, idx, record, notNull); diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 6c5dd4728d..da7eb4f697 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -66,9 +66,11 @@ struct index_desc vec* idx_foreign_primaries; // ids for primary/unique indexes with partners vec* idx_foreign_relations; // ids for foreign key partner relations vec* idx_foreign_indexes; // ids for foreign key partner indexes - ValueExprNode* idx_expression; // node tree for indexed expresssion + ValueExprNode* idx_expression; // node tree for indexed expression dsc idx_expression_desc; // descriptor for expression result Statement* idx_expression_statement; // stored statement for expression evaluation + BoolExprNode* idx_condition; // node tree for index condition + Statement* idx_condition_statement; // stored statement for index condition // This structure should exactly match IRTD structure for current ODS struct idx_repeat { @@ -114,7 +116,8 @@ const int idx_descending = 2; const int idx_in_progress = 4; const int idx_foreign = 8; const int idx_primary = 16; -const int idx_expressn = 32; +const int idx_expression = 32; +const int idx_condition = 64; // these flags are for idx_runtime_flags diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index e33482ba34..3963416f8c 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -34,6 +34,7 @@ void BTR_complement_key(Jrd::temporary_key*); void BTR_create(Jrd::thread_db*, Jrd::IndexCreation&, Jrd::SelectivityList&); bool BTR_delete_index(Jrd::thread_db*, Jrd::win*, USHORT); bool BTR_description(Jrd::thread_db*, Jrd::jrd_rel*, Ods::index_root_page*, Jrd::index_desc*, USHORT); +bool BTR_check_condition(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*); DSC* BTR_eval_expression(Jrd::thread_db*, Jrd::index_desc*, Jrd::Record*, bool&); void BTR_evaluate(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::RecordBitmap**, Jrd::RecordBitmap*); UCHAR* BTR_find_leaf(Ods::btree_page*, Jrd::temporary_key*, UCHAR*, USHORT*, bool, bool); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 401fe9d10a..466db86f67 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1235,7 +1235,6 @@ static const deferred_task task_table[] = { dfw_add_file, add_file }, { dfw_add_shadow, add_shadow }, { dfw_delete_index, modify_index }, - { dfw_delete_expression_index, modify_index }, { dfw_delete_rfr, delete_rfr }, { dfw_delete_relation, delete_relation }, { dfw_delete_shadow, delete_shadow }, @@ -2744,7 +2743,8 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* { case 0: cleanup_index_creation(tdbb, work, transaction); - MET_delete_dependencies(tdbb, work->dfw_name, obj_expression_index, transaction); + MET_delete_dependencies(tdbb, work->dfw_name, obj_index_expression, transaction); + MET_delete_dependencies(tdbb, work->dfw_name, obj_index_condition, transaction); return false; case 1: @@ -2753,14 +2753,14 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* case 3: { - jrd_rel* relation = NULL; + jrd_rel* relation = nullptr; + CompilerScratch* csb = nullptr; + MemoryPool* new_pool = nullptr; + + const auto dbb = tdbb->getDatabase(); + const auto attachment = tdbb->getAttachment(); + index_desc idx; - MemoryPool* new_pool = NULL; - - SET_TDBB(tdbb); - Database* dbb = tdbb->getDatabase(); - Jrd::Attachment* attachment = tdbb->getAttachment(); - MOVE_CLEAR(&idx, sizeof(index_desc)); AutoCacheRequest request(tdbb, irq_c_exp_index, IRQ_REQUESTS); @@ -2774,9 +2774,8 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* if (!relation) { relation = MET_relation(tdbb, REL.RDB$RELATION_ID); - if (relation->rel_name.length() == 0) { + if (relation->rel_name.length() == 0) relation->rel_name = REL.RDB$RELATION_NAME; - } if (IDX.RDB$INDEX_ID && IDX.RDB$STATISTICS < 0.0) { @@ -2791,7 +2790,8 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* if (IDX.RDB$INDEX_ID) { IDX_delete_index(tdbb, relation, IDX.RDB$INDEX_ID - 1); - MET_delete_dependencies(tdbb, work->dfw_name, obj_expression_index, transaction); + MET_delete_dependencies(tdbb, work->dfw_name, obj_index_expression, transaction); + MET_delete_dependencies(tdbb, work->dfw_name, obj_index_condition, transaction); MODIFY IDX IDX.RDB$INDEX_ID.NULL = TRUE; END_MODIFY @@ -2811,35 +2811,30 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* if (IDX.RDB$INDEX_TYPE == 1) idx.idx_flags |= idx_descending; - CompilerScratch* csb = NULL; - // allocate a new pool to contain the expression tree for the expression index + MET_scan_relation(tdbb, relation); + + // Allocate a new pool to contain the expression tree + // for index expression / condition new_pool = attachment->createPool(); - { // scope - Jrd::ContextPoolHolder context(tdbb, new_pool); - MET_scan_relation(tdbb, relation); - if (!IDX.RDB$EXPRESSION_BLR.NULL) - { - MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR, - nullptr, &csb, work->dfw_name, obj_expression_index, 0, - transaction); + Jrd::ContextPoolHolder context(tdbb, new_pool); - idx.idx_expression_statement = Statement::makeValueExpression(tdbb, - idx.idx_expression, idx.idx_expression_desc, csb, false); - } - } // end scope + MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$EXPRESSION_BLR, + nullptr, &csb, work->dfw_name, obj_index_expression, 0, + transaction); + + idx.idx_expression_statement = Statement::makeValueExpression(tdbb, + idx.idx_expression, idx.idx_expression_desc, csb, false); // fake a description of the index idx.idx_count = 1; - idx.idx_flags |= idx_expressn; + idx.idx_flags |= idx_expression; idx.idx_rpt[0].idx_itype = DFW_assign_index_type(tdbb, work->dfw_name, idx.idx_expression_desc.dsc_dtype, idx.idx_expression_desc.dsc_sub_type); idx.idx_rpt[0].idx_selectivity = 0; - - delete csb; } } END_FOR @@ -2854,6 +2849,34 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* Arg::Gds(isc_idx_create_err) << Arg::Str(work->dfw_name)); } + idx.idx_condition = nullptr; + idx.idx_condition_statement = nullptr; + + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + { + AutoCacheRequest request(tdbb, irq_c_cond_index, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + IDX IN RDB$INDICES WITH + IDX.RDB$CONDITION_BLR NOT MISSING AND + IDX.RDB$INDEX_NAME EQ work->dfw_name.c_str() + { + Jrd::ContextPoolHolder context(tdbb, new_pool); + + MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR, + nullptr, &csb, work->dfw_name, obj_index_condition, 0, + transaction); + + idx.idx_condition_statement = Statement::makeBoolExpression(tdbb, + idx.idx_condition, csb, false); + + idx.idx_flags |= idx_condition; + } + END_FOR + } + + delete csb; + // Actually create the index // Protect relation from modification to create consistent index @@ -2878,8 +2901,10 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* tdbb->setTransaction(current_transaction); tdbb->setRequest(current_request); - // Get rid of the expression statement. + // Get rid of the expression/condition statements idx.idx_expression_statement->release(tdbb); + if (idx.idx_condition_statement) + idx.idx_condition_statement->release(tdbb); throw; } @@ -2889,8 +2914,10 @@ static bool create_expression_index(thread_db* tdbb, SSHORT phase, DeferredWork* DFW_update_index(work->dfw_name.c_str(), idx.idx_id, selectivity, transaction); - // Get rid of the expression statement. + // Get rid of the expression/condition statements idx.idx_expression_statement->release(tdbb); + if (idx.idx_condition_statement) + idx.idx_condition_statement->release(tdbb); } break; @@ -3294,7 +3321,6 @@ static bool modify_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ break; case dfw_delete_index : - case dfw_delete_expression_index : task_routine = delete_index; is_create = false; break; @@ -3590,6 +3616,39 @@ static bool create_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ // Msg308: can't create index %s } + idx.idx_condition = nullptr; + idx.idx_condition_statement = nullptr; + + if (dbb->getEncodedOdsVersion() >= ODS_13_1) + { + // Allocate a new pool to contain the expression tree + // for index condition + const auto new_pool = attachment->createPool(); + CompilerScratch* csb = nullptr; + + AutoCacheRequest request(tdbb, irq_c_cond_index, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + IDX IN RDB$INDICES WITH + IDX.RDB$CONDITION_BLR NOT MISSING AND + IDX.RDB$INDEX_NAME EQ work->dfw_name.c_str() + { + Jrd::ContextPoolHolder context(tdbb, new_pool); + + MET_get_dependencies(tdbb, relation, nullptr, 0, nullptr, &IDX.RDB$CONDITION_BLR, + nullptr, &csb, work->dfw_name, obj_index_condition, 0, + transaction); + + idx.idx_condition_statement = Statement::makeBoolExpression(tdbb, + idx.idx_condition, csb, false); + + idx.idx_flags |= idx_condition; + } + END_FOR + + delete csb; + } + // Actually create the index partner_relation = NULL; @@ -3660,6 +3719,9 @@ static bool create_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ fb_assert(work->dfw_id == idx.idx_id); DFW_update_index(work->dfw_name.c_str(), idx.idx_id, selectivity, transaction); + if (idx.idx_condition_statement) + idx.idx_condition_statement->release(tdbb); + if (partner_relation) { // signal to other processes about new constraint @@ -4707,10 +4769,8 @@ static bool delete_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ if (isTempIndex) return false; - if (work->dfw_type == dfw_delete_expression_index) - { - MET_delete_dependencies(tdbb, arg->dfw_name, obj_expression_index, transaction); - } + MET_delete_dependencies(tdbb, arg->dfw_name, obj_index_expression, transaction); + MET_delete_dependencies(tdbb, arg->dfw_name, obj_index_condition, transaction); // if index was bound to deleted FK constraint // then work->dfw_args was set in VIO_erase @@ -5309,8 +5369,9 @@ static bool find_depend_in_dfw(thread_db* tdbb, case obj_procedure: dfw_type = dfw_delete_procedure; break; - case obj_expression_index: - dfw_type = dfw_delete_expression_index; + case obj_index_expression: + case obj_index_condition: + dfw_type = dfw_delete_index; break; case obj_package_header: dfw_type = dfw_drop_package_header; @@ -5350,7 +5411,7 @@ static bool find_depend_in_dfw(thread_db* tdbb, } } - if (work->dfw_type == dfw_type && dfw_type == dfw_delete_expression_index) + if (work->dfw_type == dfw_type && dfw_type == dfw_delete_index) { for (FB_SIZE_T i = 0; i < work->dfw_args.getCount(); ++i) { diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index bc1fb80b15..6a96b6b4c6 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -618,6 +618,9 @@ bool IndexCreateTask::handler(WorkItem& _item) { Record* record = stack.pop(); + if (!BTR_check_condition(tdbb, idx, record)) + continue; + result = BTR_key(tdbb, relation, record, idx, &key, ((idx->idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); @@ -1155,6 +1158,9 @@ void IDX_garbage_collect(thread_db* tdbb, record_param* rpb, RecordStack& going, { Record* const rec1 = stack1.object(); + if (!BTR_check_condition(tdbb, &idx, rec1)) + continue; + idx_e result = BTR_key(tdbb, rpb->rpb_relation, rec1, &idx, &key1, ((idx.idx_flags & idx_unique) ? INTL_KEY_UNIQUE : INTL_KEY_SORT)); @@ -1269,6 +1275,9 @@ void IDX_modify(thread_db* tdbb, while (BTR_next_index(tdbb, org_rpb->rpb_relation, transaction, &idx, &window)) { + if (!BTR_check_condition(tdbb, &idx, new_rpb->rpb_record)) + continue; + IndexErrorContext context(new_rpb->rpb_relation, &idx); idx_e error_code; @@ -1345,6 +1354,8 @@ void IDX_modify_check_constraints(thread_db* tdbb, continue; } + fb_assert(!(idx.idx_flags & idx_condition)); + IndexErrorContext context(new_rpb->rpb_relation, &idx); idx_e error_code; @@ -1494,6 +1505,9 @@ void IDX_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) while (BTR_next_index(tdbb, rpb->rpb_relation, transaction, &idx, &window)) { + if (!BTR_check_condition(tdbb, &idx, rpb->rpb_record)) + continue; + IndexErrorContext context(rpb->rpb_relation, &idx); idx_e error_code; @@ -1532,11 +1546,11 @@ static bool cmpRecordKeys(thread_db* tdbb, HalfStaticArray tmp; DSC desc1, desc2; - if (idx2->idx_flags & idx_expressn) + if (idx2->idx_flags & idx_expression) { // Remove assertion below if\when expression index will participate in FK, // currently it is impossible. - fb_assert((idx1->idx_flags & idx_expressn) != 0); + fb_assert(idx1->idx_flags & idx_expression); bool flag_idx; const dsc* desc_idx = BTR_eval_expression(tdbb, idx2, rec2, flag_idx); @@ -1812,6 +1826,9 @@ static idx_e check_partner_index(thread_db* tdbb, BUGCHECK(175); // msg 175 partner index description not found } + fb_assert(!(idx->idx_flags & idx_condition)); + fb_assert(!(partner_idx.idx_flags & idx_condition)); + bool starting = false; USHORT segment; @@ -2063,11 +2080,19 @@ static void release_index_block(thread_db* tdbb, IndexBlock* index_block) * **************************************/ if (index_block->idb_expression_statement) + { index_block->idb_expression_statement->release(tdbb); + index_block->idb_expression_statement = nullptr; + } + index_block->idb_expression = nullptr; + index_block->idb_expression_desc.clear(); - index_block->idb_expression_statement = NULL; - index_block->idb_expression = NULL; - MOVE_CLEAR(&index_block->idb_expression_desc, sizeof(dsc)); + if (index_block->idb_condition_statement) + { + index_block->idb_condition_statement->release(tdbb); + index_block->idb_condition_statement = nullptr; + } + index_block->idb_condition = nullptr; LCK_release(tdbb, index_block->idb_lock); } diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 3c8e869209..fa624f6a12 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -85,6 +85,8 @@ enum irq_type_t irq_c_exp_index, // create expression index irq_l_exp_index, // lookup expression index irq_l_exp_index_blr, // lookup expression index BLR + irq_c_cond_index, // create condition index + irq_l_cond_index, // lookup condition index irq_l_rel_id, // lookup relation id irq_l_procedure, // lookup procedure name diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 8f5d7f52e0..f850528a73 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -312,6 +312,8 @@ public: ValueExprNode* idb_expression; // node tree for index expression Statement* idb_expression_statement; // statement for index expression evaluation dsc idb_expression_desc; // descriptor for expression result + BoolExprNode* idb_condition; // node tree for index condition + Statement* idb_condition_statement; // statement for index condition evaluation Lock* idb_lock; // lock to synchronize changes to index USHORT idb_id; }; diff --git a/src/jrd/met.epp b/src/jrd/met.epp index edbd0170d1..aaa73f5ad4 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -2560,6 +2560,98 @@ SLONG MET_lookup_index_name(thread_db* tdbb, } +void MET_lookup_index_condition(thread_db* tdbb, jrd_rel* relation, index_desc* idx) +{ +/************************************** +* +* M E T _ l o o k u p _ i n d e x _ c o n d i t i o n +* +************************************** +* +* Functional description +* Lookup information about an index, in +* the metadata cache if possible. +* +**************************************/ + SET_TDBB(tdbb); + const auto attachment = tdbb->getAttachment(); + + // Check the index blocks for the relation to see if we have a cached block + + IndexBlock* index_block; + for (index_block = relation->rel_index_blocks; index_block; index_block = index_block->idb_next) + { + if (index_block->idb_id == idx->idx_id) + break; + } + + if (index_block && index_block->idb_condition) + { + idx->idx_condition = index_block->idb_condition; + idx->idx_condition_statement = index_block->idb_condition_statement; + return; + } + + const auto dbb = tdbb->getDatabase(); + if (dbb->getEncodedOdsVersion() < ODS_13_1) + return; + + if (!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)) + MET_scan_relation(tdbb, relation); + + CompilerScratch* csb = nullptr; + AutoCacheRequest request(tdbb, irq_l_cond_index, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + IDX IN RDB$INDICES WITH + IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND + IDX.RDB$INDEX_ID EQ idx->idx_id + 1 + { + if (idx->idx_condition_statement) + { + idx->idx_condition_statement->release(tdbb); + idx->idx_condition_statement = nullptr; + } + + // Parse the blr, making sure to create the resulting expression + // tree and request in its own pool so that it may be cached + // with the index block in the "permanent" metadata cache + + { // scope + Jrd::ContextPoolHolder context(tdbb, attachment->createPool()); + + MET_parse_blob(tdbb, relation, &IDX.RDB$CONDITION_BLR, &csb, nullptr, false, false); + + idx->idx_condition_statement = Statement::makeBoolExpression(tdbb, + idx->idx_condition, csb, false); + } // end scope + } + END_FOR + + delete csb; + + // If there is no existing index block for this index, create + // one and link it in with the index blocks for this relation + + if (!index_block) + index_block = IDX_create_index_block(tdbb, relation, idx->idx_id); + + // If we can't get the lock, no big deal: just give up on caching the index info + + if (!LCK_lock(tdbb, index_block->idb_lock, LCK_SR, LCK_NO_WAIT)) + { + // clear lock error from status vector + fb_utils::init_status(tdbb->tdbb_status_vector); + return; + } + + // Fill in the cached information about the index + + index_block->idb_condition = idx->idx_condition; + index_block->idb_condition_statement = idx->idx_condition_statement; +} + + void MET_lookup_index_expression(thread_db* tdbb, jrd_rel* relation, index_desc* idx) { /************************************** diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index a0193df9f2..73b0925a86 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -107,6 +107,7 @@ SLONG MET_lookup_generator(Jrd::thread_db*, const Jrd::MetaName&, bool* sysGen bool MET_lookup_generator_id(Jrd::thread_db*, SLONG, Jrd::MetaName&, bool* sysGen = 0); void MET_update_generator_increment(Jrd::thread_db* tdbb, SLONG gen_id, SLONG step); void MET_lookup_index(Jrd::thread_db*, Jrd::MetaName&, const Jrd::MetaName&, USHORT); +void MET_lookup_index_condition(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); void MET_lookup_index_expression(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); bool MET_lookup_index_expression_blr(Jrd::thread_db* tdbb, const Jrd::MetaName& index_name, Jrd::bid& blob_id); SLONG MET_lookup_index_name(Jrd::thread_db*, const Jrd::MetaName&, SLONG*, Jrd::IndexStatus* status); diff --git a/src/jrd/names.h b/src/jrd/names.h index 1c81cafc3c..b7634ae10e 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -44,6 +44,8 @@ NAME("RDB$COLLATION_NAME", nam_collate_name) NAME("RDB$COMPLEX_NAME", nam_cpx_name) NAME("RDB$COMPUTED_BLR", nam_computed) NAME("RDB$COMPUTED_SOURCE", nam_c_source) +NAME("RDB$CONDITION_BLR", nam_cond_blr) +NAME("RDB$CONDITION_SOURCE", nam_cond_source) NAME("RDB$CONSTRAINT_NAME", nam_con_name) NAME("RDB$CONSTRAINT_TYPE", nam_con_type) NAME("RDB$CONST_NAME_UQ", nam_con_uq) diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 887dfd9a56..0bfd7bd55d 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -36,7 +36,7 @@ const ObjectType obj_trigger = 2; const ObjectType obj_computed = 3; const ObjectType obj_validation = 4; const ObjectType obj_procedure = 5; -const ObjectType obj_expression_index = 6; +const ObjectType obj_index_expression = 6; const ObjectType obj_exception = 7; const ObjectType obj_user = 8; const ObjectType obj_field = 9; @@ -72,8 +72,9 @@ const ObjectType obj_filters = 33; const ObjectType obj_jobs = 34; const ObjectType obj_tablespace = 35; const ObjectType obj_tablespaces = 36; +const ObjectType obj_index_condition = 37; -const ObjectType obj_type_MAX = 37; +const ObjectType obj_type_MAX = 38; // used in the parser only / no relation with obj_type_MAX (should be greater) const ObjectType obj_user_or_role= 100; diff --git a/src/jrd/optimizer/Retrieval.cpp b/src/jrd/optimizer/Retrieval.cpp index 1755e8f1c4..85055911fd 100644 --- a/src/jrd/optimizer/Retrieval.cpp +++ b/src/jrd/optimizer/Retrieval.cpp @@ -435,7 +435,7 @@ void Retrieval::analyzeNavigation(const InversionCandidateList& inversions) // only a single-column ORDER BY clause can be mapped to // an expression index - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { if (sort->expressions.getCount() != 1) continue; @@ -498,7 +498,7 @@ void Retrieval::analyzeNavigation(const InversionCandidateList& inversions) for (const auto node : nodes) { - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { if (!checkExpressionIndex(csb, idx, node, stream)) continue; @@ -1477,7 +1477,7 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch, const auto idx = indexScratch->index; - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { // see if one side or the other is matchable to the index expression @@ -1553,7 +1553,7 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch, const auto fieldNode = nodeAs(match); - if (!(idx->idx_flags & idx_expressn)) + if (!(idx->idx_flags & idx_expression)) fb_assert(fieldNode); const bool isDesc = (idx->idx_flags & idx_descending); @@ -1565,7 +1565,7 @@ bool Retrieval::matchBoolean(IndexScratch* indexScratch, for (unsigned i = 0; i < idx->idx_count; i++) { - if (!(idx->idx_flags & idx_expressn) && + if (!(idx->idx_flags & idx_expression) && fieldNode->fieldId != idx->idx_rpt[i].idx_field) { continue; @@ -2106,7 +2106,7 @@ bool Retrieval::validateStarts(IndexScratch* indexScratch, const auto idx = indexScratch->index; - if (idx->idx_flags & idx_expressn) + if (idx->idx_flags & idx_expression) { // AB: What if the expression contains a number/float etc.. and // we use starting with against it? Is that allowed? diff --git a/src/jrd/relations.h b/src/jrd/relations.h index d9e1b03110..ce43141663 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -99,6 +99,8 @@ RELATION(nam_indices, rel_indices, ODS_8_0, rel_persistent) FIELD(f_idx_exp_blr, nam_exp_blr, fld_value, 1, ODS_8_0) FIELD(f_idx_exp_source, nam_exp_source, fld_source, 1, ODS_8_0) FIELD(f_idx_statistics, nam_statistics, fld_statistics, 1, ODS_8_0) + FIELD(f_idx_cond_blr, nam_cond_blr, fld_value, 1, ODS_13_1) + FIELD(f_idx_cond_source, nam_cond_source, fld_source, 1, ODS_13_1) END_RELATION // Relation 5 (RDB$RELATION_FIELDS) diff --git a/src/jrd/tra.h b/src/jrd/tra.h index ec9ca7ba5a..76cf8b2f45 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -490,7 +490,6 @@ enum dfw_t { dfw_revoke, dfw_scan_relation, dfw_create_expression_index, - dfw_delete_expression_index, dfw_create_procedure, dfw_modify_procedure, dfw_delete_procedure, @@ -517,7 +516,7 @@ enum dfw_t { dfw_change_repl_state, // deferred works argument types - dfw_arg_index_name, // index name for dfw_delete_expression_index, mandatory + dfw_arg_index_name, // index name for dfw_delete_index, mandatory dfw_arg_partner_rel_id, // partner relation id for dfw_delete_index if index is FK, optional dfw_arg_proc_name, // procedure name for dfw_delete_prm, mandatory dfw_arg_force_computed, // we need to drop dependencies from a field that WAS computed diff --git a/src/jrd/types.h b/src/jrd/types.h index f65d684e6a..4d8956f18f 100644 --- a/src/jrd/types.h +++ b/src/jrd/types.h @@ -86,7 +86,7 @@ TYPE("TRIGGER", obj_trigger, nam_obj_type) TYPE("COMPUTED_FIELD", obj_computed, nam_obj_type) TYPE("VALIDATION", obj_validation, nam_obj_type) TYPE("PROCEDURE", obj_procedure, nam_obj_type) -TYPE("EXPRESSION_INDEX", obj_expression_index, nam_obj_type) +TYPE("INDEX_EXPRESSION", obj_index_expression, nam_obj_type) TYPE("EXCEPTION", obj_exception, nam_obj_type) TYPE("USER", obj_user, nam_obj_type) TYPE("FIELD", obj_field, nam_obj_type) @@ -100,6 +100,7 @@ TYPE("BLOB_FILTER", obj_blob_filter, nam_obj_type) TYPE("COLLATION", obj_collation, nam_obj_type) TYPE("PACKAGE", obj_package_header, nam_obj_type) TYPE("PACKAGE BODY", obj_package_body, nam_obj_type) +TYPE("INDEX_CONDITION", obj_index_condition, nam_obj_type) TYPE("LIMBO", 1, nam_trans_state) TYPE("COMMITTED", 2, nam_trans_state) diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 4e00bb45b8..6a9b91dcd8 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2088,12 +2088,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) // hvlad: lets add index name to the DFW item even if we add it again later within // additional argument. This is needed to make DFW work items different for different // indexes dropped at the same transaction and to not merge them at DFW_merge_work. - if (EVL_field(0, rpb->rpb_record, f_idx_exp_blr, &desc2)) { - work = DFW_post_work(transaction, dfw_delete_expression_index, &idx_name, r2->rel_id); - } - else { - work = DFW_post_work(transaction, dfw_delete_index, &idx_name, r2->rel_id); - } + work = DFW_post_work(transaction, dfw_delete_index, &idx_name, r2->rel_id); // add index id and name (the latter is required to delete dependencies correctly) DFW_post_work_arg(transaction, work, &idx_name, id, dfw_arg_index_name);