9
9
*
10
10
*
11
11
* IDENTIFICATION
12
- * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.184 2005/06/13 23:14:48 tgl Exp $
12
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.185 2005/06/14 04:04:30 tgl Exp $
13
13
*
14
14
*-------------------------------------------------------------------------
15
15
*/
@@ -65,6 +65,17 @@ static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
65
65
Relids outer_relids );
66
66
static List * find_clauses_for_join (PlannerInfo * root ,RelOptInfo * rel ,
67
67
Relids outer_relids ,bool isouterjoin );
68
+ static bool match_variant_ordering (PlannerInfo * root ,
69
+ IndexOptInfo * index ,
70
+ List * restrictclauses ,
71
+ ScanDirection * indexscandir );
72
+ static List * identify_ignorable_ordering_cols (PlannerInfo * root ,
73
+ IndexOptInfo * index ,
74
+ List * restrictclauses );
75
+ static bool match_index_to_query_keys (PlannerInfo * root ,
76
+ IndexOptInfo * index ,
77
+ ScanDirection indexscandir ,
78
+ List * ignorables );
68
79
static bool match_boolean_index_clause (Node * clause ,int indexcol ,
69
80
IndexOptInfo * index );
70
81
static bool match_special_index_operator (Expr * clause ,Oid opclass ,
@@ -315,22 +326,24 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
315
326
}
316
327
317
328
/*
318
- * 4. If the index is ordered, a backwards scan might be
319
- * interesting. Currently this is only possible for a DESC query
320
- * result ordering.
329
+ * 4. If the index is ordered, and there is a requested query
330
+ * ordering that we failed to match, consider variant ways of
331
+ * achieving the ordering. Again, this is only interesting
332
+ * at top level.
321
333
*/
322
- if (istoplevel && index_is_ordered && !isjoininner )
334
+ if (istoplevel && index_is_ordered && !isjoininner &&
335
+ root -> query_pathkeys != NIL &&
336
+ pathkeys_useful_for_ordering (root ,useful_pathkeys )== 0 )
323
337
{
324
- index_pathkeys = build_index_pathkeys (root ,index ,
325
- BackwardScanDirection );
326
- useful_pathkeys = truncate_useless_pathkeys (root ,rel ,
327
- index_pathkeys );
328
- if (useful_pathkeys != NIL )
338
+ ScanDirection indexscandir ;
339
+
340
+ if (match_variant_ordering (root ,index ,restrictclauses ,
341
+ & indexscandir ))
329
342
{
330
343
ipath = create_index_path (root ,index ,
331
344
restrictclauses ,
332
- useful_pathkeys ,
333
- BackwardScanDirection ,
345
+ root -> query_pathkeys ,
346
+ indexscandir ,
334
347
false);
335
348
result = lappend (result ,ipath );
336
349
}
@@ -1220,6 +1233,255 @@ find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
1220
1233
return clause_list ;
1221
1234
}
1222
1235
1236
+ /****************************************************************************
1237
+ *---- ROUTINES TO HANDLE PATHKEYS ----
1238
+ ****************************************************************************/
1239
+
1240
+ /*
1241
+ * match_variant_ordering
1242
+ *Try to match an index's ordering to the query's requested ordering
1243
+ *
1244
+ * This is used when the index is ordered but a naive comparison fails to
1245
+ * match its ordering (pathkeys) to root->query_pathkeys. It may be that
1246
+ * we need to scan the index backwards. Also, a less naive comparison can
1247
+ * help for both forward and backward indexscans. Columns of the index
1248
+ * that have an equality restriction clause can be ignored in the match;
1249
+ * that is, an index on (x,y) can be considered to match the ordering of
1250
+ *... WHERE x = 42 ORDER BY y;
1251
+ *
1252
+ * Note: it would be possible to similarly ignore useless ORDER BY items;
1253
+ * that is, an index on just y could be considered to match the ordering of
1254
+ *... WHERE x = 42 ORDER BY x, y;
1255
+ * But proving that this is safe would require finding a btree opclass
1256
+ * containing both the = operator and the < or > operator in the ORDER BY
1257
+ * item. That's significantly more expensive than what we do here, since
1258
+ * we'd have to look at restriction clauses unrelated to the current index
1259
+ * and search for opclasses without any hint from the index. The practical
1260
+ * use-cases seem to be mostly covered by ignoring index columns, so that's
1261
+ * all we do for now.
1262
+ *
1263
+ * Inputs:
1264
+ * 'index' is the index of interest.
1265
+ * 'restrictclauses' is the list of sublists of restriction clauses
1266
+ *matching the columns of the index (NIL if none)
1267
+ *
1268
+ * Returns TRUE if able to match the requested query pathkeys, FALSE if not.
1269
+ * In the TRUE case, sets '*indexscandir' to either ForwardScanDirection or
1270
+ * BackwardScanDirection to indicate the proper scan direction.
1271
+ */
1272
+ static bool
1273
+ match_variant_ordering (PlannerInfo * root ,
1274
+ IndexOptInfo * index ,
1275
+ List * restrictclauses ,
1276
+ ScanDirection * indexscandir )
1277
+ {
1278
+ List * ignorables ;
1279
+
1280
+ /*
1281
+ * Forget the whole thing if not a btree index; our check for ignorable
1282
+ * columns assumes we are dealing with btree opclasses. (It'd be possible
1283
+ * to factor out just the try for backwards indexscan, but considering
1284
+ * that we presently have no orderable indexes except btrees anyway,
1285
+ * it's hardly worth contorting this code for that case.)
1286
+ *
1287
+ * Note: if you remove this, you probably need to put in a check on
1288
+ * amoptionalkey to prevent possible clauseless scan on an index that
1289
+ * won't cope.
1290
+ */
1291
+ if (index -> relam != BTREE_AM_OID )
1292
+ return false;
1293
+ /*
1294
+ * Figure out which index columns can be optionally ignored because
1295
+ * they have an equality constraint. This is the same set for either
1296
+ * forward or backward scan, so we do it just once.
1297
+ */
1298
+ ignorables = identify_ignorable_ordering_cols (root ,index ,
1299
+ restrictclauses );
1300
+ /*
1301
+ * Try to match to forward scan, then backward scan. However, we can
1302
+ * skip the forward-scan case if there are no ignorable columns,
1303
+ * because find_usable_indexes() would have found the match already.
1304
+ */
1305
+ if (ignorables &&
1306
+ match_index_to_query_keys (root ,index ,ForwardScanDirection ,
1307
+ ignorables ))
1308
+ {
1309
+ * indexscandir = ForwardScanDirection ;
1310
+ return true;
1311
+ }
1312
+ if (match_index_to_query_keys (root ,index ,BackwardScanDirection ,
1313
+ ignorables ))
1314
+ {
1315
+ * indexscandir = BackwardScanDirection ;
1316
+ return true;
1317
+ }
1318
+ return false;
1319
+ }
1320
+
1321
+ /*
1322
+ * identify_ignorable_ordering_cols
1323
+ *Determine which index columns can be ignored for ordering purposes
1324
+ *
1325
+ * Returns an integer List of column numbers (1-based) of ignorable
1326
+ * columns. The ignorable columns are those that have equality constraints
1327
+ * against pseudoconstants.
1328
+ */
1329
+ static List *
1330
+ identify_ignorable_ordering_cols (PlannerInfo * root ,
1331
+ IndexOptInfo * index ,
1332
+ List * restrictclauses )
1333
+ {
1334
+ List * result = NIL ;
1335
+ int indexcol = 0 ;/* note this is 0-based */
1336
+ ListCell * l ;
1337
+
1338
+ /* restrictclauses is either NIL or has a sublist per column */
1339
+ foreach (l ,restrictclauses )
1340
+ {
1341
+ List * sublist = (List * )lfirst (l );
1342
+ Oid opclass = index -> classlist [indexcol ];
1343
+ ListCell * l2 ;
1344
+
1345
+ foreach (l2 ,sublist )
1346
+ {
1347
+ RestrictInfo * rinfo = (RestrictInfo * )lfirst (l2 );
1348
+ OpExpr * clause = (OpExpr * )rinfo -> clause ;
1349
+ Oid clause_op ;
1350
+ int op_strategy ;
1351
+ bool varonleft ;
1352
+ bool ispc ;
1353
+
1354
+ /* We know this clause passed match_clause_to_indexcol */
1355
+
1356
+ /* First check for boolean-index cases. */
1357
+ if (IsBooleanOpclass (opclass ))
1358
+ {
1359
+ if (match_boolean_index_clause ((Node * )clause ,indexcol ,
1360
+ index ))
1361
+ {
1362
+ /*
1363
+ * The clause means either col = TRUE or col = FALSE;
1364
+ * we do not care which, it's an equality constraint
1365
+ * either way.
1366
+ */
1367
+ result = lappend_int (result ,indexcol + 1 );
1368
+ break ;
1369
+ }
1370
+ }
1371
+
1372
+ /* Else clause must be a binary opclause. */
1373
+ Assert (IsA (clause ,OpExpr ));
1374
+
1375
+ /* Determine left/right sides and check the operator */
1376
+ clause_op = clause -> opno ;
1377
+ if (match_index_to_operand (linitial (clause -> args ),indexcol ,
1378
+ index ))
1379
+ {
1380
+ /* clause_op is correct */
1381
+ varonleft = true;
1382
+ }
1383
+ else
1384
+ {
1385
+ Assert (match_index_to_operand (lsecond (clause -> args ),indexcol ,
1386
+ index ));
1387
+ /* Must flip operator to get the opclass member */
1388
+ clause_op = get_commutator (clause_op );
1389
+ varonleft = false;
1390
+ }
1391
+ if (!OidIsValid (clause_op ))
1392
+ continue ;/* ignore non match, per next comment */
1393
+ op_strategy = get_op_opclass_strategy (clause_op ,opclass );
1394
+
1395
+ /*
1396
+ * You might expect to see Assert(op_strategy != 0) here,
1397
+ * but you won't: the clause might contain a special indexable
1398
+ * operator rather than an ordinary opclass member. Currently
1399
+ * none of the special operators are very likely to expand to
1400
+ * an equality operator; we do not bother to check, but just
1401
+ * assume no match.
1402
+ */
1403
+ if (op_strategy != BTEqualStrategyNumber )
1404
+ continue ;
1405
+
1406
+ /* Now check that other side is pseudoconstant */
1407
+ if (varonleft )
1408
+ ispc = is_pseudo_constant_clause_relids (lsecond (clause -> args ),
1409
+ rinfo -> right_relids );
1410
+ else
1411
+ ispc = is_pseudo_constant_clause_relids (linitial (clause -> args ),
1412
+ rinfo -> left_relids );
1413
+ if (ispc )
1414
+ {
1415
+ result = lappend_int (result ,indexcol + 1 );
1416
+ break ;
1417
+ }
1418
+ }
1419
+ indexcol ++ ;
1420
+ }
1421
+ return result ;
1422
+ }
1423
+
1424
+ /*
1425
+ * match_index_to_query_keys
1426
+ *Check a single scan direction for "intelligent" match to query keys
1427
+ *
1428
+ * 'index' is the index of interest.
1429
+ * 'indexscandir' is the scan direction to consider
1430
+ * 'ignorables' is an integer list of indexes of ignorable index columns
1431
+ *
1432
+ * Returns TRUE on successful match (ie, the query_pathkeys can be considered
1433
+ * to match this index).
1434
+ */
1435
+ static bool
1436
+ match_index_to_query_keys (PlannerInfo * root ,
1437
+ IndexOptInfo * index ,
1438
+ ScanDirection indexscandir ,
1439
+ List * ignorables )
1440
+ {
1441
+ List * index_pathkeys ;
1442
+ ListCell * index_cell ;
1443
+ int index_col ;
1444
+ ListCell * r ;
1445
+
1446
+ /* Get the pathkeys that exactly describe the index */
1447
+ index_pathkeys = build_index_pathkeys (root ,index ,indexscandir );
1448
+
1449
+ /*
1450
+ * Can we match to the query's requested pathkeys? The inner loop
1451
+ * skips over ignorable index columns while trying to match.
1452
+ */
1453
+ index_cell = list_head (index_pathkeys );
1454
+ index_col = 0 ;
1455
+
1456
+ foreach (r ,root -> query_pathkeys )
1457
+ {
1458
+ List * rsubkey = (List * )lfirst (r );
1459
+
1460
+ for (;;)
1461
+ {
1462
+ List * isubkey ;
1463
+
1464
+ if (index_cell == NULL )
1465
+ return false;
1466
+ isubkey = (List * )lfirst (index_cell );
1467
+ index_cell = lnext (index_cell );
1468
+ index_col ++ ;/* index_col is now 1-based */
1469
+ /*
1470
+ * Since we are dealing with canonicalized pathkeys, pointer
1471
+ * comparison is sufficient to determine a match.
1472
+ */
1473
+ if (rsubkey == isubkey )
1474
+ break ;/* matched current query pathkey */
1475
+
1476
+ if (!list_member_int (ignorables ,index_col ))
1477
+ return false;/* definite failure to match */
1478
+ /* otherwise loop around and try to match to next index col */
1479
+ }
1480
+ }
1481
+
1482
+ return true;
1483
+ }
1484
+
1223
1485
/****************************************************************************
1224
1486
*---- PATH CREATION UTILITIES ----
1225
1487
****************************************************************************/
@@ -1230,7 +1492,8 @@ find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
1230
1492
* of RestrictInfos.
1231
1493
*
1232
1494
* This is used to flatten out the result of group_clauses_by_indexkey()
1233
- * to produce an indexclauses list.
1495
+ * to produce an indexclauses list. The original list structure mustn't
1496
+ * be altered, but it's OK to share copies of the underlying RestrictInfos.
1234
1497
*/
1235
1498
List *
1236
1499
flatten_clausegroups_list (List * clausegroups )