aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
authorDavid Rowley <drowley@postgresql.org>2024-05-01 13:23:05 +1200
committerDavid Rowley <drowley@postgresql.org>2024-05-01 13:23:05 +1200
commit0a34bcd0c23e7f67daad7b026320a12c52d797ac (patch)
tree2dc7670da55694fb9ebee35f225486c220861bdd /src/backend/executor
parent1ee22d1e87b3c442928abe9535d6b2bf8460fc67 (diff)
downloadpostgresql-0a34bcd0c23e7f67daad7b026320a12c52d797ac.tar.gz
postgresql-0a34bcd0c23e7f67daad7b026320a12c52d797ac.zip
Ensure we allocate NAMEDATALEN bytes for names in Index Only Scans
As an optimization, we store "name" columns as cstrings in btree indexes. Here we modify it so that Index Only Scans convert these cstrings back to names with NAMEDATALEN bytes rather than storing the cstring in the tuple slot, as was happening previously. Bug: #17855 Reported-by: Alexander Lakhin Reviewed-by: Alexander Lakhin, Tom Lane Discussion: https://postgr.es/m/17855-5f523e0f9769a566@postgresql.org Backpatch-through: 12, all supported versions
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/nodeIndexonlyscan.c95
1 files changed, 88 insertions, 7 deletions
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 661971e7070..bb1fd5d15da 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -35,19 +35,21 @@
#include "access/tableam.h"
#include "access/tupdesc.h"
#include "access/visibilitymap.h"
+#include "catalog/pg_type.h"
#include "executor/execdebug.h"
#include "executor/nodeIndexonlyscan.h"
#include "executor/nodeIndexscan.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/predicate.h"
+#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"
static TupleTableSlot *IndexOnlyNext(IndexOnlyScanState *node);
-static void StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup,
- TupleDesc itupdesc);
+static void StoreIndexTuple(IndexOnlyScanState *node, TupleTableSlot *slot,
+ IndexTuple itup, TupleDesc itupdesc);
/* ----------------------------------------------------------------
@@ -208,7 +210,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
ExecForceStoreHeapTuple(scandesc->xs_hitup, slot, false);
}
else if (scandesc->xs_itup)
- StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc);
+ StoreIndexTuple(node, slot, scandesc->xs_itup, scandesc->xs_itupdesc);
else
elog(ERROR, "no data returned for index-only scan");
@@ -266,7 +268,8 @@ IndexOnlyNext(IndexOnlyScanState *node)
* right now we don't need it elsewhere.
*/
static void
-StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
+StoreIndexTuple(IndexOnlyScanState *node, TupleTableSlot *slot,
+ IndexTuple itup, TupleDesc itupdesc)
{
/*
* Note: we must use the tupdesc supplied by the AM in index_deform_tuple,
@@ -279,6 +282,37 @@ StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
ExecClearTuple(slot);
index_deform_tuple(itup, itupdesc, slot->tts_values, slot->tts_isnull);
+
+ /*
+ * Copy all name columns stored as cstrings back into a NAMEDATALEN byte
+ * sized allocation. We mark this branch as unlikely as generally "name"
+ * is used only for the system catalogs and this would have to be a user
+ * query running on those or some other user table with an index on a name
+ * column.
+ */
+ if (unlikely(node->ioss_NameCStringAttNums != NULL))
+ {
+ int attcount = node->ioss_NameCStringCount;
+
+ for (int idx = 0; idx < attcount; idx++)
+ {
+ int attnum = node->ioss_NameCStringAttNums[idx];
+ Name name;
+
+ /* skip null Datums */
+ if (slot->tts_isnull[attnum])
+ continue;
+
+ /* allocate the NAMEDATALEN and copy the datum into that memory */
+ name = (Name) MemoryContextAlloc(node->ss.ps.ps_ExprContext->ecxt_per_tuple_memory,
+ NAMEDATALEN);
+
+ /* use namestrcpy to zero-pad all trailing bytes */
+ namestrcpy(name, DatumGetCString(slot->tts_values[attnum]));
+ slot->tts_values[attnum] = NameGetDatum(name);
+ }
+ }
+
ExecStoreVirtualTuple(slot);
}
@@ -492,8 +526,11 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
{
IndexOnlyScanState *indexstate;
Relation currentRelation;
+ Relation indexRelation;
LOCKMODE lockmode;
TupleDesc tupDesc;
+ int indnkeyatts;
+ int namecount;
/*
* create state structure
@@ -566,7 +603,8 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
/* Open the index relation. */
lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
- indexstate->ioss_RelationDesc = index_open(node->indexid, lockmode);
+ indexRelation = index_open(node->indexid, lockmode);
+ indexstate->ioss_RelationDesc = indexRelation;
/*
* Initialize index-specific scan state
@@ -579,7 +617,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
* build the index scan keys from the index qualification
*/
ExecIndexBuildScanKeys((PlanState *) indexstate,
- indexstate->ioss_RelationDesc,
+ indexRelation,
node->indexqual,
false,
&indexstate->ioss_ScanKeys,
@@ -593,7 +631,7 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
* any ORDER BY exprs have to be turned into scankeys in the same way
*/
ExecIndexBuildScanKeys((PlanState *) indexstate,
- indexstate->ioss_RelationDesc,
+ indexRelation,
node->indexorderby,
true,
&indexstate->ioss_OrderByKeys,
@@ -622,6 +660,49 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
indexstate->ioss_RuntimeContext = NULL;
}
+ indexstate->ioss_NameCStringAttNums = NULL;
+ indnkeyatts = indexRelation->rd_index->indnkeyatts;
+ namecount = 0;
+
+ /*
+ * The "name" type for btree uses text_ops which results in storing
+ * cstrings in the indexed keys rather than names. Here we detect that in
+ * a generic way in case other index AMs want to do the same optimization.
+ * Check for opclasses with an opcintype of NAMEOID and an index tuple
+ * descriptor with CSTRINGOID. If any of these are found, create an array
+ * marking the index attribute number of each of them. StoreIndexTuple()
+ * handles copying the name Datums into a NAMEDATALEN-byte allocation.
+ */
+
+ /* First, count the number of such index keys */
+ for (int attnum = 0; attnum < indnkeyatts; attnum++)
+ {
+ if (indexRelation->rd_att->attrs[attnum].atttypid == CSTRINGOID &&
+ indexRelation->rd_opcintype[attnum] == NAMEOID)
+ namecount++;
+ }
+
+ if (namecount > 0)
+ {
+ int idx = 0;
+
+ /*
+ * Now create an array to mark the attribute numbers of the keys that
+ * need to be converted from cstring to name.
+ */
+ indexstate->ioss_NameCStringAttNums = (AttrNumber *)
+ palloc(sizeof(AttrNumber) * namecount);
+
+ for (int attnum = 0; attnum < indnkeyatts; attnum++)
+ {
+ if (indexRelation->rd_att->attrs[attnum].atttypid == CSTRINGOID &&
+ indexRelation->rd_opcintype[attnum] == NAMEOID)
+ indexstate->ioss_NameCStringAttNums[idx++] = (AttrNumber) attnum;
+ }
+ }
+
+ indexstate->ioss_NameCStringCount = namecount;
+
/*
* all done.
*/