Partition of: pt FOR VALUES IN (1, 2)
Partition constraint: ((category IS NOT NULL) AND (category = ANY (ARRAY[1, 2])))
Indexes:
- "pt12_expr_idx" btree ((mydouble(category) + 1))
+ "pt12_mydouble_category_1_idx" btree ((mydouble(category) + 1))
"pt12_sdata_idx" btree (sdata)
"pt12_tdata_idx" btree (tdata COLLATE mycollation)
#include "utils/syscache.h"
+/* context for ChooseIndexExpressionName_walker */
+typedef struct CIEN_context
+{
+ Relation rel; /* index's parent relation */
+ StringInfo buf; /* output string */
+} CIEN_context;
+
/* non-export function prototypes */
static bool CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts);
static void CheckPredicate(Expr *predicate);
const List *colnames, const List *exclusionOpNames,
bool primary, bool isconstraint);
static char *ChooseIndexNameAddition(const List *colnames);
-static List *ChooseIndexColumnNames(const List *indexElems);
+static List *ChooseIndexColumnNames(Relation rel, const List *indexElems);
+static char *ChooseIndexExpressionName(Relation rel, Node *indexExpr);
+static bool ChooseIndexExpressionName_walker(Node *node,
+ CIEN_context *context);
static void ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params,
bool isTopLevel);
static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
/*
* Choose the index column names.
*/
- indexColNames = ChooseIndexColumnNames(allIndexParams);
+ indexColNames = ChooseIndexColumnNames(rel, allIndexParams);
/*
* Select name for index if caller didn't specify
/*
* Select the actual names to be used for the columns of an index, given the
- * list of IndexElems for the columns. This is mostly about ensuring the
- * names are unique so we don't get a conflicting-attribute-names error.
+ * parent Relation and the list of IndexElems for the columns. The logic in
+ * this function is mostly about ensuring the names are unique so we don't
+ * get a conflicting-attribute-names error.
*
* Returns a List of plain strings (char *, not String nodes).
*/
static List *
-ChooseIndexColumnNames(const List *indexElems)
+ChooseIndexColumnNames(Relation rel, const List *indexElems)
{
List *result = NIL;
ListCell *lc;
else if (ielem->name)
origname = ielem->name; /* simple column reference */
else
- origname = "expr"; /* default name for expression */
+ origname = ChooseIndexExpressionName(rel, ielem->expr);
/* If it conflicts with any previous column, tweak it */
curname = origname;
return result;
}
+/*
+ * Generate a suitable index-column name for an index expression.
+ *
+ * Our strategy is to collect the Var names, Const values, and function names
+ * appearing in the expression, and print them separated by underscores.
+ * We could expend a lot more effort to handle additional expression node
+ * types, but this seems sufficient to usually produce a column name distinct
+ * from other index expressions.
+ */
+static char *
+ChooseIndexExpressionName(Relation rel, Node *indexExpr)
+{
+ StringInfoData buf;
+ CIEN_context context;
+ int nlen;
+
+ /* Prepare ... */
+ initStringInfo(&buf);
+ context.rel = rel;
+ context.buf = &buf;
+ /* Walk the tree, stopping when we have enough text */
+ (void) ChooseIndexExpressionName_walker(indexExpr, &context);
+ /* Ensure generated names are shorter than NAMEDATALEN */
+ nlen = pg_mbcliplen(buf.data, buf.len, NAMEDATALEN - 1);
+ buf.data[nlen] = '\0';
+ return buf.data;
+}
+
+/* Recursive guts of ChooseIndexExpressionName */
+static bool
+ChooseIndexExpressionName_walker(Node *node,
+ CIEN_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ TupleDesc tupdesc = RelationGetDescr(context->rel);
+ Form_pg_attribute att;
+
+ /* Paranoia: ignore the Var if it looks fishy */
+ if (var->varno != 1 || var->varlevelsup != 0 ||
+ var->varattno <= 0 || var->varattno > tupdesc->natts)
+ return false;
+ att = TupleDescAttr(tupdesc, var->varattno - 1);
+ if (att->attisdropped)
+ return false; /* even more paranoia; shouldn't happen */
+
+ if (context->buf->len > 0)
+ appendStringInfoChar(context->buf, '_');
+ appendStringInfoString(context->buf, NameStr(att->attname));
+
+ /* Done if we've already reached NAMEDATALEN */
+ return (context->buf->len >= NAMEDATALEN);
+ }
+ else if (IsA(node, Const))
+ {
+ Const *constval = (Const *) node;
+
+ if (context->buf->len > 0)
+ appendStringInfoChar(context->buf, '_');
+ if (constval->constisnull)
+ appendStringInfoString(context->buf, "NULL");
+ else
+ {
+ Oid typoutput;
+ bool typIsVarlena;
+ char *extval;
+
+ getTypeOutputInfo(constval->consttype,
+ &typoutput, &typIsVarlena);
+ extval = OidOutputFunctionCall(typoutput, constval->constvalue);
+
+ /*
+ * We sanitize constant values by dropping non-alphanumeric ASCII
+ * characters. This is probably not really necessary, but it
+ * reduces the odds of needing to double-quote the generated name.
+ */
+ for (const char *ptr = extval; *ptr; ptr++)
+ {
+ if (IS_HIGHBIT_SET(*ptr) ||
+ strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789", *ptr) != NULL)
+ appendStringInfoChar(context->buf, *ptr);
+ }
+ }
+
+ /* Done if we've already reached NAMEDATALEN */
+ return (context->buf->len >= NAMEDATALEN);
+ }
+ else if (IsA(node, FuncExpr))
+ {
+ FuncExpr *funcexpr = (FuncExpr *) node;
+ char *fname = get_func_name(funcexpr->funcid);
+
+ if (fname)
+ {
+ if (context->buf->len > 0)
+ appendStringInfoChar(context->buf, '_');
+ appendStringInfoString(context->buf, fname);
+ }
+ /* fall through to examine arguments */
+ }
+
+ /* Abandon recursion once we reach NAMEDATALEN */
+ if (context->buf->len >= NAMEDATALEN)
+ return true;
+
+ return expression_tree_walker(node, ChooseIndexExpressionName_walker,
+ context);
+}
+
/*
* ExecReindex
*
return "?column?";
}
-/*
- * FigureIndexColname -
- * choose the name for an expression column in an index
- *
- * This is actually just like FigureColname, except we return NULL if
- * we can't pick a good name.
- */
-char *
-FigureIndexColname(Node *node)
-{
- char *name = NULL;
-
- (void) FigureColnameInternal(node, &name);
- return name;
-}
-
/*
* FigureColnameInternal -
* internal workhorse for FigureColname
if (ielem->expr)
{
- /* Extract preliminary index col name before transforming expr */
- if (ielem->indexcolname == NULL)
- ielem->indexcolname = FigureIndexColname(ielem->expr);
-
- /* Now do parse transformation of the expression */
+ /* Do parse transformation of the expression */
ielem->expr = transformExpr(pstate, ielem->expr,
EXPR_KIND_INDEX_EXPRESSION);
*
* For a plain index attribute, 'name' is the name of the table column to
* index, and 'expr' is NULL. For an index expression, 'name' is NULL and
- * 'expr' is the expression tree.
+ * 'expr' is the expression tree. indexcolname is currently used only to
+ * force column name choices when cloning an index.
*/
typedef struct IndexElem
{
extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
int levelsup);
extern char *FigureColname(Node *node);
-extern char *FigureIndexColname(Node *node);
#endif /* PARSE_TARGET_H */
Column | Type | Key? | Definition | Storage | Stats target
--------+------------------+------+------------+---------+--------------
a | integer | yes | a | plain |
- expr | double precision | yes | (d + e) | plain | 1000
+ d_e | double precision | yes | (d + e) | plain | 1000
b | cstring | yes | b | plain |
btree, for table "public.attmp"
CREATE TABLE test_tbl1 (x int, y text);
CREATE INDEX test_tbl1_idx ON test_tbl1((row(x,y)::test_type1));
ALTER TYPE test_type1 ALTER ATTRIBUTE b TYPE varchar; -- fails
-ERROR: cannot alter type "test_type1" because column "test_tbl1_idx.row" uses it
+ERROR: cannot alter type "test_type1" because column "test_tbl1_idx.x_y" uses it
DROP TABLE test_tbl1;
DROP TYPE test_type1;
CREATE TYPE test_type2 AS (a int, b text);
--------+------+-----------+----------+---------
f1 | text | | |
Indexes:
- "ataddindex_expr_excl" EXCLUDE USING btree ((f1 ~~ 'a'::text) WITH =)
+ "ataddindex_f1_a_excl" EXCLUDE USING btree ((f1 ~~ 'a'::text) WITH =)
DROP TABLE ataddindex;
CREATE TABLE ataddindex(id int, ref_id int);
"func_index_index" UNIQUE, btree (textcat(f1, f2))
\d func_index_index
- Index "public.func_index_index"
- Column | Type | Key? | Definition
----------+------+------+-----------------
- textcat | text | yes | textcat(f1, f2)
+ Index "public.func_index_index"
+ Column | Type | Key? | Definition
+---------------+------+------+-----------------
+ textcat_f1_f2 | text | yes | textcat(f1, f2)
unique, btree, for table "public.func_index_heap"
--
Index "public.func_index_index"
Column | Type | Key? | Definition
--------+------+------+------------
- expr | text | yes | (f1 || f2)
+ f1_f2 | text | yes | (f1 || f2)
unique, btree, for table "public.func_index_heap"
-- this should fail because of unsafe column type (anonymous record)
create index on func_index_heap ((f1 || f2), (row(f1, f2)));
-ERROR: column "row" has pseudo-type record
+ERROR: column "f1_f21" has pseudo-type record
--
-- Test unique index with included columns
--
f1 | text | | |
f2 | text | | |
Indexes:
- "concur_heap_expr_idx" btree ((f2 || f1))
+ "concur_heap_f2_f1_idx" btree ((f2 || f1))
"concur_index1" btree (f2, f1)
"concur_index2" UNIQUE, btree (f1)
"concur_index3" UNIQUE, btree (f2) INVALID
f1 | text | | |
f2 | text | | |
Indexes:
- "concur_heap_expr_idx" btree ((f2 || f1))
+ "concur_heap_f2_f1_idx" btree ((f2 || f1))
"concur_index1" btree (f2, f1)
"concur_index2" UNIQUE, btree (f1)
"concur_index3" UNIQUE, btree (f2)
DROP INDEX CONCURRENTLY "concur_index4";
DROP INDEX CONCURRENTLY "concur_index5";
DROP INDEX CONCURRENTLY "concur_index1";
-DROP INDEX CONCURRENTLY "concur_heap_expr_idx";
+DROP INDEX CONCURRENTLY "concur_heap_f2_f1_idx";
\d concur_heap
Table "public.concur_heap"
Column | Type | Collation | Nullable | Default
b | integer | | |
Partition of: part_column_drop FOR VALUES FROM (1) TO (10)
Indexes:
+ "part_column_drop_1_10_b_1_idx" btree ((b = 1))
"part_column_drop_1_10_b_idx" btree (b) WHERE b = 1
+ "part_column_drop_1_10_d_2_idx" btree ((d = 2))
"part_column_drop_1_10_d_idx" btree (d) WHERE d = 2
- "part_column_drop_1_10_expr_idx" btree ((b = 1))
- "part_column_drop_1_10_expr_idx1" btree ((d = 2))
drop table part_column_drop;
b | text | | | | extended | |
c | text | | | | external | | C
Indexes:
- "ctlt13_like_expr_idx" btree ((a || c))
+ "ctlt13_like_a_c_idx" btree ((a || c))
Check constraints:
"cc" CHECK (length(b) > 100)
"ctlt1_a_check" CHECK (length(a) > 2)
b | text | | | | extended | | B
Indexes:
"ctlt_all_pkey" PRIMARY KEY, btree (a)
+ "ctlt_all_a_b_idx" btree ((a || b))
"ctlt_all_b_idx" btree (b)
- "ctlt_all_expr_idx" btree ((a || b))
Check constraints:
"cc" CHECK (length(b) > 100)
"ctlt1_a_check" CHECK (length(a) > 2)
b | text | | | | extended | | B
Indexes:
"pg_attrdef_pkey" PRIMARY KEY, btree (a)
+ "pg_attrdef_a_b_idx" btree ((a || b))
"pg_attrdef_b_idx" btree (b)
- "pg_attrdef_expr_idx" btree ((a || b))
Check constraints:
"cc" CHECK (length(b) > 100)
"ctlt1_a_check" CHECK (length(a) > 2)
b | text | | | | extended | | B
Indexes:
"ctlt1_pkey" PRIMARY KEY, btree (a)
+ "ctlt1_a_b_idx" btree ((a || b))
"ctlt1_b_idx" btree (b)
- "ctlt1_expr_idx" btree ((a || b))
Check constraints:
"cc" CHECK (length(b) > 100)
"ctlt1_a_check" CHECK (length(a) > 2)
b | integer | | |
Partition of: idxpart FOR VALUES FROM (0) TO (1000)
Indexes:
+ "idxpart1_a_0_idx" btree ((a + 0))
"idxpart1_a_a1_idx" btree (a, a)
"idxpart1_a_idx" hash (a)
"idxpart1_a_idx1" btree (a) WHERE b > 1
"idxpart1_a_idx2" btree (a)
- "idxpart1_expr_idx" btree ((a + 0))
drop table idxpart;
-- If CREATE INDEX ONLY, don't create indexes on partitions; and existing
d | boolean | | |
Partition of: idxpart FOR VALUES FROM (0) TO (10)
Indexes:
+ "idxpart1_a_b_idx" btree ((a + b)) WHERE d = true
"idxpart1_a_idx" btree (a)
"idxpart1_b_c_idx" btree (b, c)
- "idxpart1_expr_idx" btree ((a + b)) WHERE d = true
select relname, relkind, inhparent::regclass
from pg_class left join pg_index ix on (indexrelid = oid)
left join pg_inherits on (ix.indexrelid = inhrelid)
where relname like 'idxpart%' order by relname;
- relname | relkind | inhparent
--------------------+---------+-----------
- idxpart | p |
- idxpart1 | r |
- idxpart1_a_idx | i | idxparti
- idxpart1_b_c_idx | i | idxparti2
- idxpart1_expr_idx | i |
- idxparti | I |
- idxparti2 | I |
+ relname | relkind | inhparent
+------------------+---------+-----------
+ idxpart | p |
+ idxpart1 | r |
+ idxpart1_a_b_idx | i |
+ idxpart1_a_idx | i | idxparti
+ idxpart1_b_c_idx | i | idxparti2
+ idxparti | I |
+ idxparti2 | I |
(7 rows)
create index idxparti3 on idxpart ((a+b)) where d = true;
d | boolean | | |
Partition of: idxpart FOR VALUES FROM (0) TO (10)
Indexes:
+ "idxpart1_a_b_idx" btree ((a + b)) WHERE d = true
"idxpart1_a_idx" btree (a)
"idxpart1_b_c_idx" btree (b, c)
- "idxpart1_expr_idx" btree ((a + b)) WHERE d = true
select relname, relkind, inhparent::regclass
from pg_class left join pg_index ix on (indexrelid = oid)
left join pg_inherits on (ix.indexrelid = inhrelid)
where relname like 'idxpart%' order by relname;
- relname | relkind | inhparent
--------------------+---------+-----------
- idxpart | p |
- idxpart1 | r |
- idxpart1_a_idx | i | idxparti
- idxpart1_b_c_idx | i | idxparti2
- idxpart1_expr_idx | i | idxparti3
- idxparti | I |
- idxparti2 | I |
- idxparti3 | I |
+ relname | relkind | inhparent
+------------------+---------+-----------
+ idxpart | p |
+ idxpart1 | r |
+ idxpart1_a_b_idx | i | idxparti3
+ idxpart1_a_idx | i | idxparti
+ idxpart1_b_c_idx | i | idxparti2
+ idxparti | I |
+ idxparti2 | I |
+ idxparti3 | I |
(8 rows)
drop table idxpart;
ERROR: division by zero
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
- relname | indisvalid
---------------------+------------
- idxpart11_expr_idx | f
- idxpart1_expr_idx | f
- idxpart2_expr_idx | t
- idxpart_expr_idx | f
+ relname | indisvalid
+-------------------+------------
+ idxpart11_a_b_idx | f
+ idxpart1_a_b_idx | f
+ idxpart2_a_b_idx | t
+ idxpart_a_b_idx | f
(4 rows)
-- attach the indexes; parents stay invalid
-alter index idxpart1_expr_idx attach partition idxpart11_expr_idx;
-alter index idxpart_expr_idx attach partition idxpart1_expr_idx;
-alter index idxpart_expr_idx attach partition idxpart2_expr_idx;
+alter index idxpart1_a_b_idx attach partition idxpart11_a_b_idx;
+alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
+alter index idxpart_a_b_idx attach partition idxpart2_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
- relname | indisvalid
---------------------+------------
- idxpart11_expr_idx | f
- idxpart1_expr_idx | f
- idxpart2_expr_idx | t
- idxpart_expr_idx | f
+ relname | indisvalid
+-------------------+------------
+ idxpart11_a_b_idx | f
+ idxpart1_a_b_idx | f
+ idxpart2_a_b_idx | t
+ idxpart_a_b_idx | f
(4 rows)
-- fix the index on the leaf partition
delete from idxpart11 where b = 0;
-reindex index concurrently idxpart11_expr_idx;
+reindex index concurrently idxpart11_a_b_idx;
-- reattach the leaf partition index; parents should now be valid
-alter index idxpart1_expr_idx attach partition idxpart11_expr_idx;
+alter index idxpart1_a_b_idx attach partition idxpart11_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
- relname | indisvalid
---------------------+------------
- idxpart11_expr_idx | t
- idxpart1_expr_idx | t
- idxpart2_expr_idx | t
- idxpart_expr_idx | t
+ relname | indisvalid
+-------------------+------------
+ idxpart11_a_b_idx | t
+ idxpart1_a_b_idx | t
+ idxpart2_a_b_idx | t
+ idxpart_a_b_idx | t
(4 rows)
drop table idxpart;
ERROR: division by zero
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
- relname | indisvalid
--------------------+------------
- idxpart1_expr_idx | f
- idxpart2_expr_idx | f
- idxpart_expr_idx | f
+ relname | indisvalid
+------------------+------------
+ idxpart1_a_b_idx | f
+ idxpart2_a_b_idx | f
+ idxpart_a_b_idx | f
(3 rows)
-- attach both; parent stays invalid
-alter index idxpart_expr_idx attach partition idxpart1_expr_idx;
-alter index idxpart_expr_idx attach partition idxpart2_expr_idx;
+alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
+alter index idxpart_a_b_idx attach partition idxpart2_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
- relname | indisvalid
--------------------+------------
- idxpart1_expr_idx | f
- idxpart2_expr_idx | f
- idxpart_expr_idx | f
+ relname | indisvalid
+------------------+------------
+ idxpart1_a_b_idx | f
+ idxpart2_a_b_idx | f
+ idxpart_a_b_idx | f
(3 rows)
-- fix only idxpart1's index, leave idxpart2's still invalid
delete from idxpart1 where b = 0;
-reindex index concurrently idxpart1_expr_idx;
+reindex index concurrently idxpart1_a_b_idx;
-- re-attach the fixed child; parent should stay invalid
-alter index idxpart_expr_idx attach partition idxpart1_expr_idx;
+alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
- relname | indisvalid
--------------------+------------
- idxpart1_expr_idx | t
- idxpart2_expr_idx | f
- idxpart_expr_idx | f
+ relname | indisvalid
+------------------+------------
+ idxpart1_a_b_idx | t
+ idxpart2_a_b_idx | f
+ idxpart_a_b_idx | f
(3 rows)
drop table idxpart;
from pg_class join pg_inherits on inhrelid = oid,
lateral pg_get_indexdef(pg_class.oid)
where relkind in ('i', 'I') and relname like 'idxpart%' order by relname;
- child | parent | childdef
--------------------+------------------+---------------------------------------------------------------------------
- idxpart1_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart1_expr_idx ON public.idxpart1 USING btree (((a + b)))
- idxpart2_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart2_expr_idx ON public.idxpart2 USING btree (((a + b)))
- idxpart3_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart3_expr_idx ON public.idxpart3 USING btree (((a + b)))
+ child | parent | childdef
+------------------+-----------------+--------------------------------------------------------------------------
+ idxpart1_a_b_idx | idxpart_a_b_idx | CREATE INDEX idxpart1_a_b_idx ON public.idxpart1 USING btree (((a + b)))
+ idxpart2_a_b_idx | idxpart_a_b_idx | CREATE INDEX idxpart2_a_b_idx ON public.idxpart2 USING btree (((a + b)))
+ idxpart3_a_b_idx | idxpart_a_b_idx | CREATE INDEX idxpart3_a_b_idx ON public.idxpart3 USING btree (((a + b)))
(3 rows)
drop table idxpart;
from pg_class c join pg_index i on c.oid = i.indexrelid
where indrelid::regclass::text like 'idxpart%'
order by indexrelid::regclass::text collate "C";
- relname | pg_get_indexdef
--------------------+------------------------------------------------------------------------------
- idxpart1_abs_idx | CREATE INDEX idxpart1_abs_idx ON public.idxpart1 USING btree (abs(b))
- idxpart1_expr_idx | CREATE INDEX idxpart1_expr_idx ON public.idxpart1 USING btree (((b + 1)))
- idxpart2_abs_idx | CREATE INDEX idxpart2_abs_idx ON public.idxpart2 USING btree (abs(b))
- idxpart2_expr_idx | CREATE INDEX idxpart2_expr_idx ON public.idxpart2 USING btree (((b + 1)))
- idxpart_abs_idx | CREATE INDEX idxpart_abs_idx ON ONLY public.idxpart USING btree (abs(b))
- idxpart_expr_idx | CREATE INDEX idxpart_expr_idx ON ONLY public.idxpart USING btree (((b + 1)))
+ relname | pg_get_indexdef
+--------------------+-----------------------------------------------------------------------------
+ idxpart1_abs_b_idx | CREATE INDEX idxpart1_abs_b_idx ON public.idxpart1 USING btree (abs(b))
+ idxpart1_b_1_idx | CREATE INDEX idxpart1_b_1_idx ON public.idxpart1 USING btree (((b + 1)))
+ idxpart2_abs_b_idx | CREATE INDEX idxpart2_abs_b_idx ON public.idxpart2 USING btree (abs(b))
+ idxpart2_b_1_idx | CREATE INDEX idxpart2_b_1_idx ON public.idxpart2 USING btree (((b + 1)))
+ idxpart_abs_b_idx | CREATE INDEX idxpart_abs_b_idx ON ONLY public.idxpart USING btree (abs(b))
+ idxpart_b_1_idx | CREATE INDEX idxpart_b_1_idx ON ONLY public.idxpart USING btree (((b + 1)))
(6 rows)
drop table idxpart;
pg_inherits inh on (idx.indexrelid = inh.inhrelid)
where indexrelid::regclass::text like 'parted_isvalid%'
order by indexrelid::regclass::text collate "C";
- indexrelid | indisvalid | indrelid | inhparent
---------------------------------+------------+-----------------------+-------------------------------
- parted_isvalid_idx | f | parted_isvalid_tab |
- parted_isvalid_idx_11 | f | parted_isvalid_tab_11 | parted_isvalid_tab_1_expr_idx
- parted_isvalid_tab_12_expr_idx | t | parted_isvalid_tab_12 | parted_isvalid_tab_1_expr_idx
- parted_isvalid_tab_1_expr_idx | f | parted_isvalid_tab_1 | parted_isvalid_idx
- parted_isvalid_tab_2_expr_idx | t | parted_isvalid_tab_2 | parted_isvalid_idx
+ indexrelid | indisvalid | indrelid | inhparent
+-------------------------------+------------+-----------------------+------------------------------
+ parted_isvalid_idx | f | parted_isvalid_tab |
+ parted_isvalid_idx_11 | f | parted_isvalid_tab_11 | parted_isvalid_tab_1_a_b_idx
+ parted_isvalid_tab_12_a_b_idx | t | parted_isvalid_tab_12 | parted_isvalid_tab_1_a_b_idx
+ parted_isvalid_tab_1_a_b_idx | f | parted_isvalid_tab_1 | parted_isvalid_idx
+ parted_isvalid_tab_2_a_b_idx | t | parted_isvalid_tab_2 | parted_isvalid_idx
(5 rows)
drop table parted_isvalid_tab;
create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c);
-- MergeAppend must be used when a default partition exists
explain (costs off) select * from mcrparted order by a, abs(b), c;
- QUERY PLAN
--------------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------------
Merge Append
Sort Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c
- -> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0 mcrparted_1
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
- -> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
- -> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4 mcrparted_5
- -> Index Scan using mcrparted5_a_abs_c_idx on mcrparted5 mcrparted_6
- -> Index Scan using mcrparted_def_a_abs_c_idx on mcrparted_def mcrparted_7
+ -> Index Scan using mcrparted0_a_abs_b_c_idx on mcrparted0 mcrparted_1
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan using mcrparted4_a_abs_b_c_idx on mcrparted4 mcrparted_5
+ -> Index Scan using mcrparted5_a_abs_b_c_idx on mcrparted5 mcrparted_6
+ -> Index Scan using mcrparted_def_a_abs_b_c_idx on mcrparted_def mcrparted_7
(9 rows)
drop table mcrparted_def;
-- Append is used for a RANGE partitioned table with no default
-- and no subpartitions
explain (costs off) select * from mcrparted order by a, abs(b), c;
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Append
- -> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0 mcrparted_1
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
- -> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
- -> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4 mcrparted_5
- -> Index Scan using mcrparted5_a_abs_c_idx on mcrparted5 mcrparted_6
+ -> Index Scan using mcrparted0_a_abs_b_c_idx on mcrparted0 mcrparted_1
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan using mcrparted4_a_abs_b_c_idx on mcrparted4 mcrparted_5
+ -> Index Scan using mcrparted5_a_abs_b_c_idx on mcrparted5 mcrparted_6
(7 rows)
-- Append is used with subpaths in reverse order with backwards index scans
explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc;
- QUERY PLAN
-----------------------------------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------------------------------
Append
- -> Index Scan Backward using mcrparted5_a_abs_c_idx on mcrparted5 mcrparted_6
- -> Index Scan Backward using mcrparted4_a_abs_c_idx on mcrparted4 mcrparted_5
- -> Index Scan Backward using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
- -> Index Scan Backward using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
- -> Index Scan Backward using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
- -> Index Scan Backward using mcrparted0_a_abs_c_idx on mcrparted0 mcrparted_1
+ -> Index Scan Backward using mcrparted5_a_abs_b_c_idx on mcrparted5 mcrparted_6
+ -> Index Scan Backward using mcrparted4_a_abs_b_c_idx on mcrparted4 mcrparted_5
+ -> Index Scan Backward using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan Backward using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan Backward using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan Backward using mcrparted0_a_abs_b_c_idx on mcrparted0 mcrparted_1
(7 rows)
-- check that Append plan is used containing a MergeAppend for sub-partitions
create table mcrparted5a partition of mcrparted5 for values in(20);
create table mcrparted5_def partition of mcrparted5 default;
explain (costs off) select * from mcrparted order by a, abs(b), c;
- QUERY PLAN
----------------------------------------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------------------------------------
Append
- -> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0 mcrparted_1
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
- -> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
- -> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4 mcrparted_5
+ -> Index Scan using mcrparted0_a_abs_b_c_idx on mcrparted0 mcrparted_1
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan using mcrparted4_a_abs_b_c_idx on mcrparted4 mcrparted_5
-> Merge Append
Sort Key: mcrparted_7.a, (abs(mcrparted_7.b)), mcrparted_7.c
- -> Index Scan using mcrparted5a_a_abs_c_idx on mcrparted5a mcrparted_7
- -> Index Scan using mcrparted5_def_a_abs_c_idx on mcrparted5_def mcrparted_8
+ -> Index Scan using mcrparted5a_a_abs_b_c_idx on mcrparted5a mcrparted_7
+ -> Index Scan using mcrparted5_def_a_abs_b_c_idx on mcrparted5_def mcrparted_8
(10 rows)
drop table mcrparted5_def;
-- into the main Append when the sub-partition is unordered but contains
-- just a single sub-partition.
explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c;
- QUERY PLAN
----------------------------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------------------------
Append
- -> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0 mcrparted_1
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
- -> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
- -> Index Scan using mcrparted4_a_abs_c_idx on mcrparted4 mcrparted_5
- -> Index Scan using mcrparted5a_a_abs_c_idx on mcrparted5a mcrparted_6
+ -> Index Scan using mcrparted0_a_abs_b_c_idx on mcrparted0 mcrparted_1
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan using mcrparted4_a_abs_b_c_idx on mcrparted4 mcrparted_5
+ -> Index Scan using mcrparted5a_a_abs_b_c_idx on mcrparted5a mcrparted_6
(7 rows)
-- check that Append is used when the sub-partitioned tables are pruned
-- during planning.
explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c;
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Append
- -> Index Scan using mcrparted0_a_abs_c_idx on mcrparted0 mcrparted_1
+ -> Index Scan using mcrparted0_a_abs_b_c_idx on mcrparted0 mcrparted_1
Index Cond: (a < 20)
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
Index Cond: (a < 20)
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
Index Cond: (a < 20)
- -> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
Index Cond: (a < 20)
(9 rows)
create index on mcrparted3 (a, abs(b), c);
create index on mcrparted4 (a, abs(b), c);
explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1;
- QUERY PLAN
--------------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------------
Limit
-> Append
-> Sort
Sort Key: mcrparted_1.a, (abs(mcrparted_1.b)), mcrparted_1.c
-> Seq Scan on mcrparted0 mcrparted_1
Filter: (a < 20)
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_2
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_2
Index Cond: (a < 20)
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_3
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_3
Index Cond: (a < 20)
- -> Index Scan using mcrparted3_a_abs_c_idx on mcrparted3 mcrparted_4
+ -> Index Scan using mcrparted3_a_abs_b_c_idx on mcrparted3 mcrparted_4
Index Cond: (a < 20)
(12 rows)
-- Ensure Append node can be used when the partition is ordered by some
-- pathkeys which were deemed redundant.
explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c;
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Append
- -> Index Scan using mcrparted1_a_abs_c_idx on mcrparted1 mcrparted_1
+ -> Index Scan using mcrparted1_a_abs_b_c_idx on mcrparted1 mcrparted_1
Index Cond: (a = 10)
- -> Index Scan using mcrparted2_a_abs_c_idx on mcrparted2 mcrparted_2
+ -> Index Scan using mcrparted2_a_abs_b_c_idx on mcrparted2 mcrparted_2
Index Cond: (a = 10)
(5 rows)
create index on test_range_elem using spgist(int4range(i,i+10));
explain (costs off)
select count(*) from test_range_elem where int4range(i,i+10) <@ int4range(10,30);
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------------------------------------------
Aggregate
- -> Index Scan using test_range_elem_int4range_idx on test_range_elem
+ -> Index Scan using test_range_elem_int4range_i_i_10_idx on test_range_elem
Index Cond: (int4range(i, (i + 10)) <@ '[10,30)'::int4range)
(3 rows)
WHERE s.schemaname = 'stats_import'
AND s.tablename IN ('test', 'is_odd')
ORDER BY s.tablename, s.attname, s.inherited;
- schemaname | tablename | attname | inherited | r
---------------+-----------+---------+-----------+---
- stats_import | is_odd | expr | f | t
- stats_import | test | arange | f | t
- stats_import | test | comp | f | t
- stats_import | test | id | f | t
- stats_import | test | name | f | t
- stats_import | test | tags | f | t
+ schemaname | tablename | attname | inherited | r
+--------------+-----------+----------+-----------+---
+ stats_import | is_odd | comp_2_1 | f | t
+ stats_import | test | arange | f | t
+ stats_import | test | comp | f | t
+ stats_import | test | id | f | t
+ stats_import | test | name | f | t
+ stats_import | test | tags | f | t
(6 rows)
SELECT c.relname, COUNT(*) AS num_stats
DROP INDEX CONCURRENTLY "concur_index4";
DROP INDEX CONCURRENTLY "concur_index5";
DROP INDEX CONCURRENTLY "concur_index1";
-DROP INDEX CONCURRENTLY "concur_heap_expr_idx";
+DROP INDEX CONCURRENTLY "concur_heap_f2_f1_idx";
\d concur_heap
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
-- attach the indexes; parents stay invalid
-alter index idxpart1_expr_idx attach partition idxpart11_expr_idx;
-alter index idxpart_expr_idx attach partition idxpart1_expr_idx;
-alter index idxpart_expr_idx attach partition idxpart2_expr_idx;
+alter index idxpart1_a_b_idx attach partition idxpart11_a_b_idx;
+alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
+alter index idxpart_a_b_idx attach partition idxpart2_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
-- fix the index on the leaf partition
delete from idxpart11 where b = 0;
-reindex index concurrently idxpart11_expr_idx;
+reindex index concurrently idxpart11_a_b_idx;
-- reattach the leaf partition index; parents should now be valid
-alter index idxpart1_expr_idx attach partition idxpart11_expr_idx;
+alter index idxpart1_a_b_idx attach partition idxpart11_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
drop table idxpart;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
-- attach both; parent stays invalid
-alter index idxpart_expr_idx attach partition idxpart1_expr_idx;
-alter index idxpart_expr_idx attach partition idxpart2_expr_idx;
+alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
+alter index idxpart_a_b_idx attach partition idxpart2_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
-- fix only idxpart1's index, leave idxpart2's still invalid
delete from idxpart1 where b = 0;
-reindex index concurrently idxpart1_expr_idx;
+reindex index concurrently idxpart1_a_b_idx;
-- re-attach the fixed child; parent should stay invalid
-alter index idxpart_expr_idx attach partition idxpart1_expr_idx;
+alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
select relname, indisvalid from pg_class join pg_index on indexrelid = oid
where relname like 'idxpart%' order by relname;
drop table idxpart;
CHAR
CHECKPOINT
CHKVAL
+CIEN_context
CIRCLE
CMPDAffix
CONTEXT