You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

726 lines
20 KiB

  1. /* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; version 2 of the License.
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License
  10. along with this program; if not, write to the Free Software
  11. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
  12. #include "my_global.h"
  13. #include "sql_class.h"
  14. #include "sql_lex.h"
  15. #include "sql_select.h"
  16. #include "opt_hints.h"
  17. /**
  18. Information about hints. Sould be
  19. synchronized with opt_hints_enum enum.
  20. Note: Hint name depends on hint state. 'NO_' prefix is added
  21. if appropriate hint state bit(see Opt_hints_map::hints) is not
  22. set. Depending on 'switch_state_arg' argument in 'parse tree
  23. object' constructors(see parse_tree_h../cnm ints.[h,cc]) implementor
  24. can control wishful form of the hint name.
  25. */
  26. struct st_opt_hint_info opt_hint_info[]=
  27. {
  28. {"BKA", true, true},
  29. {"BNL", true, true},
  30. {"ICP", true, true},
  31. {"MRR", true, true},
  32. {"NO_RANGE_OPTIMIZATION", true, true},
  33. {"QB_NAME", false, false},
  34. {0, 0, 0}
  35. };
  36. /**
  37. Prefix for system generated query block name.
  38. Used in information warning in EXPLAIN oputput.
  39. */
  40. const LEX_CSTRING sys_qb_prefix= {"select#", 7};
  41. /*
  42. Compare LEX_CSTRING objects.
  43. @param s Pointer to LEX_CSTRING
  44. @param t Pointer to LEX_CSTRING
  45. @return 0 if strings are equal
  46. 1 if s is greater
  47. -1 if t is greater
  48. */
  49. static int cmp_lex_string(const LEX_CSTRING *s,
  50. const LEX_CSTRING *t)
  51. {
  52. return system_charset_info->
  53. coll->strnncollsp(system_charset_info,
  54. (uchar *) s->str, s->length,
  55. (uchar *) t->str, t->length);
  56. }
  57. static void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type,
  58. bool hint_state,
  59. const LEX_CSTRING *qb_name_arg,
  60. const LEX_CSTRING *table_name_arg,
  61. const LEX_CSTRING *key_name_arg/*, PT_hint *hint*/)
  62. {
  63. String str;
  64. /* Append hint name */
  65. if (!hint_state)
  66. str.append(STRING_WITH_LEN("NO_"));
  67. str.append(opt_hint_info[hint_type].hint_name);
  68. /* ER_WARN_UNKNOWN_QB_NAME with two arguments */
  69. if (err_code == ER_WARN_UNKNOWN_QB_NAME)
  70. {
  71. String qb_name_str;
  72. append_identifier(thd, &qb_name_str, qb_name_arg->str, qb_name_arg->length);
  73. push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
  74. err_code, ER_THD(thd, err_code),
  75. qb_name_str.c_ptr_safe(), str.c_ptr_safe());
  76. return;
  77. }
  78. /* ER_WARN_CONFLICTING_HINT with one argument */
  79. str.append('(');
  80. /* Append table name */
  81. if (table_name_arg && table_name_arg->length > 0)
  82. append_identifier(thd, &str, table_name_arg->str, table_name_arg->length);
  83. /* Append QB name */
  84. if (qb_name_arg && qb_name_arg->length > 0)
  85. {
  86. str.append(STRING_WITH_LEN("@"));
  87. append_identifier(thd, &str, qb_name_arg->str, qb_name_arg->length);
  88. }
  89. /* Append key name */
  90. if (key_name_arg && key_name_arg->length > 0)
  91. {
  92. str.append(' ');
  93. append_identifier(thd, &str, key_name_arg->str, key_name_arg->length);
  94. }
  95. /* Append additional hint arguments if they exist */
  96. // OLEGS: todo
  97. // if (hint)
  98. // {
  99. // if (qb_name_arg || table_name_arg || key_name_arg)
  100. // str.append(' ');
  101. // hint->append_args(thd, &str);
  102. // }
  103. str.append(')');
  104. push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
  105. err_code, ER_THD(thd, err_code), str.c_ptr_safe());
  106. }
  107. /**
  108. Returns a pointer to Opt_hints_global object,
  109. creates Opt_hints object if not exist.
  110. @param pc pointer to Parse_context object
  111. @return pointer to Opt_hints object,
  112. NULL if failed to create the object
  113. */
  114. static Opt_hints_global *get_global_hints(Parse_context *pc)
  115. {
  116. LEX *lex= pc->thd->lex;
  117. if (!lex->opt_hints_global)
  118. lex->opt_hints_global= new Opt_hints_global(pc->thd->mem_root);
  119. if (lex->opt_hints_global)
  120. lex->opt_hints_global->set_resolved();
  121. return lex->opt_hints_global;
  122. }
  123. static Opt_hints_qb *get_qb_hints(Parse_context *pc)
  124. {
  125. if (pc->select->opt_hints_qb)
  126. return pc->select->opt_hints_qb;
  127. Opt_hints_global *global_hints= get_global_hints(pc);
  128. if (global_hints == NULL)
  129. return NULL;
  130. Opt_hints_qb *qb= new Opt_hints_qb(global_hints, pc->thd->mem_root,
  131. pc->select->select_number);
  132. if (qb)
  133. {
  134. global_hints->register_child(qb);
  135. pc->select->opt_hints_qb= qb;
  136. qb->set_resolved();
  137. }
  138. return qb;
  139. }
  140. /**
  141. Find existing Opt_hints_qb object, print warning
  142. if the query block is not found.
  143. @param pc pointer to Parse_context object
  144. @param table_name query block name
  145. @param hint processed hint // OLEGS: amend this
  146. @return pointer to Opt_hints_table object if found,
  147. NULL otherwise
  148. */
  149. static Opt_hints_qb *find_qb_hints(Parse_context *pc,
  150. const LEX_CSTRING *qb_name,
  151. opt_hints_enum hint_type,
  152. bool hint_state)
  153. {
  154. if (qb_name->length == 0) // no QB NAME is used
  155. return pc->select->opt_hints_qb;
  156. Opt_hints_qb *qb= static_cast<Opt_hints_qb *>
  157. (pc->thd->lex->opt_hints_global->find_by_name(qb_name));
  158. if (qb == NULL)
  159. {
  160. print_warn(pc->thd, ER_WARN_UNKNOWN_QB_NAME, hint_type, hint_state,
  161. qb_name, NULL, NULL);
  162. }
  163. return qb;
  164. }
  165. /**
  166. Returns pointer to Opt_hints_table object,
  167. create Opt_hints_table object if not exist.
  168. @param pc pointer to Parse_context object
  169. @param table_name pointer to Hint_param_table object
  170. @param qb pointer to Opt_hints_qb object
  171. @return pointer to Opt_hints_table object,
  172. NULL if failed to create the object
  173. */
  174. static Opt_hints_table *get_table_hints(Parse_context *pc,
  175. const LEX_CSTRING *table_name,
  176. Opt_hints_qb *qb)
  177. {
  178. Opt_hints_table *tab=
  179. static_cast<Opt_hints_table *> (qb->find_by_name(table_name));
  180. if (!tab)
  181. {
  182. tab= new Opt_hints_table(table_name, qb, pc->thd->mem_root);
  183. qb->register_child(tab);
  184. }
  185. return tab;
  186. }
  187. bool Opt_hints::get_switch(opt_hints_enum type_arg) const
  188. {
  189. if (is_specified(type_arg))
  190. return hints_map.switch_on(type_arg);
  191. if (opt_hint_info[type_arg].check_upper_lvl)
  192. return parent->get_switch(type_arg);
  193. return false;
  194. }
  195. Opt_hints* Opt_hints::find_by_name(const LEX_CSTRING *name_arg) const
  196. {
  197. for (uint i= 0; i < child_array.size(); i++)
  198. {
  199. const LEX_CSTRING *name= child_array[i]->get_name();
  200. if (name && !cmp_lex_string(name, name_arg))
  201. return child_array[i];
  202. }
  203. return NULL;
  204. }
  205. void Opt_hints::print(THD *thd, String *str)
  206. {
  207. for (uint i= 0; i < MAX_HINT_ENUM; i++)
  208. {
  209. if (is_specified(static_cast<opt_hints_enum>(i)) && is_resolved())
  210. {
  211. append_hint_type(str, static_cast<opt_hints_enum>(i));
  212. str->append(STRING_WITH_LEN("("));
  213. append_name(thd, str);
  214. // OLEGS:
  215. //if (!opt_hint_info[i].switch_hint)
  216. // get_complex_hints(i)->append_args(thd, str);
  217. str->append(STRING_WITH_LEN(") "));
  218. }
  219. }
  220. for (uint i= 0; i < child_array.size(); i++)
  221. child_array[i]->print(thd, str);
  222. }
  223. void Opt_hints::append_hint_type(String *str, opt_hints_enum type)
  224. {
  225. const char* hint_name= opt_hint_info[type].hint_name;
  226. if(!hints_map.switch_on(type))
  227. str->append(STRING_WITH_LEN("NO_"));
  228. str->append(hint_name);
  229. }
  230. void Opt_hints::print_warn_unresolved(THD *thd)
  231. {
  232. String hint_name_str, hint_type_str;
  233. append_name(thd, &hint_name_str);
  234. for (uint i= 0; i < MAX_HINT_ENUM; i++)
  235. {
  236. if (is_specified(static_cast<opt_hints_enum>(i)))
  237. {
  238. hint_type_str.length(0);
  239. append_hint_type(&hint_type_str, static_cast<opt_hints_enum>(i));
  240. push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
  241. get_warn_unresolved_code(),
  242. ER_THD(thd, get_warn_unresolved_code()),
  243. hint_name_str.c_ptr_safe(),
  244. hint_type_str.c_ptr_safe());
  245. }
  246. }
  247. }
  248. void Opt_hints::check_unresolved(THD *thd)
  249. {
  250. if (!is_resolved())
  251. print_warn_unresolved(thd);
  252. if (!is_all_resolved())
  253. {
  254. for (uint i= 0; i < child_array.size(); i++)
  255. child_array[i]->check_unresolved(thd);
  256. }
  257. }
  258. PT_hint *Opt_hints_global::get_complex_hints(uint type)
  259. {
  260. DBUG_ASSERT(0);
  261. return NULL;
  262. }
  263. Opt_hints_qb::Opt_hints_qb(Opt_hints *opt_hints_arg,
  264. MEM_ROOT *mem_root_arg,
  265. uint select_number_arg)
  266. : Opt_hints(NULL, opt_hints_arg, mem_root_arg),
  267. select_number(select_number_arg)
  268. {
  269. sys_name.str= buff;
  270. sys_name.length= my_snprintf(buff, sizeof(buff), "%s%lx",
  271. sys_qb_prefix.str, select_number);
  272. }
  273. Opt_hints_table *Opt_hints_qb::adjust_table_hints(TABLE *table,
  274. const LEX_CSTRING *alias)
  275. {
  276. Opt_hints_table *tab= static_cast<Opt_hints_table *>(find_by_name(alias));
  277. table->pos_in_table_list->opt_hints_qb= this;
  278. if (!tab) // Tables not found
  279. return NULL;
  280. tab->adjust_key_hints(table);
  281. return tab;
  282. }
  283. void Opt_hints_table::adjust_key_hints(TABLE *table)
  284. {
  285. set_resolved();
  286. if (child_array_ptr()->size() == 0) // No key level hints
  287. {
  288. get_parent()->incr_resolved_children();
  289. return;
  290. }
  291. /* Make sure that adjustement is called only once. */
  292. DBUG_ASSERT(keyinfo_array.size() == 0);
  293. keyinfo_array.resize(table->s->keys, NULL);
  294. for (Opt_hints** hint= child_array_ptr()->begin();
  295. hint < child_array_ptr()->end(); ++hint)
  296. {
  297. KEY *key_info= table->key_info;
  298. for (uint j= 0 ; j < table->s->keys ; j++, key_info++)
  299. {
  300. if (!cmp_lex_string((*hint)->get_name(), &key_info->name))
  301. {
  302. (*hint)->set_resolved();
  303. keyinfo_array[j]= static_cast<Opt_hints_key *>(*hint);
  304. incr_resolved_children();
  305. }
  306. }
  307. }
  308. /*
  309. Do not increase number of resolved tables
  310. if there are unresolved key objects. It's
  311. important for check_unresolved() function.
  312. */
  313. if (is_all_resolved())
  314. get_parent()->incr_resolved_children();
  315. }
  316. /**
  317. Function returns hint value depending on
  318. the specfied hint level. If hint is specified
  319. on current level, current level hint value is
  320. returned, otherwise parent level hint is checked.
  321. @param hint Pointer to the hint object
  322. @param parent_hint Pointer to the parent hint object,
  323. should never be NULL
  324. @param type_arg hint type
  325. @param OUT ret_val hint value depending on
  326. what hint level is used
  327. @return true if hint is specified, false otherwise
  328. */
  329. static bool get_hint_state(Opt_hints *hint,
  330. Opt_hints *parent_hint,
  331. opt_hints_enum type_arg,
  332. bool *ret_val)
  333. {
  334. DBUG_ASSERT(parent_hint);
  335. if (opt_hint_info[type_arg].switch_hint)
  336. {
  337. if (hint && hint->is_specified(type_arg))
  338. {
  339. *ret_val= hint->get_switch(type_arg);
  340. return true;
  341. }
  342. else if (opt_hint_info[type_arg].check_upper_lvl &&
  343. parent_hint->is_specified(type_arg))
  344. {
  345. *ret_val= parent_hint->get_switch(type_arg);
  346. return true;
  347. }
  348. }
  349. else
  350. {
  351. /* Complex hint, not implemented atm */
  352. DBUG_ASSERT(0);
  353. }
  354. return false;
  355. }
  356. bool hint_key_state(const THD *thd, const TABLE *table,
  357. uint keyno, opt_hints_enum type_arg,
  358. uint optimizer_switch)
  359. {
  360. Opt_hints_table *table_hints= table->pos_in_table_list->opt_hints_table;
  361. /* Parent should always be initialized */
  362. if (table_hints && keyno != MAX_KEY)
  363. {
  364. Opt_hints_key *key_hints= table_hints->keyinfo_array.size() > 0 ?
  365. table_hints->keyinfo_array[keyno] : NULL;
  366. bool ret_val= false;
  367. if (get_hint_state(key_hints, table_hints, type_arg, &ret_val))
  368. return ret_val;
  369. }
  370. return optimizer_flag(thd, optimizer_switch);
  371. }
  372. bool hint_table_state(const THD *thd, const TABLE *table,
  373. opt_hints_enum type_arg,
  374. uint optimizer_switch)
  375. {
  376. TABLE_LIST *table_list= table->pos_in_table_list;
  377. if (table_list->opt_hints_qb)
  378. {
  379. bool ret_val= false;
  380. if (get_hint_state(table_list->opt_hints_table,
  381. table_list->opt_hints_qb,
  382. type_arg, &ret_val))
  383. return ret_val;
  384. }
  385. return optimizer_flag(thd, optimizer_switch);
  386. }
  387. bool hint_table_state_or_fallback(const THD *thd, const TABLE *table,
  388. opt_hints_enum type_arg,
  389. bool fallback_value)
  390. {
  391. TABLE_LIST *table_list= table->pos_in_table_list;
  392. if (table_list->opt_hints_qb)
  393. {
  394. bool ret_val= false;
  395. if (get_hint_state(table_list->opt_hints_table,
  396. table_list->opt_hints_qb,
  397. type_arg, &ret_val))
  398. return ret_val;
  399. }
  400. return fallback_value;
  401. }
  402. bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
  403. {
  404. const Table_level_hint_type &table_level_hint_type= *this;
  405. opt_hints_enum hint_type;
  406. bool hint_state; // ON or OFF
  407. switch (table_level_hint_type.id())
  408. {
  409. case TokenID::keyword_BNL:
  410. hint_type= BNL_HINT_ENUM;
  411. hint_state= true;
  412. break;
  413. case TokenID::keyword_NO_BNL:
  414. hint_type= BNL_HINT_ENUM;
  415. hint_state= false;
  416. break;
  417. case TokenID::keyword_BKA:
  418. hint_type= BKA_HINT_ENUM;
  419. hint_state= true;
  420. break;
  421. case TokenID::keyword_NO_BKA:
  422. hint_type= BKA_HINT_ENUM;
  423. hint_state= false;
  424. break;
  425. default:
  426. DBUG_ASSERT(0);
  427. return true;
  428. }
  429. if (const At_query_block_name_opt_table_name_list &
  430. at_query_block_name_opt_table_name_list= *this)
  431. {
  432. // this is @ query_block_name opt_table_name_list
  433. const Query_block_name &qb_name= *this;
  434. Opt_hints_qb *qb= find_qb_hints(pc, &qb_name, hint_type, hint_state);
  435. if (qb == NULL)
  436. return false;
  437. if (at_query_block_name_opt_table_name_list.is_empty())
  438. {
  439. // e.g. BKA(@qb1)
  440. if (qb->set_switch(hint_state, hint_type, false))
  441. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  442. &qb_name, NULL, NULL/*, this*/);
  443. return false;
  444. }
  445. else
  446. {
  447. // e.g. BKA(@qb1 t1, t2, t3)
  448. const Opt_table_name_list &opt_table_name_list= *this;
  449. for (const Table_name &table : opt_table_name_list)
  450. {
  451. Opt_hints_table *tab= get_table_hints(pc, &table, qb);
  452. if (!tab)
  453. return true; // OLEGS: why no warning?
  454. if (tab->set_switch(hint_state, hint_type, true))
  455. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  456. &qb_name, &table, NULL/*, this*/);
  457. }
  458. }
  459. }
  460. else
  461. {
  462. // this is opt_hint_param_table_list
  463. const Opt_table_name_list &table_name_list= *this;
  464. const Opt_hint_param_table_list &opt_hint_param_table_list= *this;
  465. Opt_hints_qb *qb= find_qb_hints(pc, &null_clex_str, hint_type, hint_state);
  466. if (qb == NULL)
  467. return false;
  468. if (table_name_list.is_empty() && opt_hint_param_table_list.is_empty())
  469. {
  470. // e.g. BKA()
  471. if (qb->set_switch(hint_state, hint_type, false))
  472. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  473. &null_clex_str, NULL, NULL/*, this*/);
  474. return false;
  475. }
  476. for (const Table_name &table : table_name_list)
  477. {
  478. // e.g. BKA(t1, t2)
  479. Opt_hints_table *tab= get_table_hints(pc, &table, qb);
  480. if (!tab)
  481. return true; // OLEGS: no warning?
  482. if (tab->set_switch(hint_state, hint_type, true))
  483. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  484. &null_clex_str, &table, NULL/*, this*/);
  485. }
  486. for (const Hint_param_table &table : opt_hint_param_table_list)
  487. {
  488. // e.g. BKA(t1@qb1, t2@qb2)
  489. const Query_block_name &qb_name= table;
  490. const Table_name &table_name= table;
  491. Opt_hints_qb *qb= find_qb_hints(pc, &qb_name, hint_type, hint_state);
  492. if (qb == NULL)
  493. return false;
  494. // OLEGS: todo
  495. Opt_hints_table *tab= get_table_hints(pc, &table_name, qb);
  496. if (!tab)
  497. return true; // OLEGS: why no warning?
  498. if (tab->set_switch(hint_state, hint_type, true))
  499. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  500. &qb_name, &table_name, NULL/*, this*/);
  501. }
  502. }
  503. return false;
  504. }
  505. bool Optimizer_hint_parser::Index_level_hint::resolve(Parse_context *pc) const
  506. {
  507. const Index_level_hint_type &index_level_hint_type= *this;
  508. opt_hints_enum hint_type;
  509. bool hint_state; // ON or OFF
  510. switch (index_level_hint_type.id())
  511. {
  512. case TokenID::keyword_NO_ICP:
  513. hint_type= ICP_HINT_ENUM;
  514. hint_state= false;
  515. break;
  516. case TokenID::keyword_MRR:
  517. hint_type= MRR_HINT_ENUM;
  518. hint_state= true;
  519. break;
  520. case TokenID::keyword_NO_MRR:
  521. hint_type= MRR_HINT_ENUM;
  522. hint_state= false;
  523. break;
  524. case TokenID::keyword_NO_RANGE_OPTIMIZATION:
  525. hint_type= NO_RANGE_HINT_ENUM;
  526. hint_state= true;
  527. break;
  528. default:
  529. DBUG_ASSERT(0);
  530. return true;
  531. }
  532. const Hint_param_table_ext &table_ext= *this;
  533. const Query_block_name &qb_name= table_ext;
  534. const Table_name &table_name= table_ext;
  535. Opt_hints_qb *qb= find_qb_hints(pc, &qb_name, hint_type, hint_state);
  536. if (qb == NULL)
  537. return false;
  538. Opt_hints_table *tab= get_table_hints(pc, &table_name, qb);
  539. if (!tab)
  540. return true; // OLEGS: why no warning?
  541. if (is_empty()) // Table level hint
  542. {
  543. if (tab->set_switch(hint_state, hint_type, false))
  544. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  545. &qb_name, &table_name, NULL/*, this*/);
  546. return false;
  547. }
  548. for (const Hint_param_index &index_name : *this)
  549. {
  550. Opt_hints_key *idx= (Opt_hints_key *)tab->find_by_name(&index_name);
  551. if (!idx)
  552. {
  553. idx= new Opt_hints_key(&index_name, tab, pc->thd->mem_root);
  554. tab->register_child(idx);
  555. }
  556. if (idx->set_switch(hint_state, hint_type, true))
  557. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
  558. &qb_name, &table_name, &index_name/*, this*/);
  559. }
  560. return false;
  561. }
  562. bool Optimizer_hint_parser::Qb_name_hint::resolve(Parse_context *pc) const
  563. {
  564. Opt_hints_qb *qb= pc->select->opt_hints_qb;
  565. DBUG_ASSERT(qb);
  566. const Query_block_name &qb_name= *this;
  567. if (qb->get_name() || // QB name is already set
  568. qb->get_parent()->find_by_name(&qb_name)) // Name is already used
  569. {
  570. print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, QB_NAME_HINT_ENUM, false,
  571. NULL, NULL, NULL/*, this*/);
  572. return false;
  573. }
  574. qb->set_name(&qb_name);
  575. return false;
  576. }
  577. bool Optimizer_hint_parser::Hint_list::resolve(Parse_context *pc)
  578. {
  579. if (!get_qb_hints(pc))
  580. return true;
  581. List_iterator_fast<Optimizer_hint_parser::Hint> li(*this);
  582. while(Optimizer_hint_parser::Hint *hint= li++)
  583. {
  584. if (const Table_level_hint &table_hint=
  585. static_cast<const Table_level_hint &>(*hint))
  586. {
  587. if (table_hint.resolve(pc))
  588. return true;
  589. }
  590. else if (const Index_level_hint &index_hint=
  591. static_cast<const Index_level_hint &>(*hint))
  592. {
  593. if (index_hint.resolve(pc))
  594. return true;
  595. }
  596. else if (const Qb_name_hint &qb_hint=
  597. static_cast<const Qb_name_hint &>(*hint))
  598. {
  599. if (qb_hint.resolve(pc))
  600. return true; // OLEGS: check this result
  601. }
  602. }
  603. return false;
  604. }