mirror of
https://github.com/postgres/postgres.git
synced 2025-11-15 03:41:20 +03:00
Replace functional-index facility with expressional indexes. Any column
of an index can now be a computed expression instead of a simple variable. Restrictions on expressions are the same as for predicates (only immutable functions, no sub-selects). This fixes problems recently introduced with inlining SQL functions, because the inlining transformation is applied to both expression trees so the planner can still match them up. Along the way, improve efficiency of handling index predicates (both predicates and index expressions are now cached by the relcache) and fix 7.3 oversight that didn't record dependencies of predicate expressions.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.140 2003/05/20 20:35:10 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.141 2003/05/28 16:03:59 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -535,12 +535,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
||||
Form_pg_index idxrec;
|
||||
Form_pg_class idxrelrec;
|
||||
Form_pg_am amrec;
|
||||
List *indexprs;
|
||||
List *context;
|
||||
Oid indrelid;
|
||||
int len;
|
||||
int keyno;
|
||||
Oid keycoltypes[INDEX_MAX_KEYS];
|
||||
Oid keycoltype;
|
||||
StringInfoData buf;
|
||||
StringInfoData keybuf;
|
||||
char *str;
|
||||
char *sep;
|
||||
|
||||
/*
|
||||
@@ -576,6 +578,30 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam);
|
||||
amrec = (Form_pg_am) GETSTRUCT(ht_am);
|
||||
|
||||
/*
|
||||
* Get the index expressions, if any. (NOTE: we do not use the relcache
|
||||
* versions of the expressions and predicate, because we want to display
|
||||
* non-const-folded expressions.)
|
||||
*/
|
||||
if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
|
||||
{
|
||||
Datum exprsDatum;
|
||||
bool isnull;
|
||||
char *exprsString;
|
||||
|
||||
exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
|
||||
Anum_pg_index_indexprs, &isnull);
|
||||
Assert(!isnull);
|
||||
exprsString = DatumGetCString(DirectFunctionCall1(textout,
|
||||
exprsDatum));
|
||||
indexprs = (List *) stringToNode(exprsString);
|
||||
pfree(exprsString);
|
||||
}
|
||||
else
|
||||
indexprs = NIL;
|
||||
|
||||
context = deparse_context_for(get_rel_name(indrelid), indrelid);
|
||||
|
||||
/*
|
||||
* Start the index definition. Note that the index's name should
|
||||
* never be schema-qualified, but the indexed rel's name may be.
|
||||
@@ -588,76 +614,72 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
||||
quote_identifier(NameStr(amrec->amname)));
|
||||
|
||||
/*
|
||||
* Collect the indexed attributes in keybuf
|
||||
* Report the indexed attributes
|
||||
*/
|
||||
initStringInfo(&keybuf);
|
||||
sep = "";
|
||||
for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
|
||||
for (keyno = 0; keyno < idxrec->indnatts; keyno++)
|
||||
{
|
||||
AttrNumber attnum = idxrec->indkey[keyno];
|
||||
char *attname;
|
||||
|
||||
if (attnum == InvalidAttrNumber)
|
||||
break;
|
||||
|
||||
attname = get_relid_attribute_name(indrelid, attnum);
|
||||
keycoltypes[keyno] = get_atttype(indrelid, attnum);
|
||||
|
||||
appendStringInfo(&keybuf, sep);
|
||||
appendStringInfo(&buf, sep);
|
||||
sep = ", ";
|
||||
|
||||
/*
|
||||
* Add the indexed field name
|
||||
*/
|
||||
appendStringInfo(&keybuf, "%s", quote_identifier(attname));
|
||||
if (attnum != 0)
|
||||
{
|
||||
/* Simple index column */
|
||||
char *attname;
|
||||
|
||||
attname = get_relid_attribute_name(indrelid, attnum);
|
||||
appendStringInfo(&buf, "%s", quote_identifier(attname));
|
||||
keycoltype = get_atttype(indrelid, attnum);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* expressional index */
|
||||
Node *indexkey;
|
||||
|
||||
if (indexprs == NIL)
|
||||
elog(ERROR, "too few entries in indexprs list");
|
||||
indexkey = (Node *) lfirst(indexprs);
|
||||
indexprs = lnext(indexprs);
|
||||
/* Deparse */
|
||||
str = deparse_expression(indexkey, context, false, false);
|
||||
/* Need parens if it's not a bare function call */
|
||||
if (indexkey && IsA(indexkey, FuncExpr) &&
|
||||
((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL)
|
||||
appendStringInfo(&buf, "%s", str);
|
||||
else
|
||||
appendStringInfo(&buf, "(%s)", str);
|
||||
keycoltype = exprType(indexkey);
|
||||
}
|
||||
|
||||
/*
|
||||
* If not a functional index, add the operator class name
|
||||
* Add the operator class name
|
||||
*/
|
||||
if (idxrec->indproc == InvalidOid)
|
||||
get_opclass_name(idxrec->indclass[keyno],
|
||||
keycoltypes[keyno],
|
||||
&keybuf);
|
||||
}
|
||||
|
||||
if (idxrec->indproc != InvalidOid)
|
||||
{
|
||||
/*
|
||||
* For functional index say 'func (attrs) opclass'
|
||||
*/
|
||||
appendStringInfo(&buf, "%s(%s)",
|
||||
generate_function_name(idxrec->indproc,
|
||||
keyno, keycoltypes),
|
||||
keybuf.data);
|
||||
get_opclass_name(idxrec->indclass[0],
|
||||
get_func_rettype(idxrec->indproc),
|
||||
get_opclass_name(idxrec->indclass[keyno], keycoltype,
|
||||
&buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Otherwise say 'attr opclass [, ...]'
|
||||
*/
|
||||
appendStringInfo(&buf, "%s", keybuf.data);
|
||||
}
|
||||
|
||||
appendStringInfoChar(&buf, ')');
|
||||
|
||||
/*
|
||||
* If it's a partial index, decompile and append the predicate
|
||||
*/
|
||||
if (VARSIZE(&idxrec->indpred) > VARHDRSZ)
|
||||
if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
|
||||
{
|
||||
Node *node;
|
||||
List *context;
|
||||
char *exprstr;
|
||||
char *str;
|
||||
Datum predDatum;
|
||||
bool isnull;
|
||||
char *predString;
|
||||
|
||||
/* Convert TEXT object to C string */
|
||||
exprstr = DatumGetCString(DirectFunctionCall1(textout,
|
||||
PointerGetDatum(&idxrec->indpred)));
|
||||
/* Convert expression to node tree */
|
||||
node = (Node *) stringToNode(exprstr);
|
||||
/* Convert text string to node tree */
|
||||
predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
|
||||
Anum_pg_index_indpred, &isnull);
|
||||
Assert(!isnull);
|
||||
predString = DatumGetCString(DirectFunctionCall1(textout,
|
||||
predDatum));
|
||||
node = (Node *) stringToNode(predString);
|
||||
pfree(predString);
|
||||
|
||||
/*
|
||||
* If top level is a List, assume it is an implicit-AND structure,
|
||||
@@ -667,7 +689,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
||||
if (node && IsA(node, List))
|
||||
node = (Node *) make_ands_explicit((List *) node);
|
||||
/* Deparse */
|
||||
context = deparse_context_for(get_rel_name(indrelid), indrelid);
|
||||
str = deparse_expression(node, context, false, false);
|
||||
appendStringInfo(&buf, " WHERE %s", str);
|
||||
}
|
||||
@@ -681,7 +702,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
||||
memcpy(VARDATA(indexdef), buf.data, buf.len);
|
||||
|
||||
pfree(buf.data);
|
||||
pfree(keybuf.data);
|
||||
|
||||
ReleaseSysCache(ht_idx);
|
||||
ReleaseSysCache(ht_idxrel);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.138 2003/05/26 00:11:27 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.139 2003/05/28 16:03:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -3905,14 +3905,13 @@ btcostestimate(PG_FUNCTION_ARGS)
|
||||
indexSelectivity, indexCorrelation);
|
||||
|
||||
/*
|
||||
* If it's a functional index, leave the default zero-correlation
|
||||
* estimate in place. If not, and if we can get an estimate for the
|
||||
* first variable's ordering correlation C from pg_statistic, estimate
|
||||
* If the first column is a simple variable, and we can get an estimate
|
||||
* for its ordering correlation C from pg_statistic, estimate
|
||||
* the index correlation as C / number-of-columns. (The idea here is
|
||||
* that multiple columns dilute the importance of the first column's
|
||||
* ordering, but don't negate it entirely.)
|
||||
*/
|
||||
if (index->indproc == InvalidOid)
|
||||
if (index->indexkeys[0] != 0)
|
||||
{
|
||||
Oid relid;
|
||||
HeapTuple tuple;
|
||||
@@ -3942,8 +3941,7 @@ btcostestimate(PG_FUNCTION_ARGS)
|
||||
|
||||
Assert(nnumbers == 1);
|
||||
varCorrelation = numbers[0];
|
||||
for (nKeys = 1; index->indexkeys[nKeys] != 0; nKeys++)
|
||||
/* skip */ ;
|
||||
nKeys = index->ncolumns;
|
||||
|
||||
*indexCorrelation = varCorrelation / nKeys;
|
||||
|
||||
|
||||
209
src/backend/utils/cache/relcache.c
vendored
209
src/backend/utils/cache/relcache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.184 2003/02/09 06:56:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.185 2003/05/28 16:03:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -52,6 +52,8 @@
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/catcache.h"
|
||||
@@ -939,10 +941,9 @@ void
|
||||
RelationInitIndexAccessInfo(Relation relation)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Size iformsize;
|
||||
Form_pg_index iform;
|
||||
Form_pg_am aform;
|
||||
MemoryContext indexcxt;
|
||||
MemoryContext oldcontext;
|
||||
IndexStrategy strategy;
|
||||
Oid *operator;
|
||||
RegProcedure *support;
|
||||
@@ -952,8 +953,9 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
uint16 amsupport;
|
||||
|
||||
/*
|
||||
* Make a copy of the pg_index entry for the index. Note that this is
|
||||
* a variable-length tuple.
|
||||
* Make a copy of the pg_index entry for the index. Since pg_index
|
||||
* contains variable-length and possibly-null fields, we have to do
|
||||
* this honestly rather than just treating it as a Form_pg_index struct.
|
||||
*/
|
||||
tuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(RelationGetRelid(relation)),
|
||||
@@ -961,11 +963,11 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "RelationInitIndexAccessInfo: no pg_index entry for index %u",
|
||||
RelationGetRelid(relation));
|
||||
iformsize = tuple->t_len - tuple->t_data->t_hoff;
|
||||
iform = (Form_pg_index) MemoryContextAlloc(CacheMemoryContext, iformsize);
|
||||
memcpy(iform, GETSTRUCT(tuple), iformsize);
|
||||
oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
|
||||
relation->rd_indextuple = heap_copytuple(tuple);
|
||||
relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
ReleaseSysCache(tuple);
|
||||
relation->rd_index = iform;
|
||||
|
||||
/*
|
||||
* Make a copy of the pg_am entry for the index's access method
|
||||
@@ -982,6 +984,9 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
relation->rd_am = aform;
|
||||
|
||||
natts = relation->rd_rel->relnatts;
|
||||
if (natts != relation->rd_index->indnatts)
|
||||
elog(ERROR, "RelationInitIndexAccessInfo: relnatts disagrees with indnatts for index %u",
|
||||
RelationGetRelid(relation));
|
||||
amstrategies = aform->amstrategies;
|
||||
amsupport = aform->amsupport;
|
||||
|
||||
@@ -1047,9 +1052,15 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
* Fill the strategy map and the support RegProcedure arrays.
|
||||
* (supportinfo is left as zeroes, and is filled on-the-fly when used)
|
||||
*/
|
||||
IndexSupportInitialize(iform,
|
||||
IndexSupportInitialize(relation->rd_index,
|
||||
strategy, operator, support,
|
||||
amstrategies, amsupport, natts);
|
||||
|
||||
/*
|
||||
* expressions and predicate cache will be filled later
|
||||
*/
|
||||
relation->rd_indexprs = NIL;
|
||||
relation->rd_indpred = NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1087,8 +1098,7 @@ IndexSupportInitialize(Form_pg_index iform,
|
||||
{
|
||||
OpClassCacheEnt *opcentry;
|
||||
|
||||
if (iform->indkey[attIndex] == InvalidAttrNumber ||
|
||||
!OidIsValid(iform->indclass[attIndex]))
|
||||
if (!OidIsValid(iform->indclass[attIndex]))
|
||||
elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
|
||||
|
||||
/* look up the info for this opclass, using a cache */
|
||||
@@ -1729,8 +1739,8 @@ RelationClearRelation(Relation relation, bool rebuild)
|
||||
* with, we can only get rid of these fields:
|
||||
*/
|
||||
FreeTriggerDesc(relation->trigdesc);
|
||||
if (relation->rd_index)
|
||||
pfree(relation->rd_index);
|
||||
if (relation->rd_indextuple)
|
||||
pfree(relation->rd_indextuple);
|
||||
if (relation->rd_am)
|
||||
pfree(relation->rd_am);
|
||||
if (relation->rd_rel)
|
||||
@@ -2659,6 +2669,136 @@ insert_ordered_oid(List *list, Oid datum)
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* RelationGetIndexExpressions -- get the index expressions for an index
|
||||
*
|
||||
* We cache the result of transforming pg_index.indexprs into a node tree.
|
||||
* If the rel is not an index or has no expressional columns, we return NIL.
|
||||
* Otherwise, the returned tree is copied into the caller's memory context.
|
||||
* (We don't want to return a pointer to the relcache copy, since it could
|
||||
* disappear due to relcache invalidation.)
|
||||
*/
|
||||
List *
|
||||
RelationGetIndexExpressions(Relation relation)
|
||||
{
|
||||
List *result;
|
||||
Datum exprsDatum;
|
||||
bool isnull;
|
||||
char *exprsString;
|
||||
MemoryContext oldcxt;
|
||||
|
||||
/* Quick exit if we already computed the result. */
|
||||
if (relation->rd_indexprs)
|
||||
return (List *) copyObject(relation->rd_indexprs);
|
||||
|
||||
/* Quick exit if there is nothing to do. */
|
||||
if (relation->rd_indextuple == NULL ||
|
||||
heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs))
|
||||
return NIL;
|
||||
|
||||
/*
|
||||
* We build the tree we intend to return in the caller's context.
|
||||
* After successfully completing the work, we copy it into the relcache
|
||||
* entry. This avoids problems if we get some sort of
|
||||
* error partway through.
|
||||
*
|
||||
* We make use of the syscache's copy of pg_index's tupledesc
|
||||
* to access the non-fixed fields of the tuple. We assume that
|
||||
* the syscache will be initialized before any access of a
|
||||
* partial index could occur. (This would probably fail if we
|
||||
* were to allow partial indexes on system catalogs.)
|
||||
*/
|
||||
exprsDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple,
|
||||
Anum_pg_index_indexprs, &isnull);
|
||||
Assert(!isnull);
|
||||
exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum));
|
||||
result = (List *) stringToNode(exprsString);
|
||||
pfree(exprsString);
|
||||
|
||||
/*
|
||||
* Run the expressions through eval_const_expressions. This is not just
|
||||
* an optimization, but is necessary, because the planner will be
|
||||
* comparing them to const-folded qual clauses, and may fail to detect
|
||||
* valid matches without this.
|
||||
*/
|
||||
result = (List *) eval_const_expressions((Node *) result);
|
||||
|
||||
/* May as well fix opfuncids too */
|
||||
fix_opfuncids((Node *) result);
|
||||
|
||||
/* Now save a copy of the completed tree in the relcache entry. */
|
||||
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||
relation->rd_indexprs = (List *) copyObject(result);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* RelationGetIndexPredicate -- get the index predicate for an index
|
||||
*
|
||||
* We cache the result of transforming pg_index.indpred into a node tree.
|
||||
* If the rel is not an index or has no predicate, we return NIL.
|
||||
* Otherwise, the returned tree is copied into the caller's memory context.
|
||||
* (We don't want to return a pointer to the relcache copy, since it could
|
||||
* disappear due to relcache invalidation.)
|
||||
*/
|
||||
List *
|
||||
RelationGetIndexPredicate(Relation relation)
|
||||
{
|
||||
List *result;
|
||||
Datum predDatum;
|
||||
bool isnull;
|
||||
char *predString;
|
||||
MemoryContext oldcxt;
|
||||
|
||||
/* Quick exit if we already computed the result. */
|
||||
if (relation->rd_indpred)
|
||||
return (List *) copyObject(relation->rd_indpred);
|
||||
|
||||
/* Quick exit if there is nothing to do. */
|
||||
if (relation->rd_indextuple == NULL ||
|
||||
heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred))
|
||||
return NIL;
|
||||
|
||||
/*
|
||||
* We build the tree we intend to return in the caller's context.
|
||||
* After successfully completing the work, we copy it into the relcache
|
||||
* entry. This avoids problems if we get some sort of
|
||||
* error partway through.
|
||||
*
|
||||
* We make use of the syscache's copy of pg_index's tupledesc
|
||||
* to access the non-fixed fields of the tuple. We assume that
|
||||
* the syscache will be initialized before any access of a
|
||||
* partial index could occur. (This would probably fail if we
|
||||
* were to allow partial indexes on system catalogs.)
|
||||
*/
|
||||
predDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple,
|
||||
Anum_pg_index_indpred, &isnull);
|
||||
Assert(!isnull);
|
||||
predString = DatumGetCString(DirectFunctionCall1(textout, predDatum));
|
||||
result = (List *) stringToNode(predString);
|
||||
pfree(predString);
|
||||
|
||||
/*
|
||||
* Run the expression through eval_const_expressions. This is not just
|
||||
* an optimization, but is necessary, because the planner will be
|
||||
* comparing it to const-folded qual clauses, and may fail to detect
|
||||
* valid matches without this.
|
||||
*/
|
||||
result = (List *) eval_const_expressions((Node *) result);
|
||||
|
||||
/* May as well fix opfuncids too */
|
||||
fix_opfuncids((Node *) result);
|
||||
|
||||
/* Now save a copy of the completed tree in the relcache entry. */
|
||||
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||
relation->rd_indpred = (List *) copyObject(result);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* load_relcache_init_file, write_relcache_init_file
|
||||
@@ -2829,14 +2969,19 @@ load_relcache_init_file(void)
|
||||
if (rel->rd_isnailed)
|
||||
nailed_indexes++;
|
||||
|
||||
/* next, read the pg_index tuple form */
|
||||
/* next, read the pg_index tuple */
|
||||
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
|
||||
goto read_failed;
|
||||
|
||||
rel->rd_index = (Form_pg_index) palloc(len);
|
||||
if ((nread = fread(rel->rd_index, 1, len, fp)) != len)
|
||||
rel->rd_indextuple = (HeapTuple) palloc(len);
|
||||
if ((nread = fread(rel->rd_indextuple, 1, len, fp)) != len)
|
||||
goto read_failed;
|
||||
|
||||
/* Fix up internal pointers in the tuple -- see heap_copytuple */
|
||||
rel->rd_indextuple->t_datamcxt = CurrentMemoryContext;
|
||||
rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
|
||||
rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
|
||||
|
||||
/* next, read the access method tuple form */
|
||||
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
|
||||
goto read_failed;
|
||||
@@ -2904,6 +3049,7 @@ load_relcache_init_file(void)
|
||||
nailed_rels++;
|
||||
|
||||
Assert(rel->rd_index == NULL);
|
||||
Assert(rel->rd_indextuple == NULL);
|
||||
Assert(rel->rd_am == NULL);
|
||||
Assert(rel->rd_indexcxt == NULL);
|
||||
Assert(rel->rd_istrat == NULL);
|
||||
@@ -2917,11 +3063,13 @@ load_relcache_init_file(void)
|
||||
* format is complex and subject to change). They must be rebuilt
|
||||
* if needed by RelationCacheInitializePhase2. This is not
|
||||
* expected to be a big performance hit since few system catalogs
|
||||
* have such.
|
||||
* have such. Ditto for index expressions and predicates.
|
||||
*/
|
||||
rel->rd_rules = NULL;
|
||||
rel->rd_rulescxt = NULL;
|
||||
rel->trigdesc = NULL;
|
||||
rel->rd_indexprs = NIL;
|
||||
rel->rd_indpred = NIL;
|
||||
|
||||
/*
|
||||
* Reset transient-state fields in the relcache entry
|
||||
@@ -3076,26 +3224,15 @@ write_relcache_init_file(void)
|
||||
if (rel->rd_rel->relkind == RELKIND_INDEX)
|
||||
{
|
||||
Form_pg_am am = rel->rd_am;
|
||||
HeapTuple tuple;
|
||||
|
||||
/*
|
||||
* We need to write the index tuple form, but this is a bit
|
||||
* tricky since it's a variable-length struct. Rather than
|
||||
* hoping to intuit the length, fetch the pg_index tuple
|
||||
* afresh using the syscache, and write that.
|
||||
*/
|
||||
tuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(RelationGetRelid(rel)),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "write_relcache_init_file: no pg_index entry for index %u",
|
||||
RelationGetRelid(rel));
|
||||
len = tuple->t_len - tuple->t_data->t_hoff;
|
||||
/* write the pg_index tuple */
|
||||
/* we assume this was created by heap_copytuple! */
|
||||
len = HEAPTUPLESIZE + rel->rd_indextuple->t_len;
|
||||
if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
|
||||
elog(FATAL, "cannot write init file -- index tuple form length");
|
||||
if (fwrite(GETSTRUCT(tuple), 1, len, fp) != len)
|
||||
elog(FATAL, "cannot write init file -- index tuple form");
|
||||
ReleaseSysCache(tuple);
|
||||
elog(FATAL, "cannot write init file -- index tuple length");
|
||||
|
||||
if (fwrite(rel->rd_indextuple, 1, len, fp) != len)
|
||||
elog(FATAL, "cannot write init file -- index tuple");
|
||||
|
||||
/* next, write the access method tuple form */
|
||||
len = sizeof(FormData_pg_am);
|
||||
|
||||
Reference in New Issue
Block a user