Extend pg_get_indexdef() to know about index predicates. Also, tweak
it to suppress index opclass output for opclasses that are the default for their datatype; only non-default opclasses are shown explicitly. This is expected to improve portability of the CREATE INDEX command across future versions of Postgres --- we've changed index opclasses too often in the past to think we won't do so again.
This commit is contained in:
parent
0648d78ac4
commit
e5bbf19659
@ -3,7 +3,7 @@
|
|||||||
* back to source text
|
* back to source text
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.82 2001/08/12 21:35:19 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.83 2001/10/01 20:15:26 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -41,7 +41,9 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
|
#include "catalog/index.h"
|
||||||
#include "catalog/pg_index.h"
|
#include "catalog/pg_index.h"
|
||||||
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_shadow.h"
|
#include "catalog/pg_shadow.h"
|
||||||
#include "executor/spi.h"
|
#include "executor/spi.h"
|
||||||
@ -94,10 +96,6 @@ static void *plan_getrule = NULL;
|
|||||||
static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
|
static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
|
||||||
static void *plan_getview = NULL;
|
static void *plan_getview = NULL;
|
||||||
static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1";
|
static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1";
|
||||||
static void *plan_getam = NULL;
|
|
||||||
static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
|
|
||||||
static void *plan_getopclass = NULL;
|
|
||||||
static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -138,6 +136,8 @@ static void get_sublink_expr(Node *node, deparse_context *context);
|
|||||||
static void get_from_clause(Query *query, deparse_context *context);
|
static void get_from_clause(Query *query, deparse_context *context);
|
||||||
static void get_from_clause_item(Node *jtnode, Query *query,
|
static void get_from_clause_item(Node *jtnode, Query *query,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
|
static void get_opclass_name(Oid opclass, bool only_nondefault,
|
||||||
|
StringInfo buf);
|
||||||
static bool tleIsArrayAssign(TargetEntry *tle);
|
static bool tleIsArrayAssign(TargetEntry *tle);
|
||||||
static char *quote_identifier(char *ident);
|
static char *quote_identifier(char *ident);
|
||||||
static char *get_relation_name(Oid relid);
|
static char *get_relation_name(Oid relid);
|
||||||
@ -341,48 +341,16 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
|||||||
HeapTuple ht_idx;
|
HeapTuple ht_idx;
|
||||||
HeapTuple ht_idxrel;
|
HeapTuple ht_idxrel;
|
||||||
HeapTuple ht_indrel;
|
HeapTuple ht_indrel;
|
||||||
HeapTuple spi_tup;
|
|
||||||
TupleDesc spi_ttc;
|
|
||||||
int spi_fno;
|
|
||||||
Form_pg_index idxrec;
|
Form_pg_index idxrec;
|
||||||
Form_pg_class idxrelrec;
|
Form_pg_class idxrelrec;
|
||||||
Form_pg_class indrelrec;
|
Form_pg_class indrelrec;
|
||||||
Datum spi_args[1];
|
Form_pg_am amrec;
|
||||||
char spi_nulls[2];
|
|
||||||
int spirc;
|
|
||||||
int len;
|
int len;
|
||||||
int keyno;
|
int keyno;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
StringInfoData keybuf;
|
StringInfoData keybuf;
|
||||||
char *sep;
|
char *sep;
|
||||||
|
|
||||||
/*
|
|
||||||
* Connect to SPI manager
|
|
||||||
*/
|
|
||||||
if (SPI_connect() != SPI_OK_CONNECT)
|
|
||||||
elog(ERROR, "get_indexdef: cannot connect to SPI manager");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On the first call prepare the plans to lookup pg_am and pg_opclass.
|
|
||||||
*/
|
|
||||||
if (plan_getam == NULL)
|
|
||||||
{
|
|
||||||
Oid argtypes[1];
|
|
||||||
void *plan;
|
|
||||||
|
|
||||||
argtypes[0] = OIDOID;
|
|
||||||
plan = SPI_prepare(query_getam, 1, argtypes);
|
|
||||||
if (plan == NULL)
|
|
||||||
elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
|
|
||||||
plan_getam = SPI_saveplan(plan);
|
|
||||||
|
|
||||||
argtypes[0] = OIDOID;
|
|
||||||
plan = SPI_prepare(query_getopclass, 1, argtypes);
|
|
||||||
if (plan == NULL)
|
|
||||||
elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
|
|
||||||
plan_getopclass = SPI_saveplan(plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch the pg_index tuple by the Oid of the index
|
* Fetch the pg_index tuple by the Oid of the index
|
||||||
*/
|
*/
|
||||||
@ -414,21 +382,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
|||||||
indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
|
indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the am name for the index relation
|
* Fetch the pg_am tuple of the index' access method
|
||||||
|
*
|
||||||
|
* There is no syscache for this, so use index.c subroutine.
|
||||||
*/
|
*/
|
||||||
spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
|
amrec = AccessMethodObjectIdGetForm(idxrelrec->relam,
|
||||||
spi_nulls[0] = ' ';
|
CurrentMemoryContext);
|
||||||
spi_nulls[1] = '\0';
|
if (!amrec)
|
||||||
spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
|
elog(ERROR, "lookup for AM %u failed", idxrelrec->relam);
|
||||||
if (spirc != SPI_OK_SELECT)
|
|
||||||
elog(ERROR, "failed to get pg_am tuple for index %s",
|
|
||||||
NameStr(idxrelrec->relname));
|
|
||||||
if (SPI_processed != 1)
|
|
||||||
elog(ERROR, "failed to get pg_am tuple for index %s",
|
|
||||||
NameStr(idxrelrec->relname));
|
|
||||||
spi_tup = SPI_tuptable->vals[0];
|
|
||||||
spi_ttc = SPI_tuptable->tupdesc;
|
|
||||||
spi_fno = SPI_fnumber(spi_ttc, "amname");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the index definition
|
* Start the index definition
|
||||||
@ -436,13 +397,12 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
|||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
|
appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
|
||||||
idxrec->indisunique ? "UNIQUE " : "",
|
idxrec->indisunique ? "UNIQUE " : "",
|
||||||
quote_identifier(pstrdup(NameStr(idxrelrec->relname))),
|
quote_identifier(NameStr(idxrelrec->relname)),
|
||||||
quote_identifier(pstrdup(NameStr(indrelrec->relname))),
|
quote_identifier(NameStr(indrelrec->relname)),
|
||||||
quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
|
quote_identifier(NameStr(amrec->amname)));
|
||||||
spi_fno)));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect the indexed attributes
|
* Collect the indexed attributes in keybuf
|
||||||
*/
|
*/
|
||||||
initStringInfo(&keybuf);
|
initStringInfo(&keybuf);
|
||||||
sep = "";
|
sep = "";
|
||||||
@ -465,29 +425,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
|||||||
* If not a functional index, add the operator class name
|
* If not a functional index, add the operator class name
|
||||||
*/
|
*/
|
||||||
if (idxrec->indproc == InvalidOid)
|
if (idxrec->indproc == InvalidOid)
|
||||||
{
|
get_opclass_name(idxrec->indclass[keyno], true, &keybuf);
|
||||||
spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
|
|
||||||
spi_nulls[0] = ' ';
|
|
||||||
spi_nulls[1] = '\0';
|
|
||||||
spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
|
|
||||||
if (spirc != SPI_OK_SELECT)
|
|
||||||
elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
|
|
||||||
if (SPI_processed != 1)
|
|
||||||
elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
|
|
||||||
spi_tup = SPI_tuptable->vals[0];
|
|
||||||
spi_ttc = SPI_tuptable->tupdesc;
|
|
||||||
spi_fno = SPI_fnumber(spi_ttc, "opcname");
|
|
||||||
appendStringInfo(&keybuf, " %s",
|
|
||||||
quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
|
|
||||||
spi_fno)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* For functional index say 'func (attrs) opclass'
|
|
||||||
*/
|
|
||||||
if (idxrec->indproc != InvalidOid)
|
if (idxrec->indproc != InvalidOid)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* For functional index say 'func (attrs) opclass'
|
||||||
|
*/
|
||||||
HeapTuple proctup;
|
HeapTuple proctup;
|
||||||
Form_pg_proc procStruct;
|
Form_pg_proc procStruct;
|
||||||
|
|
||||||
@ -498,58 +443,67 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
|||||||
elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
|
elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
|
||||||
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
|
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
|
||||||
|
|
||||||
appendStringInfo(&buf, "%s(%s) ",
|
appendStringInfo(&buf, "%s(%s)",
|
||||||
quote_identifier(pstrdup(NameStr(procStruct->proname))),
|
quote_identifier(NameStr(procStruct->proname)),
|
||||||
keybuf.data);
|
keybuf.data);
|
||||||
|
get_opclass_name(idxrec->indclass[0], true, &buf);
|
||||||
|
|
||||||
spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
|
|
||||||
spi_nulls[0] = ' ';
|
|
||||||
spi_nulls[1] = '\0';
|
|
||||||
spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
|
|
||||||
if (spirc != SPI_OK_SELECT)
|
|
||||||
elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
|
|
||||||
if (SPI_processed != 1)
|
|
||||||
elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
|
|
||||||
spi_tup = SPI_tuptable->vals[0];
|
|
||||||
spi_ttc = SPI_tuptable->tupdesc;
|
|
||||||
spi_fno = SPI_fnumber(spi_ttc, "opcname");
|
|
||||||
appendStringInfo(&buf, "%s",
|
|
||||||
quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
|
|
||||||
spi_fno)));
|
|
||||||
ReleaseSysCache(proctup);
|
ReleaseSysCache(proctup);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* For the others say 'attr opclass [, ...]'
|
* Otherwise say 'attr opclass [, ...]'
|
||||||
*/
|
*/
|
||||||
appendStringInfo(&buf, "%s", keybuf.data);
|
appendStringInfo(&buf, "%s", keybuf.data);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Finish
|
|
||||||
*/
|
|
||||||
appendStringInfo(&buf, ")");
|
appendStringInfo(&buf, ")");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the result in upper executor memory, and free objects
|
* If it's a partial index, decompile and append the predicate
|
||||||
|
*/
|
||||||
|
if (VARSIZE(&idxrec->indpred) > VARHDRSZ)
|
||||||
|
{
|
||||||
|
Node *node;
|
||||||
|
List *context;
|
||||||
|
char *exprstr;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
/* Convert TEXT object to C string */
|
||||||
|
exprstr = DatumGetCString(DirectFunctionCall1(textout,
|
||||||
|
PointerGetDatum(&idxrec->indpred)));
|
||||||
|
/* Convert expression to node tree */
|
||||||
|
node = (Node *) stringToNode(exprstr);
|
||||||
|
/*
|
||||||
|
* If top level is a List, assume it is an implicit-AND structure,
|
||||||
|
* and convert to explicit AND. This is needed for partial index
|
||||||
|
* predicates.
|
||||||
|
*/
|
||||||
|
if (node && IsA(node, List))
|
||||||
|
node = (Node *) make_ands_explicit((List *) node);
|
||||||
|
/* Deparse */
|
||||||
|
context = deparse_context_for(NameStr(indrelrec->relname),
|
||||||
|
idxrec->indrelid);
|
||||||
|
str = deparse_expression(node, context, false);
|
||||||
|
appendStringInfo(&buf, " WHERE %s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the result as a TEXT datum, and free working data
|
||||||
*/
|
*/
|
||||||
len = buf.len + VARHDRSZ;
|
len = buf.len + VARHDRSZ;
|
||||||
indexdef = SPI_palloc(len);
|
indexdef = (text *) palloc(len);
|
||||||
VARATT_SIZEP(indexdef) = len;
|
VARATT_SIZEP(indexdef) = len;
|
||||||
memcpy(VARDATA(indexdef), buf.data, buf.len);
|
memcpy(VARDATA(indexdef), buf.data, buf.len);
|
||||||
|
|
||||||
pfree(buf.data);
|
pfree(buf.data);
|
||||||
pfree(keybuf.data);
|
pfree(keybuf.data);
|
||||||
|
pfree(amrec);
|
||||||
ReleaseSysCache(ht_idx);
|
ReleaseSysCache(ht_idx);
|
||||||
ReleaseSysCache(ht_idxrel);
|
ReleaseSysCache(ht_idxrel);
|
||||||
ReleaseSysCache(ht_indrel);
|
ReleaseSysCache(ht_indrel);
|
||||||
|
|
||||||
/*
|
|
||||||
* Disconnect from SPI manager
|
|
||||||
*/
|
|
||||||
if (SPI_finish() != SPI_OK_FINISH)
|
|
||||||
elog(ERROR, "get_viewdef: SPI_finish() failed");
|
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(indexdef);
|
PG_RETURN_TEXT_P(indexdef);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2546,6 +2500,32 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
|||||||
dpns->namespace = sv_namespace;
|
dpns->namespace = sv_namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------
|
||||||
|
* get_opclass_name - fetch name of an index operator class
|
||||||
|
*
|
||||||
|
* The opclass name is appended (after a space) to buf.
|
||||||
|
* If "only_nondefault" is true, the opclass name is appended only if
|
||||||
|
* it isn't the default for its datatype.
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
get_opclass_name(Oid opclass, bool only_nondefault,
|
||||||
|
StringInfo buf)
|
||||||
|
{
|
||||||
|
HeapTuple ht_opc;
|
||||||
|
Form_pg_opclass opcrec;
|
||||||
|
|
||||||
|
ht_opc = SearchSysCache(CLAOID,
|
||||||
|
ObjectIdGetDatum(opclass),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(ht_opc))
|
||||||
|
elog(ERROR, "cache lookup failed for opclass %u", opclass);
|
||||||
|
opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
|
||||||
|
if (!only_nondefault || !opcrec->opcdefault)
|
||||||
|
appendStringInfo(buf, " %s",
|
||||||
|
quote_identifier(NameStr(opcrec->opcname)));
|
||||||
|
ReleaseSysCache(ht_opc);
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* tleIsArrayAssign - check for array assignment
|
* tleIsArrayAssign - check for array assignment
|
||||||
|
Loading…
x
Reference in New Issue
Block a user