1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "query_object.h"
17
18 #include "db_common.h"
19 #include "db_errno.h"
20 #include "get_query_info.h"
21 #include "log_print.h"
22
23 namespace DistributedDB {
24 namespace {
25 const int INVALID_LIMIT = INT_MAX;
26 const int LIMIT_FIELD_VALUE_SIZE = 2;
27 }
28
QueryObject()29 QueryObject::QueryObject()
30 : isValid_(true),
31 initialized_(false),
32 limit_(INVALID_LIMIT),
33 offset_(0),
34 hasOrderBy_(false),
35 hasLimit_(false),
36 hasPrefixKey_(false),
37 hasInKeys_(false),
38 orderByCounts_(0),
39 isUseLocalSchema_(true)
40 {
41 }
42
SetAttrWithQueryObjNodes()43 void QueryObject::SetAttrWithQueryObjNodes()
44 {
45 for (const auto &iter : queryObjNodes_) {
46 SymbolType symbolType = SqliteQueryHelper::GetSymbolType(iter.operFlag);
47 if (iter.operFlag == QueryObjType::LIMIT) {
48 hasLimit_ = true;
49 if (iter.fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) {
50 limit_ = iter.fieldValue[0].integerValue;
51 offset_ = iter.fieldValue[1].integerValue;
52 }
53 } else if (iter.operFlag == QueryObjType::ORDERBY) {
54 hasOrderBy_ = true;
55 } else if (symbolType == SymbolType::PREFIXKEY_SYMBOL) {
56 hasPrefixKey_ = true;
57 } else if (symbolType == SymbolType::IN_KEYS_SYMBOL) {
58 hasInKeys_ = true;
59 }
60 }
61 }
62
QueryObject(const Query & query)63 QueryObject::QueryObject(const Query &query)
64 : QueryObject(GetQueryInfo::GetQueryExpression(query))
65 {
66 }
67
QueryObject(const QueryExpression & queryExpression)68 QueryObject::QueryObject(const QueryExpression &queryExpression)
69 : initialized_(false),
70 limit_(INVALID_LIMIT),
71 offset_(0),
72 hasOrderBy_(false),
73 hasLimit_(false),
74 hasPrefixKey_(false),
75 hasInKeys_(false),
76 orderByCounts_(0),
77 isUseLocalSchema_(true)
78 {
79 QueryExpression queryExpressions = queryExpression;
80 queryObjNodes_ = queryExpressions.GetQueryExpression();
81 SetAttrWithQueryObjNodes();
82 isValid_ = queryExpressions.GetErrFlag();
83 prefixKey_ = queryExpressions.GetPreFixKey();
84 suggestIndex_ = queryExpressions.GetSuggestIndex();
85 tableName_ = queryExpressions.GetTableName();
86 isTableNameSpecified_ = queryExpressions.IsTableNameSpecified();
87 keys_ = queryExpressions.GetKeys();
88 sortType_ = static_cast<SortType>(queryExpressions.GetSortType());
89 tables_ = queryExpressions.GetTables();
90 validStatus = queryExpressions.GetExpressionStatus();
91 isAssetsOnly_ = queryExpressions.IsAssetsOnly();
92 groupNum_ = queryExpressions.GetGroupNum();
93 assetsGroupMap_ = queryExpressions.GetAssetsOnlyGroupMap();
94 assetsOnlyErrFlag_ = queryExpressions.GetExpressionStatusForAssetsOnly();
95 isUseFromTables_ = queryExpressions.IsUseFromTables();
96 }
97
QueryObject(const std::list<QueryObjNode> & queryObjNodes,const std::vector<uint8_t> & prefixKey,const std::set<Key> & keys)98 QueryObject::QueryObject(const std::list<QueryObjNode> &queryObjNodes, const std::vector<uint8_t> &prefixKey,
99 const std::set<Key> &keys)
100 : queryObjNodes_(queryObjNodes),
101 prefixKey_(prefixKey),
102 keys_(keys),
103 isValid_(true),
104 initialized_(false),
105 limit_(INVALID_LIMIT),
106 offset_(0),
107 hasOrderBy_(false),
108 hasLimit_(false),
109 hasPrefixKey_(false),
110 hasInKeys_(false),
111 orderByCounts_(0),
112 isUseLocalSchema_(true)
113 {
114 SetAttrWithQueryObjNodes();
115 }
116
~QueryObject()117 QueryObject::~QueryObject()
118 {}
119
Init()120 int QueryObject::Init()
121 {
122 if (initialized_) {
123 return E_OK;
124 }
125
126 int errCode = Parse();
127 if (errCode != E_OK) {
128 LOGE("Parse query object err[%d]!", errCode);
129 return errCode;
130 }
131
132 initialized_ = true;
133 return errCode;
134 }
135
GetQueryHelper(int & errCode)136 SqliteQueryHelper QueryObject::GetQueryHelper(int &errCode)
137 {
138 errCode = Init();
139 if (errCode != E_OK) {
140 return SqliteQueryHelper(QueryObjInfo{});
141 }
142 QueryObjInfo info {schema_, queryObjNodes_, prefixKey_, suggestIndex_, keys_,
143 orderByCounts_, isValid_, hasOrderBy_, hasLimit_, hasPrefixKey_, tableName_, isTableNameSpecified_, sortType_};
144 return SqliteQueryHelper {info}; // compiler RVO by default, and RVO is generally required after C++17
145 }
146
IsValid()147 bool QueryObject::IsValid()
148 {
149 if (!initialized_) {
150 (void)Init();
151 }
152 return isValid_;
153 }
154
HasLimit() const155 bool QueryObject::HasLimit() const
156 {
157 return hasLimit_;
158 }
159
GetLimitVal(int & limit,int & offset) const160 void QueryObject::GetLimitVal(int &limit, int &offset) const
161 {
162 limit = limit_;
163 offset = offset_;
164 }
165
SetSchema(const SchemaObject & schema)166 void QueryObject::SetSchema(const SchemaObject &schema)
167 {
168 schema_ = schema;
169 }
170
IsCountValid() const171 bool QueryObject::IsCountValid() const
172 {
173 if (hasLimit_ || hasOrderBy_) {
174 LOGI("It is invalid for limit and orderby!");
175 return false;
176 }
177 return true;
178 }
179
GetPrefixKey() const180 const std::vector<uint8_t> &QueryObject::GetPrefixKey() const
181 {
182 return prefixKey_;
183 }
184
ClearNodesFlag()185 void QueryObject::ClearNodesFlag()
186 {
187 limit_ = INVALID_LIMIT;
188 offset_ = 0;
189 isValid_ = true;
190 hasOrderBy_ = false;
191 hasLimit_ = false;
192 hasPrefixKey_ = false;
193 hasInKeys_ = false;
194 orderByCounts_ = 0;
195 }
196
Parse()197 int QueryObject::Parse()
198 {
199 if (!isValid_) {
200 LOGE("Invalid query object!");
201 return -E_INVALID_QUERY_FORMAT;
202 }
203 int errCode = ParseQueryObjNodes();
204 if (errCode != E_OK) {
205 LOGE("Check query object illegal!");
206 isValid_ = false;
207 }
208 return errCode;
209 }
210
ParseQueryObjNodes()211 int QueryObject::ParseQueryObjNodes()
212 {
213 ClearNodesFlag();
214
215 auto iter = queryObjNodes_.begin();
216 int errCode = E_OK;
217 while (iter != queryObjNodes_.end()) {
218 errCode = ParseNode(iter);
219 if (errCode != E_OK) {
220 return errCode;
221 }
222 iter++;
223 }
224 return errCode;
225 }
226
ParseNode(const std::list<QueryObjNode>::iterator & iter)227 int QueryObject::ParseNode(const std::list<QueryObjNode>::iterator &iter)
228 {
229 // The object is newly instantiated in the connection, and there is no reentrancy problem.
230 if (!iter->IsValid()) {
231 return -E_INVALID_QUERY_FORMAT;
232 }
233
234 switch (SqliteQueryHelper::GetSymbolType(iter->operFlag)) {
235 case SymbolType::COMPARE_SYMBOL:
236 case SymbolType::RELATIONAL_SYMBOL:
237 case SymbolType::RANGE_SYMBOL:
238 return CheckEqualFormat(iter);
239 case SymbolType::LINK_SYMBOL:
240 return CheckLinkerFormat(iter);
241 case SymbolType::PREFIXKEY_SYMBOL: {
242 if (hasPrefixKey_) {
243 LOGE("Only filter by prefix key once!!");
244 return -E_INVALID_QUERY_FORMAT;
245 }
246 hasPrefixKey_ = true;
247 if (prefixKey_.size() > DBConstant::MAX_KEY_SIZE) {
248 return -E_INVALID_ARGS;
249 }
250 return E_OK;
251 }
252 case SymbolType::SUGGEST_INDEX_SYMBOL:
253 return CheckSuggestIndexFormat(iter);
254 case SymbolType::IN_KEYS_SYMBOL: {
255 if (hasInKeys_) {
256 LOGE("Only filter by keys in once!!");
257 return -E_INVALID_QUERY_FORMAT;
258 }
259 int errCode = CheckInKeys();
260 if (errCode != E_OK) {
261 return errCode;
262 }
263 hasInKeys_ = true;
264 return E_OK;
265 }
266 default:
267 return ParseNodeByOperFlag(iter);
268 }
269 return E_OK;
270 }
271
ParseNodeByOperFlag(const std::list<QueryObjNode>::iterator & iter)272 int QueryObject::ParseNodeByOperFlag(const std::list<QueryObjNode>::iterator &iter)
273 {
274 switch (iter->operFlag) {
275 case QueryObjType::LIMIT:
276 hasLimit_ = true;
277 if (iter->fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) {
278 limit_ = iter->fieldValue[0].integerValue;
279 offset_ = iter->fieldValue[1].integerValue;
280 }
281 return CheckLimitFormat(iter);
282 case QueryObjType::ORDERBY:
283 return CheckOrderByFormat(iter);
284 default:
285 return E_OK;
286 }
287 }
288
CheckLinkerBefore(const std::list<QueryObjNode>::iterator & iter) const289 int QueryObject::CheckLinkerBefore(const std::list<QueryObjNode>::iterator &iter) const
290 {
291 auto preIter = std::prev(iter, 1);
292 SymbolType symbolType = SqliteQueryHelper::GetSymbolType(preIter->operFlag);
293 if (symbolType != SymbolType::COMPARE_SYMBOL && symbolType != SymbolType::RELATIONAL_SYMBOL &&
294 symbolType != SymbolType::LOGIC_SYMBOL && symbolType != SymbolType::RANGE_SYMBOL &&
295 symbolType != SymbolType::PREFIXKEY_SYMBOL && symbolType != SymbolType::IN_KEYS_SYMBOL) {
296 LOGE("Must be a comparison operation before the connective! operFlag = %s", VNAME(preIter->operFlag));
297 return -E_INVALID_QUERY_FORMAT;
298 }
299 return E_OK;
300 }
301
CheckEqualFormat(const std::list<QueryObjNode>::iterator & iter) const302 int QueryObject::CheckEqualFormat(const std::list<QueryObjNode>::iterator &iter) const
303 {
304 if (!schema_.IsSchemaValid()) {
305 LOGE("Schema is invalid!");
306 return -E_NOT_SUPPORT;
307 }
308
309 // use lower case in relational schema
310 std::string inPathString = isTableNameSpecified_ ? DBCommon::ToLowerCase(iter->fieldName) : iter->fieldName;
311
312 FieldPath fieldPath;
313 int errCode = SchemaUtils::ParseAndCheckFieldPath(inPathString, fieldPath);
314 if (errCode != E_OK) {
315 return -E_INVALID_QUERY_FIELD;
316 }
317
318 FieldType schemaFieldType = FieldType::LEAF_FIELD_BOOL;
319 errCode = schema_.CheckQueryableAndGetFieldType(fieldPath, schemaFieldType);
320 if (errCode != E_OK) {
321 LOGE("Get field type fail when check compare format! errCode = %d, fieldType = %u",
322 errCode, static_cast<unsigned>(schemaFieldType));
323 return -E_INVALID_QUERY_FIELD;
324 }
325
326 if (schemaFieldType == FieldType::LEAF_FIELD_BOOL &&
327 SqliteQueryHelper::GetSymbolType(iter->operFlag) == SymbolType::COMPARE_SYMBOL &&
328 iter->operFlag != QueryObjType::EQUALTO && iter->operFlag != QueryObjType::NOT_EQUALTO) { // bool can == or !=
329 LOGE("Bool forbid compare!!!");
330 return -E_INVALID_QUERY_FORMAT;
331 }
332 auto nextIter = std::next(iter, 1);
333 if (nextIter != queryObjNodes_.end()) {
334 SymbolType symbolType = SqliteQueryHelper::GetSymbolType(nextIter->operFlag);
335 if (symbolType == SymbolType::RELATIONAL_SYMBOL || symbolType == SymbolType::COMPARE_SYMBOL ||
336 symbolType == SymbolType::RANGE_SYMBOL) {
337 LOGE("After Compare you need, You need the conjunction like and or for connecting!");
338 return -E_INVALID_QUERY_FORMAT;
339 }
340 }
341 return E_OK;
342 }
343
CheckLinkerFormat(const std::list<QueryObjNode>::iterator & iter) const344 int QueryObject::CheckLinkerFormat(const std::list<QueryObjNode>::iterator &iter) const
345 {
346 auto itPre = iter;
347 for (; itPre != queryObjNodes_.begin(); itPre = std::prev(itPre, 1)) {
348 SymbolType symbolType = SqliteQueryHelper::GetSymbolType(std::prev(itPre, 1)->operFlag);
349 if (symbolType != SymbolType::PREFIXKEY_SYMBOL && symbolType != SymbolType::IN_KEYS_SYMBOL) {
350 break;
351 }
352 }
353 if (itPre == queryObjNodes_.begin()) {
354 LOGE("Connectives are not allowed in the first place!");
355 return -E_INVALID_QUERY_FORMAT;
356 }
357 auto nextIter = std::next(iter, 1);
358 if (nextIter == queryObjNodes_.end()) {
359 LOGE("Connectives are not allowed in the last place!");
360 return -E_INVALID_QUERY_FORMAT;
361 }
362 SymbolType symbolType = SqliteQueryHelper::GetSymbolType(nextIter->operFlag);
363 if (symbolType == SymbolType::INVALID_SYMBOL || symbolType == SymbolType::LINK_SYMBOL ||
364 symbolType == SymbolType::SPECIAL_SYMBOL) {
365 LOGE("Must be followed by comparison operation! operflag[%u], symbolType[%u]",
366 static_cast<unsigned>(nextIter->operFlag), static_cast<unsigned>(symbolType));
367 return -E_INVALID_QUERY_FORMAT;
368 }
369 return CheckLinkerBefore(iter);
370 }
371
CheckSuggestIndexFormat(const std::list<QueryObjNode>::iterator & iter) const372 int QueryObject::CheckSuggestIndexFormat(const std::list<QueryObjNode>::iterator &iter) const
373 {
374 auto next = std::next(iter, 1);
375 if (next != queryObjNodes_.end()) {
376 LOGE("SuggestIndex only allowed once, and must appear at the end!");
377 return -E_INVALID_QUERY_FORMAT;
378 }
379 return E_OK;
380 }
381
CheckOrderByFormat(const std::list<QueryObjNode>::iterator & iter)382 int QueryObject::CheckOrderByFormat(const std::list<QueryObjNode>::iterator &iter)
383 {
384 if (!schema_.IsSchemaValid()) {
385 return -E_NOT_SUPPORT;
386 }
387
388 FieldType schemaFieldType;
389 FieldPath fieldPath;
390
391 int errCode = SchemaUtils::ParseAndCheckFieldPath(iter->fieldName, fieldPath);
392 if (errCode != E_OK) {
393 return -E_INVALID_QUERY_FIELD;
394 }
395 errCode = schema_.CheckQueryableAndGetFieldType(fieldPath, schemaFieldType);
396 if (errCode != E_OK) {
397 return -E_INVALID_QUERY_FIELD;
398 }
399 if (schemaFieldType == FieldType::LEAF_FIELD_BOOL) {
400 return -E_INVALID_QUERY_FORMAT;
401 }
402 hasOrderBy_ = true;
403 ++orderByCounts_;
404 LOGD("Need order by %d filed value!", orderByCounts_);
405 return E_OK;
406 }
407
CheckLimitFormat(const std::list<QueryObjNode>::iterator & iter) const408 int QueryObject::CheckLimitFormat(const std::list<QueryObjNode>::iterator &iter) const
409 {
410 auto next = std::next(iter, 1);
411 if (next != queryObjNodes_.end() && SqliteQueryHelper::GetSymbolType(next->operFlag) !=
412 SymbolType::SUGGEST_INDEX_SYMBOL) {
413 LOGE("Limit should be last node or just before suggest-index node!");
414 return -E_INVALID_QUERY_FORMAT;
415 }
416 return E_OK;
417 }
418
IsQueryOnlyByKey() const419 bool QueryObject::IsQueryOnlyByKey() const
420 {
421 return std::none_of(queryObjNodes_.begin(), queryObjNodes_.end(), [&](const QueryObjNode &node) {
422 return node.operFlag != QueryObjType::LIMIT && node.operFlag != QueryObjType::QUERY_BY_KEY_PREFIX &&
423 node.operFlag != QueryObjType::IN_KEYS;
424 });
425 }
426
IsQueryByRange() const427 bool QueryObject::IsQueryByRange() const
428 {
429 return std::any_of(queryObjNodes_.begin(), queryObjNodes_.end(), [&](const QueryObjNode &node) {
430 return node.operFlag == QueryObjType::KEY_RANGE;
431 });
432 }
433
IsQueryForRelationalDB() const434 bool QueryObject::IsQueryForRelationalDB() const
435 {
436 return isTableNameSpecified_ &&
437 std::none_of(queryObjNodes_.begin(), queryObjNodes_.end(), [&](const QueryObjNode &node) {
438 return node.operFlag != QueryObjType::EQUALTO && node.operFlag != QueryObjType::NOT_EQUALTO &&
439 node.operFlag != QueryObjType::AND && node.operFlag != QueryObjType::OR &&
440 node.operFlag != QueryObjType::ORDERBY && node.operFlag != QueryObjType::LIMIT;
441 });
442 }
443
HasOrderBy() const444 bool QueryObject::HasOrderBy() const
445 {
446 return hasOrderBy_;
447 }
448
Empty() const449 bool QueryObject::Empty() const
450 {
451 return queryObjNodes_.empty();
452 }
453
CheckInKeys() const454 int QueryObject::CheckInKeys() const
455 {
456 if (keys_.empty()) {
457 LOGE("Inkeys cannot be empty.");
458 return -E_INVALID_ARGS;
459 }
460 if (keys_.size() > DBConstant::MAX_INKEYS_SIZE) {
461 LOGE("Inkeys cannot be over 128.");
462 return -E_MAX_LIMITS;
463 }
464 for (const auto &key : keys_) {
465 if (key.empty() || key.size() > DBConstant::MAX_KEY_SIZE) {
466 LOGE("The key in Inkeys cannot be empty or overlong, size:%zu.", key.size());
467 return -E_INVALID_ARGS;
468 }
469 }
470 return E_OK;
471 }
472
473 #ifdef RELATIONAL_STORE
SetSchema(const RelationalSchemaObject & schemaObj)474 int QueryObject::SetSchema(const RelationalSchemaObject &schemaObj)
475 {
476 if (!isTableNameSpecified_) {
477 return -E_INVALID_ARGS;
478 }
479 const auto &tableInfo = schemaObj.GetTable(tableName_);
480 SchemaObject schema(tableInfo);
481 schema_ = schema;
482 return E_OK;
483 }
484 #endif
485
SetLimit(int limit,int offset)486 void QueryObject::SetLimit(int limit, int offset)
487 {
488 limit_ = limit;
489 offset_ = offset;
490 for (auto &iter : queryObjNodes_) {
491 if (iter.operFlag == QueryObjType::LIMIT) {
492 if (iter.fieldValue.size() == LIMIT_FIELD_VALUE_SIZE) {
493 iter.fieldValue[0].integerValue = limit_;
494 iter.fieldValue[1].integerValue = offset_;
495 break; // only one limit node
496 }
497 }
498 }
499 }
500
SetTableName(const std::string & tableName)501 void QueryObject::SetTableName(const std::string &tableName)
502 {
503 tableName_ = tableName;
504 isTableNameSpecified_ = true;
505 }
506
GetTableName() const507 const std::string &QueryObject::GetTableName() const
508 {
509 return tableName_;
510 }
511
HasInKeys() const512 bool QueryObject::HasInKeys() const
513 {
514 return hasInKeys_;
515 }
516
SetSortType(SortType sortType)517 void QueryObject::SetSortType(SortType sortType)
518 {
519 sortType_ = sortType;
520 }
521
GetSortType() const522 SortType QueryObject::GetSortType() const
523 {
524 return sortType_;
525 }
526
CheckPrimaryKey(const std::map<int,FieldName> & primaryKeyMap) const527 int QueryObject::CheckPrimaryKey(const std::map<int, FieldName> &primaryKeyMap) const
528 {
529 // 1 primary key and name is "rowid" means no user-defined rowid
530 if (primaryKeyMap.size() == 1 && primaryKeyMap.begin()->second == "rowid") {
531 return -E_NOT_SUPPORT;
532 }
533 std::set<std::string> pkSet;
534 for (const auto &item : primaryKeyMap) {
535 std::string pk = item.second;
536 std::transform(pk.begin(), pk.end(), pk.begin(), ::tolower);
537 pkSet.insert(pk);
538 }
539 std::set<std::string> queryPkSet;
540 for (const auto &queryObjNode : queryObjNodes_) {
541 if (queryObjNode.operFlag != QueryObjType::IN && queryObjNode.operFlag != QueryObjType::EQUALTO) {
542 continue;
543 }
544 std::string field = queryObjNode.fieldName;
545 std::transform(field.begin(), field.end(), field.begin(), ::tolower);
546 if (pkSet.find(field) == pkSet.end()) {
547 LOGE("[Query] query without pk!");
548 return -E_NOT_SUPPORT;
549 }
550 if (queryObjNode.type == QueryValueType::VALUE_TYPE_DOUBLE) {
551 LOGE("[Query] query with pk double!");
552 return -E_NOT_SUPPORT;
553 }
554 queryPkSet.insert(field);
555 }
556 if (queryPkSet.size() != pkSet.size()) {
557 LOGE("[Query] pk count is different! query %zu schema %zu", queryPkSet.size(), pkSet.size());
558 return -E_NOT_SUPPORT;
559 }
560 return E_OK;
561 }
562
GetQueryExpressions(const Query & query)563 std::vector<QueryExpression> QueryObject::GetQueryExpressions(const Query &query)
564 {
565 return GetQueryInfo::GetQueryExpression(query).GetQueryExpressions();
566 }
567
IsAssetsOnly() const568 bool QueryObject::IsAssetsOnly() const
569 {
570 return isAssetsOnly_;
571 }
572
GetGroupNum() const573 uint32_t QueryObject::GetGroupNum() const
574 {
575 return groupNum_ <= 1 ? 1 : groupNum_;
576 }
577
GetAssetsOnlyGroupMap() const578 AssetsGroupMap QueryObject::GetAssetsOnlyGroupMap() const
579 {
580 return assetsGroupMap_;
581 }
582
AssetsOnlyErrFlag() const583 int QueryObject::AssetsOnlyErrFlag() const
584 {
585 return assetsOnlyErrFlag_;
586 }
587
SetUseLocalSchema(bool isUse)588 void QueryObject::SetUseLocalSchema(bool isUse)
589 {
590 isUseLocalSchema_ = isUse;
591 }
592
IsUseLocalSchema() const593 bool QueryObject::IsUseLocalSchema() const
594 {
595 return isUseLocalSchema_;
596 }
597
GetRemoteDev() const598 std::string QueryObject::GetRemoteDev() const
599 {
600 return remoteDev_;
601 }
602
SetRemoteDev(const std::string & dev)603 void QueryObject::SetRemoteDev(const std::string &dev)
604 {
605 remoteDev_ = dev;
606 }
607
IsUseFromTables() const608 bool QueryObject::IsUseFromTables() const
609 {
610 return isUseFromTables_;
611 }
612 }
613
614