Skip to content

Commit 8b1cb44

Browse files
committed
CBL-6285: Explain for predictive query no longer names an index
Update the default table name from main source in the query expression. When we create the query object from the database, with, for example, SQLiteDataFile::compileQuery, the defaultTableName of QueryTranslator is initialized with the table name of the default collection. This table is assumed to be the main data source of the query expression. Therefore, we need to update it as we parse the first item in the FROM clause, before PredictionNode is created.
1 parent c3ecb46 commit 8b1cb44

File tree

5 files changed

+27
-13
lines changed

5 files changed

+27
-13
lines changed

Diff for: LiteCore/Query/Translator/Node.hh

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ namespace litecore::qt {
7676
#ifdef COUCHBASE_ENTERPRISE
7777
std::function<bool(string_view id)> hasPredictiveIndex;
7878
#endif
79+
std::function<void(SourceNode*)> updateDefaultTableName;
7980
};
8081

8182
/** State used during parsing, passed down through the recursive descent. */

Diff for: LiteCore/Query/Translator/QueryTranslator.cc

+20-11
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ namespace litecore {
4444
return _delegate.tableExists(indexTable);
4545
};
4646
#endif
47+
root.updateDefaultTableName = [&](SourceNode* source) -> void {
48+
_defaultTableName = tableNameForCollectionSource(source);
49+
};
4750
return root;
4851
}
4952

@@ -112,6 +115,21 @@ namespace litecore {
112115
}
113116
}
114117

118+
string QueryTranslator::tableNameForCollectionSource(SourceNode* source) const {
119+
string name(source->collection());
120+
if ( name.empty() ) name = _defaultCollectionName;
121+
if ( !source->scope().empty() ) name = string(source->scope()) + "." + name;
122+
123+
DeletionStatus delStatus = source->usesDeletedDocs() ? kLiveAndDeletedDocs : kLiveDocs;
124+
//FIXME: Support kDeletedDocs
125+
126+
string tableName = _delegate.collectionTableName(name, delStatus);
127+
if ( name != _defaultCollectionName && !_delegate.tableExists(tableName) )
128+
fail("no such collection \"%s\"", name.c_str());
129+
130+
return tableName;
131+
}
132+
115133
string QueryTranslator::tableNameForSource(SourceNode* source, ParseContext& ctx) {
116134
string tableName(source->tableName());
117135
if ( !tableName.empty() ) return tableName;
@@ -129,17 +147,7 @@ namespace litecore {
129147
_hashedTables.emplace(tableName, plainTableName);
130148
if ( _delegate.tableExists(tableName) ) source->setTableName(ctx.newString(tableName));
131149
} else {
132-
string name(source->collection());
133-
if ( name.empty() ) name = _defaultCollectionName;
134-
if ( !source->scope().empty() ) name = string(source->scope()) + "." + name;
135-
136-
DeletionStatus delStatus = source->usesDeletedDocs() ? kLiveAndDeletedDocs : kLiveDocs;
137-
//FIXME: Support kDeletedDocs
138-
139-
tableName = _delegate.collectionTableName(name, delStatus);
140-
if ( name != _defaultCollectionName && !_delegate.tableExists(tableName) )
141-
fail("no such collection \"%s\"", name.c_str());
142-
150+
tableName = tableNameForCollectionSource(source);
143151
if ( auto index = dynamic_cast<IndexSourceNode*>(source) ) {
144152
switch ( index->indexType() ) {
145153
case IndexType::FTS:
@@ -164,6 +172,7 @@ namespace litecore {
164172
#endif
165173
}
166174
} else if ( source->isCollection() ) {
175+
DeletionStatus delStatus = source->usesDeletedDocs() ? kLiveAndDeletedDocs : kLiveDocs;
167176
if ( delStatus != kLiveAndDeletedDocs ) // that mode uses a fake union table
168177
_kvTables.insert(tableName);
169178
}

Diff for: LiteCore/Query/Translator/QueryTranslator.hh

+2-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ namespace litecore {
125125
private:
126126
QueryTranslator(const QueryTranslator& qp) = delete;
127127
QueryTranslator& operator=(const QueryTranslator&) = delete;
128+
string tableNameForCollectionSource(qt::SourceNode*) const;
128129
string tableNameForSource(qt::SourceNode*, qt::ParseContext&);
129130
void assignTableNameToSource(qt::SourceNode*, qt::ParseContext&);
130131
string writeSQL(function_ref<void(qt::SQLWriter&)>);
@@ -133,7 +134,7 @@ namespace litecore {
133134
qt::RootContext makeRootContext() const;
134135

135136
const Delegate& _delegate; // delegate object (SQLiteKeyStore)
136-
string _defaultTableName; // Name of the default table to use
137+
mutable string _defaultTableName; // Name of the default table to use
137138
string _defaultCollectionName; // Name of the default collection to use
138139
string _sql; // The generated SQL
139140
std::set<string> _parameters; // Plug-in "$" parameters found in parsing

Diff for: LiteCore/Query/Translator/SelectNodes.cc

+1
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ namespace litecore::qt {
481481
isFrom = true;
482482
require(_sources.empty(), "multiple non-join FROM items");
483483
ctx.from = source;
484+
ctx.delegate.updateDefaultTableName(source);
484485
}
485486
ctx.sources.emplace_back(source);
486487
}

Diff for: LiteCore/tests/PredictiveQueryTest.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//
1212

1313
#include "QueryTest.hh"
14+
#include "c4QueryTypes.h"
1415
#include "PredictiveModel.hh"
1516
#include <cmath>
1617

@@ -206,10 +207,11 @@ N_WAY_TEST_CASE_METHOD(QueryTest, "Predictive Query compound indexed", "[Query][
206207
// Query numbers in descending order of square-ness:
207208
std::stringstream sstr;
208209
sstr << "{'WHAT': [['.num'], " << square << "],"
210+
<< " 'FROM': [{'COLLECTION': '" + collectionName + "'}],"
209211
<< " 'WHERE': ['AND', ['>=', " << square + ", 1], ['>=', " << even << ", 1]],"
210212
<< " 'ORDER_BY': [['DESC', ['.num']]] }";
211213

212-
Retained<Query> query{store->compileQuery(json5(sstr.str()))};
214+
Retained<Query> query{db->compileQuery(json5(sstr.str()))};
213215
string explanation = query->explain();
214216
Log("Explanation: %s", explanation.c_str());
215217

0 commit comments

Comments
 (0)