8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 00:03:02 +01:00

Partial indices: DDL, ODS, dependencies, metadata

This commit is contained in:
Dmitry Yemanov 2022-04-12 11:34:21 +03:00
parent 8e641c4497
commit 02efe138cd
21 changed files with 441 additions and 83 deletions

View File

@ -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,

View File

@ -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<MetaName> 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<RelationSourceNode> relation;
NestConst<ValueListNode> columns;
NestConst<ValueSourceClause> computed;
NestConst<BoolSourceClause> partial;
};

View File

@ -1635,6 +1635,14 @@ unique_opt
%type index_definition(<createIndexNode>)
index_definition($createIndexNode)
: index_column_expr($createIndexNode) index_condition_opt
{
$createIndexNode->partial = $2;
}
;
%type index_column_expr(<createIndexNode>)
index_column_expr($createIndexNode)
: column_list
{ $createIndexNode->columns = $1; }
| column_parens
@ -1647,6 +1655,18 @@ index_definition($createIndexNode)
}
;
%type <boolSourceClause> index_condition_opt
index_condition_opt
: /* nothing */
{ $$ = nullptr; }
| WHERE search_condition
{
auto clause = newNode<BoolSourceClause>();
clause->value = $2;
clause->source = makeParseStr(YYPOSNARG(1), YYPOSNARG(2));
$$ = clause;
}
;
// CREATE SHADOW
%type <createShadowNode> shadow_clause

View File

@ -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<BoolExprNode*>(csb->csb_node);
});
}
Statement* Statement::makeValueExpression(thread_db* tdbb, ValueExprNode*& node, dsc& desc,
CompilerScratch* csb, bool internalFlag)
{

View File

@ -49,6 +49,9 @@ public:
static Statement* makeStatement(thread_db* tdbb, CompilerScratch* csb, bool internalFlag,
std::function<void ()> 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);

View File

@ -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);

View File

@ -66,9 +66,11 @@ struct index_desc
vec<int>* idx_foreign_primaries; // ids for primary/unique indexes with partners
vec<int>* idx_foreign_relations; // ids for foreign key partner relations
vec<int>* 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

View File

@ -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);

View File

@ -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)
{

View File

@ -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<UCHAR, 256> 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);
}

View File

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

View File

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

View File

@ -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)
{
/**************************************

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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<FieldNode>(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?

View File

@ -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)

View File

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

View File

@ -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)

View File

@ -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);