On Wed, Feb 26, 2025 at 12:05 AM Tom Lane <t...@sss.pgh.pa.us> wrote:
> Corey Huinker <corey.huin...@gmail.com> writes: > > Just to confirm, we ARE able to assume dense packing of attributes in an > > index, and thus we can infer the attnum from the position of the attname > in > > the aggregated array, and there's no need to do a parallel array_agg of > > attnums, yes? > > Yes, absolutely, there are no dropped columns in indexes. See > upthread discussion. > > We could have avoided two sub-selects for attstattarget too, > on the same principle: just collect them all and sort it out > later. That'd risk bloating pg_dump's storage, although maybe > we could have handled that by doing additional processing > while inspecting the results of getIndexes' query, so as not > to store anything in the common case. > > regards, tom lane > 0001 - Add attnum support to attribute_statistics_update * Basically what Tom posted earlier, minus the pg_set_attribute_stats stuff, obviously. 0002 - Add attnum support to pg_dump. * Removed att_stats_arginfo * Folds appendRelStatsImport and appendAttStatsImport into dumpRelationStats
From 9dd7129318804ede9e412d886f3124e6d452a2d9 Mon Sep 17 00:00:00 2001 From: Corey Huinker <corey.huin...@gmail.com> Date: Wed, 26 Feb 2025 00:39:17 -0500 Subject: [PATCH vViaDellaAttnums 1/2] Add ability to reference columns by attnum to pg_restore_attribute_stats(). We need this because pg_dump needs to address index expression column statistics by attnum because the attribute name is not stable across upgrades. --- src/backend/statistics/attribute_stats.c | 84 +++++++++++++++++----- src/test/regress/expected/stats_import.out | 2 +- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/backend/statistics/attribute_stats.c b/src/backend/statistics/attribute_stats.c index 66a5676c810..71a7a175d1f 100644 --- a/src/backend/statistics/attribute_stats.c +++ b/src/backend/statistics/attribute_stats.c @@ -38,6 +38,7 @@ enum attribute_stats_argnum { ATTRELATION_ARG = 0, ATTNAME_ARG, + ATTNUM_ARG, INHERITED_ARG, NULL_FRAC_ARG, AVG_WIDTH_ARG, @@ -59,6 +60,7 @@ static struct StatsArgInfo attarginfo[] = { [ATTRELATION_ARG] = {"relation", REGCLASSOID}, [ATTNAME_ARG] = {"attname", NAMEOID}, + [ATTNUM_ARG] = {"attnum", INT2OID}, [INHERITED_ARG] = {"inherited", BOOLOID}, [NULL_FRAC_ARG] = {"null_frac", FLOAT4OID}, [AVG_WIDTH_ARG] = {"avg_width", INT4OID}, @@ -76,6 +78,22 @@ static struct StatsArgInfo attarginfo[] = [NUM_ATTRIBUTE_STATS_ARGS] = {0} }; +enum clear_attribute_stats_argnum +{ + C_ATTRELATION_ARG = 0, + C_ATTNAME_ARG, + C_INHERITED_ARG, + C_NUM_ATTRIBUTE_STATS_ARGS +}; + +static struct StatsArgInfo cleararginfo[] = +{ + [C_ATTRELATION_ARG] = {"relation", REGCLASSOID}, + [C_ATTNAME_ARG] = {"attname", NAMEOID}, + [C_INHERITED_ARG] = {"inherited", BOOLOID}, + [C_NUM_ATTRIBUTE_STATS_ARGS] = {0} +}; + static bool attribute_statistics_update(FunctionCallInfo fcinfo); static Node *get_attr_expr(Relation rel, int attnum); static void get_attr_stat_type(Oid reloid, AttrNumber attnum, @@ -116,9 +134,9 @@ static bool attribute_statistics_update(FunctionCallInfo fcinfo) { Oid reloid; - Name attname; - bool inherited; + char *attname; AttrNumber attnum; + bool inherited; Relation starel; HeapTuple statup; @@ -164,21 +182,51 @@ attribute_statistics_update(FunctionCallInfo fcinfo) /* lock before looking up attribute */ stats_lock_check_privileges(reloid); - stats_check_required_arg(fcinfo, attarginfo, ATTNAME_ARG); - attname = PG_GETARG_NAME(ATTNAME_ARG); - attnum = get_attnum(reloid, NameStr(*attname)); + /* user can specify either attname or attnum, but not both */ + if (!PG_ARGISNULL(ATTNAME_ARG)) + { + Name attnamename; + + if (!PG_ARGISNULL(ATTNUM_ARG)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("must specify one of attname and attnum"))); + attnamename = PG_GETARG_NAME(ATTNAME_ARG); + attname = NameStr(*attnamename); + attnum = get_attnum(reloid, attname); + /* note that this test covers attisdropped cases too: */ + if (attnum == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + attname, get_rel_name(reloid)))); + } + else if (!PG_ARGISNULL(ATTNUM_ARG)) + { + attnum = PG_GETARG_INT16(ATTNUM_ARG); + attname = get_attname(reloid, attnum, true); + /* Annoyingly, get_attname doesn't check attisdropped */ + if (attname == NULL || + !SearchSysCacheExistsAttName(reloid, attname)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, get_rel_name(reloid)))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("must specify one of attname and attnum"))); + attname = NULL; /* keep compiler quiet */ + attnum = 0; + } if (attnum < 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot modify statistics on system column \"%s\"", - NameStr(*attname)))); - - if (attnum == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - NameStr(*attname), get_rel_name(reloid)))); + attname))); stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG); inherited = PG_GETARG_BOOL(INHERITED_ARG); @@ -241,7 +289,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo) &elemtypid, &elem_eq_opr)) { ereport(WARNING, - (errmsg("unable to determine element type of attribute \"%s\"", NameStr(*attname)), + (errmsg("unable to determine element type of attribute \"%s\"", attname), errdetail("Cannot set STATISTIC_KIND_MCELEM or STATISTIC_KIND_DECHIST."))); elemtypid = InvalidOid; elem_eq_opr = InvalidOid; @@ -257,7 +305,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo) { ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not determine less-than operator for attribute \"%s\"", NameStr(*attname)), + errmsg("could not determine less-than operator for attribute \"%s\"", attname), errdetail("Cannot set STATISTIC_KIND_HISTOGRAM or STATISTIC_KIND_CORRELATION."))); do_histogram = false; @@ -271,7 +319,7 @@ attribute_statistics_update(FunctionCallInfo fcinfo) { ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("attribute \"%s\" is not a range type", NameStr(*attname)), + errmsg("attribute \"%s\" is not a range type", attname), errdetail("Cannot set STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM or STATISTIC_KIND_BOUNDS_HISTOGRAM."))); do_bounds_histogram = false; @@ -857,7 +905,7 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS) AttrNumber attnum; bool inherited; - stats_check_required_arg(fcinfo, attarginfo, ATTRELATION_ARG); + stats_check_required_arg(fcinfo, cleararginfo, C_ATTRELATION_ARG); reloid = PG_GETARG_OID(ATTRELATION_ARG); if (RecoveryInProgress()) @@ -868,7 +916,7 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS) stats_lock_check_privileges(reloid); - stats_check_required_arg(fcinfo, attarginfo, ATTNAME_ARG); + stats_check_required_arg(fcinfo, cleararginfo, C_ATTNAME_ARG); attname = PG_GETARG_NAME(ATTNAME_ARG); attnum = get_attnum(reloid, NameStr(*attname)); @@ -884,7 +932,7 @@ pg_clear_attribute_stats(PG_FUNCTION_ARGS) errmsg("column \"%s\" of relation \"%s\" does not exist", NameStr(*attname), get_rel_name(reloid)))); - stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG); + stats_check_required_arg(fcinfo, attarginfo, C_INHERITED_ARG); inherited = PG_GETARG_BOOL(INHERITED_ARG); delete_pg_statistic(reloid, attnum, inherited); diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out index 7e8b7f429c9..b8fc2fcce46 100644 --- a/src/test/regress/expected/stats_import.out +++ b/src/test/regress/expected/stats_import.out @@ -1250,7 +1250,7 @@ SELECT pg_catalog.pg_restore_attribute_stats( 'null_frac', 0.1::real, 'avg_width', 2::integer, 'n_distinct', 0.3::real); -ERROR: "attname" cannot be NULL +ERROR: must specify one of attname and attnum -- error: attname doesn't exist SELECT pg_catalog.pg_restore_attribute_stats( 'relation', 'stats_import.test'::regclass, base-commit: 0e42d31b0b2273c376ce9de946b59d155fac589c -- 2.48.1
From 92f75fc9835e7982216d32329caa34b26347a6b3 Mon Sep 17 00:00:00 2001 From: Corey Huinker <corey.huin...@gmail.com> Date: Wed, 26 Feb 2025 04:08:31 -0500 Subject: [PATCH vViaDellaAttnums 2/2] Dump expression index stats by attnum, not attname. Names of columns in index expressions are not stable across major versions, so we are forced to dump those by attnum instead. --- src/bin/pg_dump/pg_dump.c | 219 +++++++++++++++++++++----------------- src/bin/pg_dump/pg_dump.h | 2 + 2 files changed, 125 insertions(+), 96 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 0de6c959bb0..65413e07899 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -6819,7 +6819,8 @@ getFuncs(Archive *fout) */ static RelStatsInfo * getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages, - float reltuples, int32 relallvisible, char relkind) + float reltuples, int32 relallvisible, char relkind, + char **indexprattnames, int nindexprattnames) { if (!fout->dopt->dumpStatistics) return NULL; @@ -6844,6 +6845,8 @@ getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages, dobj->components |= DUMP_COMPONENT_STATISTICS; dobj->name = pg_strdup(rel->name); dobj->namespace = rel->namespace; + info->indexprattnames = indexprattnames; + info->nindexprattnames = nindexprattnames; info->relpages = relpages; info->reltuples = reltuples; info->relallvisible = relallvisible; @@ -7249,7 +7252,8 @@ getTables(Archive *fout, int *numTables) /* Add statistics */ if (tblinfo[i].interesting) getRelationStatistics(fout, &tblinfo[i].dobj, tblinfo[i].relpages, - reltuples, relallvisible, tblinfo[i].relkind); + reltuples, relallvisible, tblinfo[i].relkind, + NULL, 0); /* * Read-lock target tables to make sure they aren't DROPPED or altered @@ -7537,7 +7541,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) i_tablespace, i_indreloptions, i_indstatcols, - i_indstatvals; + i_indstatvals, + i_indexprattnames; /* * We want to perform just one query against pg_index. However, we @@ -7579,6 +7584,10 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) "c.tableoid AS contableoid, " "c.oid AS conoid, " "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, " + "(SELECT pg_catalog.array_agg(attname ORDER BY attnum) " + " FROM pg_catalog.pg_attribute " + " WHERE attrelid = i.indexrelid AND " + " i.indexprs IS NOT NULL) AS indexprattnames, " "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, " "t.reloptions AS indreloptions, "); @@ -7702,6 +7711,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) i_indreloptions = PQfnumber(res, "indreloptions"); i_indstatcols = PQfnumber(res, "indstatcols"); i_indstatvals = PQfnumber(res, "indstatvals"); + i_indexprattnames = PQfnumber(res, "indexprattnames"); indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo)); @@ -7715,6 +7725,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid)); TableInfo *tbinfo = NULL; int numinds; + char **indexprattnames = NULL; /* attnames for expression indexes only */ + int nindexprattnames = 0; /* number of attnames for expression indexes only */ /* Count rows for this table */ for (numinds = 1; numinds < ntups - j; numinds++) @@ -7784,9 +7796,16 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables) else indexkind = RELKIND_PARTITIONED_INDEX; - contype = *(PQgetvalue(res, j, i_contype)); + if (!PQgetisnull(res, j, i_indexprattnames)) + if (!parsePGArray(PQgetvalue(res, j, i_indexprattnames), + &indexprattnames, &nindexprattnames)) + pg_fatal("could not parse %s array", "indattnames"); + relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages, - reltuples, relallvisible, indexkind); + reltuples, relallvisible, indexkind, + indexprattnames, nindexprattnames); + + contype = *(PQgetvalue(res, j, i_contype)); if (contype == 'p' || contype == 'u' || contype == 'x') { @@ -10410,28 +10429,6 @@ dumpComment(Archive *fout, const char *type, catalogId, subid, dumpId, NULL); } -/* - * Tabular description of the parameters to pg_restore_attribute_stats() - * param_name, param_type - */ -static const char *att_stats_arginfo[][2] = { - {"attname", "name"}, - {"inherited", "boolean"}, - {"null_frac", "float4"}, - {"avg_width", "integer"}, - {"n_distinct", "float4"}, - {"most_common_vals", "text"}, - {"most_common_freqs", "float4[]"}, - {"histogram_bounds", "text"}, - {"correlation", "float4"}, - {"most_common_elems", "text"}, - {"most_common_elem_freqs", "float4[]"}, - {"elem_count_histogram", "float4[]"}, - {"range_length_histogram", "text"}, - {"range_empty_frac", "float4"}, - {"range_bounds_histogram", "text"}, -}; - /* * appendNamedArgument -- * @@ -10440,9 +10437,9 @@ static const char *att_stats_arginfo[][2] = { */ static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, - const char *argval, const char *argtype) + const char *argtype, const char *argval) { - appendPQExpBufferStr(out, "\t"); + appendPQExpBufferStr(out, ",\n\t"); appendStringLiteralAH(out, argname, fout); appendPQExpBufferStr(out, ", "); @@ -10451,68 +10448,6 @@ appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, appendPQExpBuffer(out, "::%s", argtype); } -/* - * appendRelStatsImport -- - * - * Append a formatted pg_restore_relation_stats statement. - */ -static void -appendRelStatsImport(PQExpBuffer out, Archive *fout, const RelStatsInfo *rsinfo, - const char *qualified_name) -{ - char reltuples_str[FLOAT_SHORTEST_DECIMAL_LEN]; - - float_to_shortest_decimal_buf(rsinfo->reltuples, reltuples_str); - - appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n"); - appendPQExpBuffer(out, "\t'version', '%u'::integer,\n", - fout->remoteVersion); - appendPQExpBuffer(out, "\t'relation', '%s'::regclass,\n", qualified_name); - appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages); - appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", reltuples_str); - appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer\n);\n", - rsinfo->relallvisible); -} - -/* - * appendAttStatsImport -- - * - * Append a series of formatted pg_restore_attribute_stats statements. - */ -static void -appendAttStatsImport(PQExpBuffer out, Archive *fout, PGresult *res, - const char *qualified_name) -{ - for (int rownum = 0; rownum < PQntuples(res); rownum++) - { - const char *sep = ""; - - appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n"); - appendPQExpBuffer(out, "\t'version', '%u'::integer,\n", - fout->remoteVersion); - appendPQExpBuffer(out, "\t'relation', '%s'::regclass,\n", - qualified_name); - for (int argno = 0; argno < lengthof(att_stats_arginfo); argno++) - { - const char *argname = att_stats_arginfo[argno][0]; - const char *argtype = att_stats_arginfo[argno][1]; - int fieldno = PQfnumber(res, argname); - - if (fieldno < 0) - pg_fatal("attribute stats export query missing field '%s'", - argname); - - if (PQgetisnull(res, rownum, fieldno)) - continue; - - appendPQExpBufferStr(out, sep); - appendNamedArgument(out, fout, argname, PQgetvalue(res, rownum, fieldno), argtype); - sep = ",\n"; - } - appendPQExpBufferStr(out, "\n);\n"); - } -} - /* * Decide which section to use based on the relkind of the parent object. * @@ -10557,6 +10492,7 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo) DumpId *deps = NULL; int ndeps = 0; const char *qualified_name; + char reltuples_str[FLOAT_SHORTEST_DECIMAL_LEN]; /* nothing to do if we are not dumping statistics */ if (!fout->dopt->dumpStatistics) @@ -10606,6 +10542,22 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo) resetPQExpBuffer(query); } + out = createPQExpBuffer(); + + qualified_name = fmtQualifiedId(rsinfo->dobj.namespace->dobj.name, + rsinfo->dobj.name); + + /* restore relation stats */ + float_to_shortest_decimal_buf(rsinfo->reltuples, reltuples_str); + appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n"); + appendPQExpBuffer(out, "\t'version', '%u'::integer,\n", + fout->remoteVersion); + appendPQExpBuffer(out, "\t'relation', '%s'::regclass,\n", qualified_name); + appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages); + appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", reltuples_str); + appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer\n);\n", + rsinfo->relallvisible); + appendPQExpBufferStr(query, "EXECUTE getAttributeStats("); appendStringLiteralAH(query, dobj->namespace->dobj.name, fout); appendPQExpBufferStr(query, ", "); @@ -10614,13 +10566,88 @@ dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo) res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); - out = createPQExpBuffer(); + /* restore attribute stats */ + for (int rownum = 0; rownum < PQntuples(res); rownum++) + { + const char *attname; - qualified_name = fmtQualifiedId(rsinfo->dobj.namespace->dobj.name, - rsinfo->dobj.name); + appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n"); + appendPQExpBuffer(out, "\t'version', '%u'::integer,\n", + fout->remoteVersion); + appendPQExpBuffer(out, "\t'relation', '%s'::regclass", + qualified_name); - appendRelStatsImport(out, fout, rsinfo, qualified_name); - appendAttStatsImport(out, fout, res, qualified_name); + + if (PQgetisnull(res, rownum, 0)) + pg_fatal("attname cannot be NULL"); + attname = PQgetvalue(res, rownum, 0); + + /* + * Expression indexes look up attname in attnames to derive attnum, + * all others use attname directly. + */ + if (rsinfo->nindexprattnames == 0) + appendNamedArgument(out, fout, "attname", "name", attname); + else + { + bool found = false; + + for (int i = 0; i < rsinfo->nindexprattnames; i++) + if (strcmp(attname, rsinfo->indexprattnames[i]) == 0) + { + appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint", (AttrNumber) i + 1); + found = true; + break; + } + + if (!found) + pg_fatal("unable to find attname '%s'", attname); + } + + if (!PQgetisnull(res, rownum, 1)) + appendNamedArgument(out, fout, "inherited", "boolean", + PQgetvalue(res, rownum, 1)); + if (!PQgetisnull(res, rownum, 2)) + appendNamedArgument(out, fout, "null_frac", "float4", + PQgetvalue(res, rownum, 2)); + if (!PQgetisnull(res, rownum, 3)) + appendNamedArgument(out, fout, "avg_width", "integer", + PQgetvalue(res, rownum, 3)); + if (!PQgetisnull(res, rownum, 4)) + appendNamedArgument(out, fout, "n_distinct", "float4", + PQgetvalue(res, rownum, 4)); + if (!PQgetisnull(res, rownum, 5)) + appendNamedArgument(out, fout, "most_common_vals", "text", + PQgetvalue(res, rownum, 5)); + if (!PQgetisnull(res, rownum, 6)) + appendNamedArgument(out, fout, "most_common_freqs", "float4[]", + PQgetvalue(res, rownum, 6)); + if (!PQgetisnull(res, rownum, 7)) + appendNamedArgument(out, fout, "histogram_bounds", "text", + PQgetvalue(res, rownum, 7)); + if (!PQgetisnull(res, rownum, 8)) + appendNamedArgument(out, fout, "correlation", "float4", + PQgetvalue(res, rownum, 8)); + if (!PQgetisnull(res, rownum, 9)) + appendNamedArgument(out, fout, "most_common_elems", "text", + PQgetvalue(res, rownum, 9)); + if (!PQgetisnull(res, rownum, 10)) + appendNamedArgument(out, fout, "most_common_elem_freqs", "float4[]", + PQgetvalue(res, rownum, 10)); + if (!PQgetisnull(res, rownum, 11)) + appendNamedArgument(out, fout, "elem_count_histogram", "float4[]", + PQgetvalue(res, rownum, 11)); + if (!PQgetisnull(res, rownum, 12)) + appendNamedArgument(out, fout, "range_length_histogram", "text", + PQgetvalue(res, rownum, 12)); + if (!PQgetisnull(res, rownum, 13)) + appendNamedArgument(out, fout, "range_empty_frac", "float4", + PQgetvalue(res, rownum, 13)); + if (!PQgetisnull(res, rownum, 14)) + appendNamedArgument(out, fout, "range_bounds_histogram", "text", + PQgetvalue(res, rownum, 14)); + appendPQExpBufferStr(out, "\n);\n"); + } PQclear(res); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 9d6a4857c4b..0584b1c7abb 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -438,6 +438,8 @@ typedef struct _indexAttachInfo typedef struct _relStatsInfo { DumpableObject dobj; + char **indexprattnames; /* attnames in an expression index */ + int32 nindexprattnames; /* number of attnames for expression indexes, else 0 */ int32 relpages; float reltuples; int32 relallvisible; -- 2.48.1