From 0b9be8e526335686d2a4eeba5a62313b5018a5c4 Mon Sep 17 00:00:00 2001 From: "dgrowley@gmail.com" Date: Mon, 25 Jun 2018 16:31:47 +1200 Subject: [PATCH v1] Improve best case speed of tuple conversion map generation Previously convert_tuples_by_name_map naively performed a search of all inner loop columns for each loop done in the outer loop. When partitioned tables had many columns this could result in slow generation of the tuple conversion maps. For INSERT and UPDATE statements that touched few rows, this could mean a very large overhead indeed. We can do a bit better with this loop. It's quite likely that the columns in partitioned tables and their partitions are in the same order, so it makes sense to start searching in the inner loop at the same position that we're on in the outer loop. This makes the best case search O(N) (N being the number of columns). The worst case is still O(N^2), but it seems unlikely that would happen, although we must take some protection against starting the search one column too late due to a dropped column. Without this protection the worst case could be easy to hit. --- src/backend/access/common/tupconvert.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 2d0d2f4b32..578bf7f2ba 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -297,6 +297,7 @@ convert_tuples_by_name_map(TupleDesc indesc, AttrNumber *attrMap; int n; int i; + int outnondropped = 0; n = outdesc->natts; attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber)); @@ -313,9 +314,22 @@ convert_tuples_by_name_map(TupleDesc indesc, attname = NameStr(outatt->attname); atttypid = outatt->atttypid; atttypmod = outatt->atttypmod; + + /* + * Now search for an attribute with the same name in the indesc. It + * seems likely that a partitioned table will have the attributes in + * the same order as the partition, so we optimistically start our + * search at the same attnum (minus the so-far counted number of + * dropped columns in outdesc). If a column were dropped in outdesc + * and not indesc then if we didn't account for the dropped column, it + * could mean we end up starting our search one column after the one + * we want to find, resulting in only finding our target on the final + * column we check. + */ for (j = 0; j < indesc->natts; j++) { - Form_pg_attribute inatt = TupleDescAttr(indesc, j); + int k = (outnondropped + j) % indesc->natts; + Form_pg_attribute inatt = TupleDescAttr(indesc, k); if (inatt->attisdropped) continue; @@ -330,7 +344,7 @@ convert_tuples_by_name_map(TupleDesc indesc, attname, format_type_be(outdesc->tdtypeid), format_type_be(indesc->tdtypeid)))); - attrMap[i] = (AttrNumber) (j + 1); + attrMap[i] = inatt->attnum; break; } } @@ -342,6 +356,9 @@ convert_tuples_by_name_map(TupleDesc indesc, attname, format_type_be(outdesc->tdtypeid), format_type_be(indesc->tdtypeid)))); + + /* keep track of number of non-dropped outdesc columns */ + outnondropped++; } return attrMap; -- 2.16.2.windows.1