Permalink
Browse files

Add a hypopg_get_indexdef(oid) function

  • Loading branch information...
rjuju committed Nov 16, 2016
1 parent b6ce86d commit d2cfd7898fa6def5e6b6275439e17f6f1cbf8b53
Showing with 213 additions and 1 deletion.
  1. +1 −1 TODO.md
  2. +7 −0 expected/hypopg.out
  3. +6 −0 hypopg--1.1.0dev.sql
  4. +151 −0 hypopg.c
  5. +44 −0 hypopg_import.c
  6. +1 −0 hypopg_import.h
  7. +3 −0 test/sql/hypopg.sql
@@ -17,7 +17,7 @@ Important
- Add some more (or enhance) function. Following are interesting:
- [X] estimated index size
- [ ] estimated number of lines
- [ ] add hypopg_get_indexdef(oid) (based on src/backend/utils/adt/ruleutils.c/pg_get_indexdef_worker())
- [X] add hypopg_get_indexdef(oid)

Less important
--------------
@@ -155,3 +155,10 @@ WHERE e ~ 'Index.*<\d+>btree_hypo.*';
1
(1 row)

-- Deparse an index DDL, with almost every possible pathcode
SELECT hypopg_get_indexdef(indexrelid) FROM hypopg_create_index('create index on hypo using btree(id desc nulls first, cast(md5(val) as bpchar) bpchar_pattern_ops) with (fillfactor = 10) WHERE id < 1000 AND id +1 %2 = 3');
hypopg_get_indexdef
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE INDEX ON public.hypo USING btree (id DESC, ((md5(hypo.val))::bpchar) bpchar_pattern_ops) WITH (fillfactor = 10) WHERE ((id < 1000) AND ((id + (1 % 2)) = 3))
(1 row)

@@ -52,3 +52,9 @@ hypopg_relation_size(IN indexid oid)
RETURNS bigint
LANGUAGE c COST 100
AS '$libdir/hypopg', 'hypopg_relation_size';

CREATE FUNCTION
hypopg_get_indexdef(IN indexid oid)
RETURNS text
LANGUAGE C STRICT VOLATILE COST 100
AS '$libdir/hypopg', 'hypopg_get_indexdef';
151 hypopg.c
@@ -59,6 +59,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/ruleutils.h"
#include "utils/syscache.h"

#include "hypopg_import.h"
@@ -162,12 +163,14 @@ Datum hypopg(PG_FUNCTION_ARGS);
Datum hypopg_create_index(PG_FUNCTION_ARGS);
Datum hypopg_drop_index(PG_FUNCTION_ARGS);
Datum hypopg_relation_size(PG_FUNCTION_ARGS);
Datum hypopg_get_indexdef(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(hypopg_reset);
PG_FUNCTION_INFO_V1(hypopg);
PG_FUNCTION_INFO_V1(hypopg_create_index);
PG_FUNCTION_INFO_V1(hypopg_drop_index);
PG_FUNCTION_INFO_V1(hypopg_relation_size);
PG_FUNCTION_INFO_V1(hypopg_get_indexdef);

static hypoEntry *hypo_newEntry(Oid relid, char *accessMethod, int ncolumns,
List *options);
@@ -1507,6 +1510,154 @@ hypopg_relation_size(PG_FUNCTION_ARGS)
PG_RETURN_INT64(pages * BLCKSZ);
}

/*
* Deparse an hypoEntry, indentified by its indexid to the actual CREATE INDEX
* command.
*
* Heavilty inspired on pg_get_indexdef_worker()
*/

Datum
hypopg_get_indexdef(PG_FUNCTION_ARGS)
{
Oid indexid = PG_GETARG_OID(0);
ListCell *indexpr_item;
StringInfoData buf;
hypoEntry *entry;
ListCell *lc;
List *context;
int keyno, cpt;

foreach(lc, entries)
{
entry = (hypoEntry *) lfirst(lc);

if (entry->oid == indexid)
break;
}

if (!entry || entry->oid != indexid)
PG_RETURN_NULL();

initStringInfo(&buf);
appendStringInfo(&buf, "CREATE %s ON %s.%s USING %s (",
(entry->unique ? "UNIQUE INDEX" : "INDEX"),
quote_identifier(get_namespace_name(get_rel_namespace(entry->relid))),
quote_identifier(get_rel_name(entry->relid)),
get_am_name(entry->relam));

indexpr_item = list_head(entry->indexprs);

context = deparse_context_for(get_rel_name(entry->relid), entry->relid);

for (keyno=0; keyno<entry->ncolumns; keyno++)
{
Oid indcoll;
Oid keycoltype;
Oid keycolcollation;
char *str;

if (keyno != 0)
appendStringInfo(&buf, ", ");

if (entry->indexkeys[keyno] != 0)
{
int32 keycoltypmod;
appendStringInfo(&buf, "%s", get_attname(entry->relid,
entry->indexkeys[keyno]));

get_atttypetypmodcoll(entry->relid, entry->indexkeys[keyno],
&keycoltype, &keycoltypmod,
&keycolcollation);
}
else
{
/* expressional index */
Node *indexkey;

if (indexpr_item == NULL)
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexpr_item);
indexpr_item = lnext(indexpr_item);

/* 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)
appendStringInfoString(&buf, str);
else
appendStringInfo(&buf, "(%s)", str);

keycoltype = exprType(indexkey);
keycolcollation = exprCollation(indexkey);

cpt++;
}

/* Add collation, if not default for column */
indcoll = entry->indexcollations[keyno];
if (OidIsValid(indcoll) && indcoll != keycolcollation)
appendStringInfo(&buf, " COLLATE %s",
generate_collation_name((indcoll)));

/* Add the operator class name, if not default */
get_opclass_name(entry->opclass[keyno], entry->opcintype[keyno], &buf);

/* Add options if relevant */
if (entry->amcanorder)
{
/* if it supports sort ordering, report DESC and NULLS opts */
if (entry->reverse_sort[keyno])
{
appendStringInfoString(&buf, " DESC");
/* NULLS FIRST is the default in this case */
if (!(entry->nulls_first[keyno]))
appendStringInfoString(&buf, " NULLS LAST");
}
else
{
if (entry->nulls_first[keyno])
appendStringInfoString(&buf, " NULLS FIRST");
}
}
}

appendStringInfo(&buf, ")");

if (entry->options)
{
appendStringInfo(&buf, " WITH (");

foreach(lc, entry->options)
{
DefElem *elem = (DefElem *) lfirst(lc);

appendStringInfo(&buf, "%s = ", elem->defname);

if (strcmp(elem->defname, "fillfactor") == 0)
appendStringInfo(&buf, "%d", (int32) intVal(elem->arg));
else if (strcmp(elem->defname, "pages_per_range") == 0)
appendStringInfo(&buf, "%d", (int32) intVal(elem->arg));
else if (strcmp(elem->defname, "length") == 0)
appendStringInfo(&buf, "%d", (int32) intVal(elem->arg));
else
elog(WARNING," hypopg: option %s unhandled, please report the bug",
elem->defname);
}
appendStringInfo(&buf, ")");
}

if (entry->indpred)
{
appendStringInfo(&buf, " WHERE %s", deparse_expression((Node *)
make_ands_explicit(entry->indpred), context, false, false));
}

PG_RETURN_TEXT_P(cstring_to_text(buf.data));
}


/* Simple function to set the indexname, dealing with max name length, and the
* ending \0
@@ -23,6 +23,7 @@
#include "optimizer/planner.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"

@@ -263,3 +264,46 @@ CheckMutability(Expr *expr)
/* Now we can search for non-immutable functions */
return contain_mutable_functions((Node *) expr);
}

/*
* Copied from /git/postgresql/src/backend/utils/adt/ruleutils.c, not exported.
*
* get_opclass_name - fetch name of an index operator class
*
* The opclass name is appended (after a space) to buf.
*
* Output is suppressed if the opclass is the default for the given
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
void
get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf)
{
HeapTuple ht_opc;
Form_pg_opclass opcrec;
char *opcname;
char *nspname;

ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
if (!HeapTupleIsValid(ht_opc))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);

if (!OidIsValid(actual_datatype) ||
GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
{
/* Okay, we need the opclass name. Do we need to qualify it? */
opcname = NameStr(opcrec->opcname);
if (OpclassIsVisible(opclass))
appendStringInfo(buf, " %s", quote_identifier(opcname));
else
{
nspname = get_namespace_name(opcrec->opcnamespace);
appendStringInfo(buf, " %s.%s",
quote_identifier(nspname),
quote_identifier(opcname));
}
}
ReleaseSysCache(ht_opc);
}
@@ -22,3 +22,4 @@ extern Oid GetIndexOpClass(List *opclass, Oid attrType,

extern void CheckPredicate(Expr *predicate);
extern bool CheckMutability(Expr *expr);
extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
@@ -97,3 +97,6 @@ FROM public.hypopg_create_index('CREATE INDEX ON hypo (md5(val))');
-- Should use hypothetical index
SELECT COUNT(*) FROM do_explain('SELECT * FROM hypo WHERE md5(val) = md5(''line 1'')') e
WHERE e ~ 'Index.*<\d+>btree_hypo.*';

-- Deparse an index DDL, with almost every possible pathcode
SELECT hypopg_get_indexdef(indexrelid) FROM hypopg_create_index('create index on hypo using btree(id desc nulls first, cast(md5(val) as bpchar) bpchar_pattern_ops) with (fillfactor = 10) WHERE id < 1000 AND id +1 %2 = 3');

0 comments on commit d2cfd78

Please sign in to comment.