| 1301 | // |
| 1302 | |
| 1303 | InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions) const |
| 1304 | { |
| 1305 | if (inversions.isEmpty() && !navigationCandidate) |
| 1306 | return nullptr; |
| 1307 | |
| 1308 | const double streamCardinality = csb->csb_rpt[stream].csb_cardinality; |
| 1309 | |
| 1310 | // Prepared statements could be optimized against an almost empty table |
| 1311 | // and then cached (such as in the restore process), thus causing slowdown |
| 1312 | // when the table grows. In this case, let's consider all available indices. |
| 1313 | const bool smallTable = (streamCardinality <= THRESHOLD_CARDINALITY); |
| 1314 | |
| 1315 | // These flags work around our smart index selection algorithm. Any explicit |
| 1316 | // (i.e. user specified) plan requires all existing indices to be considered |
| 1317 | // for a retrieval. Internal (system) requests used by the engine itself are |
| 1318 | // often optimized using zero or non-actual statistics, so they are processed |
| 1319 | // using somewhat relaxed rules. |
| 1320 | const bool customPlan = csb->csb_rpt[stream].csb_plan; |
| 1321 | const bool sysRequest = (csb->csb_g_flags & csb_internal); |
| 1322 | |
| 1323 | double totalSelectivity = MAXIMUM_SELECTIVITY; // worst selectivity |
| 1324 | double totalIndexCost = 0; |
| 1325 | |
| 1326 | // The upper limit to use an index based retrieval is five indexes + almost all datapages |
| 1327 | const double maximumCost = (DEFAULT_INDEX_COST * 5) + (streamCardinality * 0.95); |
| 1328 | const double minimumSelectivity = 1 / streamCardinality; |
| 1329 | |
| 1330 | double previousTotalCost = maximumCost; |
| 1331 | |
| 1332 | // Force to always choose at least one index |
| 1333 | bool firstCandidate = true; |
| 1334 | |
| 1335 | InversionCandidate* invCandidate = nullptr; |
| 1336 | |
| 1337 | for (auto inversion : inversions) |
| 1338 | { |
| 1339 | const auto indexScratch = inversion->scratch; |
| 1340 | |
| 1341 | // If the explicit plan doesn't mention this index, fake it as used |
| 1342 | // thus excluding it from the cost-based algorithm. Otherwise, |
| 1343 | // given this index is suitable for navigation, also mark it as used. |
| 1344 | |
| 1345 | if ((indexScratch && |
| 1346 | (indexScratch->index->idx_runtime_flags & idx_plan_dont_use)) || |
| 1347 | (!customPlan && inversion == navigationCandidate)) |
| 1348 | { |
| 1349 | inversion->used = true; |
| 1350 | } |
| 1351 | } |
| 1352 | |
| 1353 | MatchedBooleanList matches; |
| 1354 | |
| 1355 | if (navigationCandidate) |
| 1356 | { |
| 1357 | matches.join(navigationCandidate->matches); |
| 1358 | |
| 1359 | // Reset the selectivity/cost prerequisites to account the navigational candidate |
| 1360 | totalSelectivity = navigationCandidate->selectivity; |