From 5d887319f3abfd60c3b9ffb5f1a4e1fc178b33ee Mon Sep 17 00:00:00 2001 From: Julien Rouhaud Date: Mon, 8 Apr 2019 22:47:16 +0200 Subject: [PATCH] Split imported code in multiple files in a dedicated directory --- Makefile | 6 +- hypopg_index.c | 2 + import/hypopg_import.c | 68 +++ import/hypopg_import_analyze.c | 143 ++++++ import/hypopg_import_index.c | 297 +++++++++++ .../hypopg_import_table.c | 478 +----------------- include/hypopg_import.h | 144 +----- include/hypopg_import_analyze.h | 24 + include/hypopg_import_index.h | 33 ++ include/hypopg_import_table.h | 137 +++++ 10 files changed, 734 insertions(+), 598 deletions(-) create mode 100644 import/hypopg_import.c create mode 100644 import/hypopg_import_analyze.c create mode 100644 import/hypopg_import_index.c rename hypopg_import.c => import/hypopg_import_table.c (74%) create mode 100644 include/hypopg_import_analyze.h create mode 100644 include/hypopg_import_index.h create mode 100644 include/hypopg_import_table.h diff --git a/Makefile b/Makefile index ef2879f..95ac58e 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,11 @@ REGRESS_OPTS = --inputdir=test PG_CONFIG ?= pg_config MODULE_big = hypopg -OBJS = hypopg.o hypopg_import.o hypopg_analyze.o hypopg_index.o hypopg_table.o + +OBJS = hypopg.o \ + hypopg_analyze.o hypopg_index.o hypopg_table.o \ + import/hypopg_import.o import/hypopg_import_analyze.o \ + import/hypopg_import_index.o import/hypopg_import_table.o all: diff --git a/hypopg_index.c b/hypopg_index.c index 40348e5..b326166 100644 --- a/hypopg_index.c +++ b/hypopg_index.c @@ -101,8 +101,10 @@ static void hypo_estimate_index_simple(hypoIndex *entry, static void hypo_estimate_index(hypoIndex *entry, RelOptInfo *rel, PlannerInfo *root); static int hypo_estimate_index_colsize(hypoIndex *entry, int col); +#if PG_VERSION_NUM >= 110000 static void hypo_index_check_uniqueness_compatibility(IndexStmt *stmt, Oid relid, hypoIndex *entry); +#endif static void hypo_index_pfree(hypoIndex *entry); static bool hypo_index_remove(Oid indexid); static const hypoIndex *hypo_index_store_parsetree(IndexStmt *node, diff --git a/import/hypopg_import.c b/import/hypopg_import.c new file mode 100644 index 0000000..4b03663 --- /dev/null +++ b/import/hypopg_import.c @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + * + * hypopg_import.c: Import of some PostgreSQL private fuctions. + * + * This program is open source, licensed under the PostgreSQL license. + * For license terms, see the LICENSE file. + * + * Copyright (c) 2008-2018, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ + + +#include "postgres.h" +#if PG_VERSION_NUM >= 90300 +#include "access/htup_details.h" +#endif +#include "catalog/namespace.h" +#include "catalog/pg_opclass.h" +#include "commands/defrem.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#include "include/hypopg_import.h" + +/* + * Copied from 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); +} diff --git a/import/hypopg_import_analyze.c b/import/hypopg_import_analyze.c new file mode 100644 index 0000000..6739439 --- /dev/null +++ b/import/hypopg_import_analyze.c @@ -0,0 +1,143 @@ +/*------------------------------------------------------------------------- + * + * hypopg_import_analyze.c: Import of some PostgreSQL private fuctions, used + * for hypothetical analyze. + * + * This program is open source, licensed under the PostgreSQL license. + * For license terms, see the LICENSE file. + * + * Copyright (c) 2008-2018, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ + + +#include "postgres.h" +#if PG_VERSION_NUM >= 100000 + +#if PG_VERSION_NUM >= 90300 +#include "access/htup_details.h" +#endif +#include "nodes/nodeFuncs.h" +#include "utils/syscache.h" +#include "include/hypopg_import.h" + + +/* + * Copied from src/backend/commands/analyze.c/, not exported + * + * examine_attribute -- pre-analysis of a single column + * + * Determine whether the column is analyzable; if so, create and initialize + * a VacAttrStats struct for it. If not, return NULL. + * + * If index_expr isn't NULL, then we're trying to analyze an expression index, + * and index_expr is the expression tree representing the column's data. + */ +VacAttrStats * +examine_attribute(Relation onerel, int attnum, Node *index_expr) +{ + Form_pg_attribute attr = TupleDescAttr(onerel->rd_att, attnum - 1); + HeapTuple typtuple; + VacAttrStats *stats; + int i; + bool ok; + + /* Never analyze dropped columns */ + if (attr->attisdropped) + return NULL; + + /* Don't analyze column if user has specified not to */ + if (attr->attstattarget == 0) + return NULL; + + /* + * Create the VacAttrStats struct. Note that we only have a copy of the + * fixed fields of the pg_attribute tuple. + */ + stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats)); + stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE); + memcpy(stats->attr, attr, ATTRIBUTE_FIXED_PART_SIZE); + + /* + * When analyzing an expression index, believe the expression tree's type + * not the column datatype --- the latter might be the opckeytype storage + * type of the opclass, which is not interesting for our purposes. (Note: + * if we did anything with non-expression index columns, we'd need to + * figure out where to get the correct type info from, but for now that's + * not a problem.) It's not clear whether anyone will care about the + * typmod, but we store that too just in case. + */ + if (index_expr) + { + stats->attrtypid = exprType(index_expr); + stats->attrtypmod = exprTypmod(index_expr); + } + else + { + stats->attrtypid = attr->atttypid; + stats->attrtypmod = attr->atttypmod; + } + + typtuple = SearchSysCacheCopy1(TYPEOID, + ObjectIdGetDatum(stats->attrtypid)); + if (!HeapTupleIsValid(typtuple)) + elog(ERROR, "cache lookup failed for type %u", stats->attrtypid); + stats->attrtype = (Form_pg_type) GETSTRUCT(typtuple); + //FIXME + stats->anl_context = CurrentMemoryContext; + stats->tupattnum = attnum; + + /* + * The fields describing the stats->stavalues[n] element types default to + * the type of the data being analyzed, but the type-specific typanalyze + * function can change them if it wants to store something else. + */ + for (i = 0; i < STATISTIC_NUM_SLOTS; i++) + { + stats->statypid[i] = stats->attrtypid; + stats->statyplen[i] = stats->attrtype->typlen; + stats->statypbyval[i] = stats->attrtype->typbyval; + stats->statypalign[i] = stats->attrtype->typalign; + } + + /* + * Call the type-specific typanalyze function. If none is specified, use + * std_typanalyze(). + */ + if (OidIsValid(stats->attrtype->typanalyze)) + ok = DatumGetBool(OidFunctionCall1(stats->attrtype->typanalyze, + PointerGetDatum(stats))); + else + ok = std_typanalyze(stats); + + if (!ok || stats->compute_stats == NULL || stats->minrows <= 0) + { + heap_freetuple(typtuple); + pfree(stats->attr); + pfree(stats); + return NULL; + } + + return stats; +} + +/* + * Copied from src/backend/commands/analyze.c, not exported + * + * Standard fetch function for use by compute_stats subroutines. + * + * This exists to provide some insulation between compute_stats routines + * and the actual storage of the sample data. + */ +Datum +std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull) +{ + int attnum = stats->tupattnum; + HeapTuple tuple = stats->rows[rownum]; + TupleDesc tupDesc = stats->tupDesc; + + return heap_getattr(tuple, attnum, tupDesc, isNull); +} + +#endif /* pg10+ */ diff --git a/import/hypopg_import_index.c b/import/hypopg_import_index.c new file mode 100644 index 0000000..22d84db --- /dev/null +++ b/import/hypopg_import_index.c @@ -0,0 +1,297 @@ +/*------------------------------------------------------------------------- + * + * hypopg_import_index.c: Import of some PostgreSQL private fuctions, used for + * hypothetical index. + * + * This program is open source, licensed under the PostgreSQL license. + * For license terms, see the LICENSE file. + * + * Copyright (c) 2008-2018, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#if PG_VERSION_NUM >= 90300 +#include "access/htup_details.h" +#endif +#include "catalog/heap.h" +#include "catalog/namespace.h" +#include "catalog/pg_opclass.h" +#include "commands/defrem.h" +#include "commands/vacuum.h" +#include "nodes/makefuncs.h" +#include "nodes/pg_list.h" +#include "optimizer/clauses.h" +#include "optimizer/planner.h" +#include "optimizer/pathnode.h" +#if PG_VERSION_NUM >= 110000 +#include "partitioning/partbounds.h" +#endif +#include "parser/parse_coerce.h" +#include "utils/builtins.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "include/hypopg.h" + +/* Copied from src/backend/optimizer/util/plancat.c, not exported. + * + * Build a targetlist representing the columns of the specified index. + * Each column is represented by a Var for the corresponding base-relation + * column, or an expression in base-relation Vars, as appropriate. + * + * There are never any dropped columns in indexes, so unlike + * build_physical_tlist, we need no failure case. + */ +List * +build_index_tlist(PlannerInfo *root, IndexOptInfo *index, + Relation heapRelation) +{ + List *tlist = NIL; + Index varno = index->rel->relid; + ListCell *indexpr_item; + int i; + + indexpr_item = list_head(index->indexprs); + for (i = 0; i < index->ncolumns; i++) + { + int indexkey = index->indexkeys[i]; + Expr *indexvar; + + if (indexkey != 0) + { + /* simple column */ + const FormData_pg_attribute *att_tup; + + if (indexkey < 0) + att_tup = SystemAttributeDefinition(indexkey, + heapRelation->rd_rel->relhasoids); + else +#if PG_VERSION_NUM >= 110000 + att_tup = TupleDescAttr(heapRelation->rd_att, indexkey - 1); +#else + att_tup = heapRelation->rd_att->attrs[indexkey - 1]; +#endif + + indexvar = (Expr *) makeVar(varno, + indexkey, + att_tup->atttypid, + att_tup->atttypmod, + att_tup->attcollation, + 0); + } + else + { + /* expression column */ + if (indexpr_item == NULL) + elog(ERROR, "wrong number of index expressions"); + indexvar = (Expr *) lfirst(indexpr_item); + indexpr_item = lnext(indexpr_item); + } + + tlist = lappend(tlist, + makeTargetEntry(indexvar, + i + 1, + NULL, + false)); + } + if (indexpr_item != NULL) + elog(ERROR, "wrong number of index expressions"); + + return tlist; +} + +/* + * Copied from src/backend/commands/indexcmds.c, not exported. + * Resolve possibly-defaulted operator class specification + */ +Oid +GetIndexOpClass(List *opclass, Oid attrType, + char *accessMethodName, Oid accessMethodId) +{ + char *schemaname; + char *opcname; + HeapTuple tuple; + Oid opClassId, + opInputType; + + /* + * Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we + * ignore those opclass names so the default *_ops is used. This can be + * removed in some later release. bjm 2000/02/07 + * + * Release 7.1 removes lztext_ops, so suppress that too for a while. tgl + * 2000/07/30 + * + * Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that + * too for awhile. I'm starting to think we need a better approach. tgl + * 2000/10/01 + * + * Release 8.0 removes bigbox_ops (which was dead code for a long while + * anyway). tgl 2003/11/11 + */ + if (list_length(opclass) == 1) + { + char *claname = strVal(linitial(opclass)); + + if (strcmp(claname, "network_ops") == 0 || + strcmp(claname, "timespan_ops") == 0 || + strcmp(claname, "datetime_ops") == 0 || + strcmp(claname, "lztext_ops") == 0 || + strcmp(claname, "timestamp_ops") == 0 || + strcmp(claname, "bigbox_ops") == 0) + opclass = NIL; + } + + if (opclass == NIL) + { + /* no operator class specified, so find the default */ + opClassId = GetDefaultOpClass(attrType, accessMethodId); + if (!OidIsValid(opClassId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("data type %s has no default operator class for access method \"%s\"", + format_type_be(attrType), accessMethodName), + errhint("You must specify an operator class for the index or define a default operator class for the data type."))); + return opClassId; + } + + /* + * Specific opclass name given, so look up the opclass. + */ + + /* deconstruct the name list */ + DeconstructQualifiedName(opclass, &schemaname, &opcname); + + if (schemaname) + { + /* Look in specific schema only */ + Oid namespaceId; + +#if PG_VERSION_NUM >= 90300 + namespaceId = LookupExplicitNamespace(schemaname, false); +#else + namespaceId = LookupExplicitNamespace(schemaname); +#endif + tuple = SearchSysCache3(CLAAMNAMENSP, + ObjectIdGetDatum(accessMethodId), + PointerGetDatum(opcname), + ObjectIdGetDatum(namespaceId)); + } + else + { + /* Unqualified opclass name, so search the search path */ + opClassId = OpclassnameGetOpcid(accessMethodId, opcname); + if (!OidIsValid(opClassId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator class \"%s\" does not exist for access method \"%s\"", + opcname, accessMethodName))); + tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId)); + } + + if (!HeapTupleIsValid(tuple)) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("operator class \"%s\" does not exist for access method \"%s\"", + NameListToString(opclass), accessMethodName))); + } + + /* + * Verify that the index operator class accepts this datatype. Note we + * will accept binary compatibility. + */ + opClassId = HeapTupleGetOid(tuple); + opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype; + + if (!IsBinaryCoercible(attrType, opInputType)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("operator class \"%s\" does not accept data type %s", + NameListToString(opclass), format_type_be(attrType)))); + + ReleaseSysCache(tuple); + + return opClassId; +} + +/* + * Copied from src/backend/commands/indexcmds.c, not exported. + * CheckPredicate + * Checks that the given partial-index predicate is valid. + * + * This used to also constrain the form of the predicate to forms that + * indxpath.c could do something with. However, that seems overly + * restrictive. One useful application of partial indexes is to apply + * a UNIQUE constraint across a subset of a table, and in that scenario + * any evaluatable predicate will work. So accept any predicate here + * (except ones requiring a plan), and let indxpath.c fend for itself. + */ +void +CheckPredicate(Expr *predicate) +{ + /* + * transformExpr() should have already rejected subqueries, aggregates, + * and window functions, based on the EXPR_KIND_ for a predicate. + */ + + /* + * A predicate using mutable functions is probably wrong, for the same + * reasons that we don't allow an index expression to use one. + */ + if (CheckMutability(predicate)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("functions in index predicate must be marked IMMUTABLE"))); +} + +/* + * Copied from src/backend/commands/indexcmds.c, not exported. + * CheckMutability + * Test whether given expression is mutable + */ +bool +CheckMutability(Expr *expr) +{ + /* + * First run the expression through the planner. This has a couple of + * important consequences. First, function default arguments will get + * inserted, which may affect volatility (consider "default now()"). + * Second, inline-able functions will get inlined, which may allow us to + * conclude that the function is really less volatile than it's marked. As + * an example, polymorphic functions must be marked with the most volatile + * behavior that they have for any input type, but once we inline the + * function we may be able to conclude that it's not so volatile for the + * particular input type we're dealing with. + * + * We assume here that expression_planner() won't scribble on its input. + */ + expr = expression_planner(expr); + + /* Now we can search for non-immutable functions */ + return contain_mutable_functions((Node *) expr); +} + +#if PG_VERSION_NUM < 90500 +/* + * Copied from src/backend/commands/amcmds.c + * + * get_am_name - given an access method OID name and type, look up its name. + */ +char * +get_am_name(Oid amOid) +{ + HeapTuple tup; + char *result = NULL; + + tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid)); + if (HeapTupleIsValid(tup)) + { + Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup); + + result = pstrdup(NameStr(amform->amname)); + ReleaseSysCache(tup); + } + return result; +} +#endif diff --git a/hypopg_import.c b/import/hypopg_import_table.c similarity index 74% rename from hypopg_import.c rename to import/hypopg_import_table.c index df64d90..d08ce8a 100644 --- a/hypopg_import.c +++ b/import/hypopg_import_table.c @@ -1,6 +1,7 @@ /*------------------------------------------------------------------------- * - * hypopg_import.c: Import of some PostgreSQL private fuctions. + * hypopg_import_table.c: Import of some PostgreSQL private fuctions, used + * for hypothetical partitioning. * * This program is open source, licensed under the PostgreSQL license. * For license terms, see the LICENSE file. @@ -10,44 +11,22 @@ *------------------------------------------------------------------------- */ - #include "postgres.h" - -#if PG_VERSION_NUM >= 90300 -#include "access/htup_details.h" -#endif #if PG_VERSION_NUM >= 100000 + +#include "access/htup_details.h" #include "access/sysattr.h" #include "access/stratnum.h" -#endif -#include "catalog/heap.h" #include "catalog/namespace.h" -#if PG_VERSION_NUM >= 100000 +#include "catalog/partition.h" #include "catalog/pg_am.h" -#endif -#include "catalog/pg_opclass.h" -#include "commands/defrem.h" -#if PG_VERSION_NUM < 90500 -#include "lib/stringinfo.h" -#endif -#include "nodes/makefuncs.h" -#if PG_VERSION_NUM >= 100000 #include "catalog/pg_type.h" +#include "commands/defrem.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" -#include "utils/ruleutils.h" -#endif #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= 100000 -#include "catalog/partition.h" -#endif -#if PG_VERSION_NUM >= 110000 -#include "catalog/partition.h" #include "optimizer/cost.h" #include "optimizer/paths.h" -#include "partitioning/partbounds.h" -#include "utils/partcache.h" -#include "partitioning/partdefs.h" -#endif #include "optimizer/planner.h" #include "optimizer/var.h" #include "parser/parse_coerce.h" @@ -57,15 +36,21 @@ #include "parser/parser.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#if PG_VERSION_NUM >= 110000 #include "utils/rel.h" +#endif +#include "utils/ruleutils.h" #include "utils/syscache.h" +#if PG_VERSION_NUM >= 110000 +#include "partitioning/partbounds.h" +#include "partitioning/partdefs.h" +#include "utils/partcache.h" +#endif - -#include "include/hypopg_import.h" - +#include "include/hypopg_import_table.h" /* pg10 only imports */ -#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000 +#if PG_VERSION_NUM < 110000 static int32 partition_rbound_datum_cmp(PartitionKey key, Datum *rb_datums, PartitionRangeDatumKind *rb_kind, Datum *tuple_datums); @@ -321,314 +306,6 @@ partition_rbound_datum_cmp(PartitionKey key, } #endif /* pg10 only imports */ -/* Copied from src/backend/optimizer/util/plancat.c, not exported. - * - * Build a targetlist representing the columns of the specified index. - * Each column is represented by a Var for the corresponding base-relation - * column, or an expression in base-relation Vars, as appropriate. - * - * There are never any dropped columns in indexes, so unlike - * build_physical_tlist, we need no failure case. - */ -List * -build_index_tlist(PlannerInfo *root, IndexOptInfo *index, - Relation heapRelation) -{ - List *tlist = NIL; - Index varno = index->rel->relid; - ListCell *indexpr_item; - int i; - - indexpr_item = list_head(index->indexprs); - for (i = 0; i < index->ncolumns; i++) - { - int indexkey = index->indexkeys[i]; - Expr *indexvar; - - if (indexkey != 0) - { - /* simple column */ - const FormData_pg_attribute *att_tup; - - if (indexkey < 0) - att_tup = SystemAttributeDefinition(indexkey, - heapRelation->rd_rel->relhasoids); - else -#if PG_VERSION_NUM >= 110000 - att_tup = TupleDescAttr(heapRelation->rd_att, indexkey - 1); -#else - att_tup = heapRelation->rd_att->attrs[indexkey - 1]; -#endif - - indexvar = (Expr *) makeVar(varno, - indexkey, - att_tup->atttypid, - att_tup->atttypmod, - att_tup->attcollation, - 0); - } - else - { - /* expression column */ - if (indexpr_item == NULL) - elog(ERROR, "wrong number of index expressions"); - indexvar = (Expr *) lfirst(indexpr_item); - indexpr_item = lnext(indexpr_item); - } - - tlist = lappend(tlist, - makeTargetEntry(indexvar, - i + 1, - NULL, - false)); - } - if (indexpr_item != NULL) - elog(ERROR, "wrong number of index expressions"); - - return tlist; -} - -/* - * Copied from src/backend/commands/indexcmds.c, not exported. - * Resolve possibly-defaulted operator class specification - */ -Oid -GetIndexOpClass(List *opclass, Oid attrType, - char *accessMethodName, Oid accessMethodId) -{ - char *schemaname; - char *opcname; - HeapTuple tuple; - Oid opClassId, - opInputType; - - /* - * Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we - * ignore those opclass names so the default *_ops is used. This can be - * removed in some later release. bjm 2000/02/07 - * - * Release 7.1 removes lztext_ops, so suppress that too for a while. tgl - * 2000/07/30 - * - * Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that - * too for awhile. I'm starting to think we need a better approach. tgl - * 2000/10/01 - * - * Release 8.0 removes bigbox_ops (which was dead code for a long while - * anyway). tgl 2003/11/11 - */ - if (list_length(opclass) == 1) - { - char *claname = strVal(linitial(opclass)); - - if (strcmp(claname, "network_ops") == 0 || - strcmp(claname, "timespan_ops") == 0 || - strcmp(claname, "datetime_ops") == 0 || - strcmp(claname, "lztext_ops") == 0 || - strcmp(claname, "timestamp_ops") == 0 || - strcmp(claname, "bigbox_ops") == 0) - opclass = NIL; - } - - if (opclass == NIL) - { - /* no operator class specified, so find the default */ - opClassId = GetDefaultOpClass(attrType, accessMethodId); - if (!OidIsValid(opClassId)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("data type %s has no default operator class for access method \"%s\"", - format_type_be(attrType), accessMethodName), - errhint("You must specify an operator class for the index or define a default operator class for the data type."))); - return opClassId; - } - - /* - * Specific opclass name given, so look up the opclass. - */ - - /* deconstruct the name list */ - DeconstructQualifiedName(opclass, &schemaname, &opcname); - - if (schemaname) - { - /* Look in specific schema only */ - Oid namespaceId; - -#if PG_VERSION_NUM >= 90300 - namespaceId = LookupExplicitNamespace(schemaname, false); -#else - namespaceId = LookupExplicitNamespace(schemaname); -#endif - tuple = SearchSysCache3(CLAAMNAMENSP, - ObjectIdGetDatum(accessMethodId), - PointerGetDatum(opcname), - ObjectIdGetDatum(namespaceId)); - } - else - { - /* Unqualified opclass name, so search the search path */ - opClassId = OpclassnameGetOpcid(accessMethodId, opcname); - if (!OidIsValid(opClassId)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("operator class \"%s\" does not exist for access method \"%s\"", - opcname, accessMethodName))); - tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId)); - } - - if (!HeapTupleIsValid(tuple)) - { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("operator class \"%s\" does not exist for access method \"%s\"", - NameListToString(opclass), accessMethodName))); - } - - /* - * Verify that the index operator class accepts this datatype. Note we - * will accept binary compatibility. - */ - opClassId = HeapTupleGetOid(tuple); - opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype; - - if (!IsBinaryCoercible(attrType, opInputType)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("operator class \"%s\" does not accept data type %s", - NameListToString(opclass), format_type_be(attrType)))); - - ReleaseSysCache(tuple); - - return opClassId; -} - -/* - * Copied from src/backend/commands/indexcmds.c, not exported. - * CheckPredicate - * Checks that the given partial-index predicate is valid. - * - * This used to also constrain the form of the predicate to forms that - * indxpath.c could do something with. However, that seems overly - * restrictive. One useful application of partial indexes is to apply - * a UNIQUE constraint across a subset of a table, and in that scenario - * any evaluatable predicate will work. So accept any predicate here - * (except ones requiring a plan), and let indxpath.c fend for itself. - */ -void -CheckPredicate(Expr *predicate) -{ - /* - * transformExpr() should have already rejected subqueries, aggregates, - * and window functions, based on the EXPR_KIND_ for a predicate. - */ - - /* - * A predicate using mutable functions is probably wrong, for the same - * reasons that we don't allow an index expression to use one. - */ - if (CheckMutability(predicate)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("functions in index predicate must be marked IMMUTABLE"))); -} - -/* - * Copied from src/backend/commands/indexcmds.c, not exported. - * CheckMutability - * Test whether given expression is mutable - */ -bool -CheckMutability(Expr *expr) -{ - /* - * First run the expression through the planner. This has a couple of - * important consequences. First, function default arguments will get - * inserted, which may affect volatility (consider "default now()"). - * Second, inline-able functions will get inlined, which may allow us to - * conclude that the function is really less volatile than it's marked. As - * an example, polymorphic functions must be marked with the most volatile - * behavior that they have for any input type, but once we inline the - * function we may be able to conclude that it's not so volatile for the - * particular input type we're dealing with. - * - * We assume here that expression_planner() won't scribble on its input. - */ - expr = expression_planner(expr); - - /* Now we can search for non-immutable functions */ - return contain_mutable_functions((Node *) expr); -} - -#if PG_VERSION_NUM < 90500 -/* - * Copied from src/backend/commands/amcmds.c - * - * get_am_name - given an access method OID name and type, look up its name. - */ -char * -get_am_name(Oid amOid) -{ - HeapTuple tup; - char *result = NULL; - - tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid)); - if (HeapTupleIsValid(tup)) - { - Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup); - - result = pstrdup(NameStr(amform->amname)); - ReleaseSysCache(tup); - } - return result; -} -#endif - - -/* - * Copied from 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); -} - -#if PG_VERSION_NUM >= 100000 /* * Copied from src/backend/commands/tablecmds.c, not exported. * @@ -1712,123 +1389,4 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, *translated_vars = vars; } - -/* - * Copied from src/backend/commands/analyze.c/, not exported - * - * examine_attribute -- pre-analysis of a single column - * - * Determine whether the column is analyzable; if so, create and initialize - * a VacAttrStats struct for it. If not, return NULL. - * - * If index_expr isn't NULL, then we're trying to analyze an expression index, - * and index_expr is the expression tree representing the column's data. - */ -VacAttrStats * -examine_attribute(Relation onerel, int attnum, Node *index_expr) -{ - Form_pg_attribute attr = TupleDescAttr(onerel->rd_att, attnum - 1); - HeapTuple typtuple; - VacAttrStats *stats; - int i; - bool ok; - - /* Never analyze dropped columns */ - if (attr->attisdropped) - return NULL; - - /* Don't analyze column if user has specified not to */ - if (attr->attstattarget == 0) - return NULL; - - /* - * Create the VacAttrStats struct. Note that we only have a copy of the - * fixed fields of the pg_attribute tuple. - */ - stats = (VacAttrStats *) palloc0(sizeof(VacAttrStats)); - stats->attr = (Form_pg_attribute) palloc(ATTRIBUTE_FIXED_PART_SIZE); - memcpy(stats->attr, attr, ATTRIBUTE_FIXED_PART_SIZE); - - /* - * When analyzing an expression index, believe the expression tree's type - * not the column datatype --- the latter might be the opckeytype storage - * type of the opclass, which is not interesting for our purposes. (Note: - * if we did anything with non-expression index columns, we'd need to - * figure out where to get the correct type info from, but for now that's - * not a problem.) It's not clear whether anyone will care about the - * typmod, but we store that too just in case. - */ - if (index_expr) - { - stats->attrtypid = exprType(index_expr); - stats->attrtypmod = exprTypmod(index_expr); - } - else - { - stats->attrtypid = attr->atttypid; - stats->attrtypmod = attr->atttypmod; - } - - typtuple = SearchSysCacheCopy1(TYPEOID, - ObjectIdGetDatum(stats->attrtypid)); - if (!HeapTupleIsValid(typtuple)) - elog(ERROR, "cache lookup failed for type %u", stats->attrtypid); - stats->attrtype = (Form_pg_type) GETSTRUCT(typtuple); - //FIXME - stats->anl_context = CurrentMemoryContext; - stats->tupattnum = attnum; - - /* - * The fields describing the stats->stavalues[n] element types default to - * the type of the data being analyzed, but the type-specific typanalyze - * function can change them if it wants to store something else. - */ - for (i = 0; i < STATISTIC_NUM_SLOTS; i++) - { - stats->statypid[i] = stats->attrtypid; - stats->statyplen[i] = stats->attrtype->typlen; - stats->statypbyval[i] = stats->attrtype->typbyval; - stats->statypalign[i] = stats->attrtype->typalign; - } - - /* - * Call the type-specific typanalyze function. If none is specified, use - * std_typanalyze(). - */ - if (OidIsValid(stats->attrtype->typanalyze)) - ok = DatumGetBool(OidFunctionCall1(stats->attrtype->typanalyze, - PointerGetDatum(stats))); - else - ok = std_typanalyze(stats); - - if (!ok || stats->compute_stats == NULL || stats->minrows <= 0) - { - heap_freetuple(typtuple); - pfree(stats->attr); - pfree(stats); - return NULL; - } - - return stats; -} - -/* - * Copied from src/backend/commands/analyze.c, not exported - * - * Standard fetch function for use by compute_stats subroutines. - * - * This exists to provide some insulation between compute_stats routines - * and the actual storage of the sample data. - */ -Datum -std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull) -{ - int attnum = stats->tupattnum; - HeapTuple tuple = stats->rows[rownum]; - TupleDesc tupDesc = stats->tupDesc; - - return heap_getattr(tuple, attnum, tupDesc, isNull); -} - - -#endif +#endif /* pg10+ */ diff --git a/include/hypopg_import.h b/include/hypopg_import.h index bbcd9bf..9ef34e8 100644 --- a/include/hypopg_import.h +++ b/include/hypopg_import.h @@ -13,6 +13,7 @@ #define _HYPOPG_IMPORT_H_ #include "commands/vacuum.h" +#include "lib/stringinfo.h" #include "nodes/pg_list.h" #include "optimizer/planner.h" #include "optimizer/pathnode.h" @@ -20,143 +21,12 @@ #include "partitioning/partbounds.h" #endif #include "utils/rel.h" - - -/* adapted from nbtinsert.h */ -#define HYPO_BTMaxItemSize \ - MAXALIGN_DOWN((BLCKSZ - \ - MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \ - MAXALIGN(sizeof(BTPageOpaqueData))) / 3) - -extern List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, - Relation heapRelation); -extern Oid GetIndexOpClass(List *opclass, Oid attrType, - char *accessMethodName, Oid accessMethodId); - -extern void CheckPredicate(Expr *predicate); -extern bool CheckMutability(Expr *expr); -#if PG_VERSION_NUM < 90500 -extern char *get_am_name(Oid amOid); +#include "include/hypopg_import_index.h" +#if PG_VERSION_NUM >= 100000 +#include "include/hypopg_import_analyze.h" +#include "include/hypopg_import_table.h" #endif + extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); -#if PG_VERSION_NUM >= 100000 - -/* pg10 only imports */ -#if PG_VERSION_NUM < 110000 -/* - * Imported from src/backend/catalog/partition.c, not exported - */ -typedef struct PartitionBoundInfoData -{ - char strategy; /* hash, list or range? */ - int ndatums; /* Length of the datums following array */ - Datum **datums; - PartitionRangeDatumKind **kind; /* The kind of each range bound datum; - * NULL for hash and list partitioned - * tables */ - int *indexes; /* Partition indexes */ - int null_index; /* Index of the null-accepting partition; -1 - * if there isn't one */ -} PartitionBoundInfoData; - -/* One bound of a hash partition */ -typedef struct PartitionHashBound -{ - int modulus; - int remainder; - int index; -} PartitionHashBound; - -/* One value coming from some (index'th) list partition */ -typedef struct PartitionListValue -{ - int index; - Datum value; -} PartitionListValue; - -/* One bound of a range partition */ -typedef struct PartitionRangeBound -{ - int index; - Datum *datums; /* range bound datums */ - PartitionRangeDatumKind *kind; /* the kind of each datum */ - bool lower; /* this is the lower (vs upper) bound */ -} PartitionRangeBound; - -/* - * Entry of a hash table used in find_all_inheritors. See below. - */ -typedef struct SeenRelsEntry -{ - Oid rel_id; /* relation oid */ - ListCell *numparents_cell; /* corresponding list cell */ -} SeenRelsEntry; - -PartitionRangeBound *make_one_range_bound(PartitionKey key, int index, - List *datums, bool lower); -int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, - void *probe, bool probe_is_bound, bool *is_equal); -int32 partition_bound_cmp(PartitionKey key, - PartitionBoundInfo boundinfo, - int offset, void *probe, bool probe_is_bound); - -int32 partition_rbound_cmp(PartitionKey key, - Datum *datums1, PartitionRangeDatumKind *kind1, - bool lower1, PartitionRangeBound *b2); -#endif /* pg10 only imports */ - -/* Context info needed for invoking a recursive querytree display routine */ -typedef struct -{ - StringInfo buf; /* output buffer to append to */ - // List *namespaces; /* List of deparse_namespace nodes */ - // List *windowClause; /* Current query level's WINDOW clause */ - // List *windowTList; /* targetlist for resolving WINDOW clause */ - // int prettyFlags; /* enabling of pretty-print functions */ - // int wrapColumn; /* max line length, or -1 for no limit */ - // int indentLevel; /* current indent level for prettyprint */ - // bool varprefix; /* true to print prefixes on Vars */ - // ParseExprKind special_exprkind; /* set only for exprkinds needing special - // * handling */ -} deparse_context; - - -PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, - char *strategy); -void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber - *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, - char strategy); -char *get_relation_name(Oid relid); -bool looks_like_function(Node *node); -int32 qsort_partition_hbound_cmp(const void *a, const void *b); -int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg); -int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg); -void get_const_expr(Const *constval, deparse_context *context, int - showtype); -void get_const_collation(Const *constval, deparse_context *context); -void simple_quote_literal(StringInfo buf, const char *val); -Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con, - const char *colName, Oid colType, int32 colTypmod); -void validateInfiniteBounds(ParseState *pstate, List *blist); -Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, - bool *need_relabel); -Expr *make_partition_op_expr(PartitionKey key, int keynum, - uint16 strategy, Expr *arg1, Expr *arg2); -void get_range_key_properties(PartitionKey key, int keynum, - PartitionRangeDatum *ldatum, - PartitionRangeDatum *udatum, - ListCell **partexprs_item, - Expr **keyCol, - Const **lower_val, Const **upper_val); -List *get_range_nulltest(PartitionKey key); -void make_inh_translation_list(Relation oldrelation, Relation newrelation, - Index newvarno, - List **translated_vars); -VacAttrStats *examine_attribute(Relation onerel, int attnum, Node *index_expr); -Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); -/* Copied from src/backend/catalog/partition.c, not exported */ -#define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1) - -#endif -#endif +#endif /* _HYPOPG_IMPORT_H_ */ diff --git a/include/hypopg_import_analyze.h b/include/hypopg_import_analyze.h new file mode 100644 index 0000000..b0cef0f --- /dev/null +++ b/include/hypopg_import_analyze.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * hypopg_import_analyze.h: Import of some PostgreSQL private fuctions, used + * for hypothetical analyze. + * + * This program is open source, licensed under the PostgreSQL license. + * For license terms, see the LICENSE file. + * + * Copyright (c) 2008-2018, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#ifndef _HYPOPG_IMPORT_ANALYZE_H_ +#define _HYPOPG_IMPORT_ANALYZE_H_ + +#if PG_VERSION_NUM < 100000 +#error "This could should only be included on pg10+ code" +#endif + + +VacAttrStats *examine_attribute(Relation onerel, int attnum, Node *index_expr); +Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); + +#endif /* _HYPOPG_IMPORT_ANALYZE_H_ */ diff --git a/include/hypopg_import_index.h b/include/hypopg_import_index.h new file mode 100644 index 0000000..4715771 --- /dev/null +++ b/include/hypopg_import_index.h @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------- + * + * hypopg_import_index.h: Import of some PostgreSQL private fuctions, used for + * hypothetical index. + * + * This program is open source, licensed under the PostgreSQL license. + * For license terms, see the LICENSE file. + * + * Copyright (c) 2008-2018, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#ifndef _HYPOPG_IMPORT_INDEX_H_ +#define _HYPOPG_IMPORT_INDEX_H_ + +/* adapted from nbtinsert.h */ +#define HYPO_BTMaxItemSize \ + MAXALIGN_DOWN((BLCKSZ - \ + MAXALIGN(SizeOfPageHeaderData + 3*sizeof(ItemIdData)) - \ + MAXALIGN(sizeof(BTPageOpaqueData))) / 3) + +extern List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, + Relation heapRelation); +extern Oid GetIndexOpClass(List *opclass, Oid attrType, + char *accessMethodName, Oid accessMethodId); + +extern void CheckPredicate(Expr *predicate); +extern bool CheckMutability(Expr *expr); +#if PG_VERSION_NUM < 90500 +extern char *get_am_name(Oid amOid); +#endif + +#endif /* _HYPOPG_IMPORT_INDEX_H_ */ diff --git a/include/hypopg_import_table.h b/include/hypopg_import_table.h new file mode 100644 index 0000000..46107cb --- /dev/null +++ b/include/hypopg_import_table.h @@ -0,0 +1,137 @@ +/*------------------------------------------------------------------------- + * + * hypopg_import_table.h: Import of some PostgreSQL private fuctions, used + * for hypothetical partitioning. + * + * This program is open source, licensed under the PostgreSQL license. + * For license terms, see the LICENSE file. + * + * Copyright (c) 2008-2018, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#ifndef _HYPOPG_IMPORT_TABLE_H_ +#define _HYPOPG_IMPORT_TABLE_H_ + +#include "catalog/partition.h" + +#if PG_VERSION_NUM < 100000 +#error "This could should only be included on pg10+ code" +#endif + + +/* pg10 only imports */ +#if PG_VERSION_NUM < 110000 +/* + * Imported from src/backend/catalog/partition.c, not exported + */ +typedef struct PartitionBoundInfoData +{ + char strategy; /* hash, list or range? */ + int ndatums; /* Length of the datums following array */ + Datum **datums; + PartitionRangeDatumKind **kind; /* The kind of each range bound datum; + * NULL for hash and list partitioned + * tables */ + int *indexes; /* Partition indexes */ + int null_index; /* Index of the null-accepting partition; -1 + * if there isn't one */ +} PartitionBoundInfoData; + +/* One bound of a hash partition */ +typedef struct PartitionHashBound +{ + int modulus; + int remainder; + int index; +} PartitionHashBound; + +/* One value coming from some (index'th) list partition */ +typedef struct PartitionListValue +{ + int index; + Datum value; +} PartitionListValue; + +/* One bound of a range partition */ +typedef struct PartitionRangeBound +{ + int index; + Datum *datums; /* range bound datums */ + PartitionRangeDatumKind *kind; /* the kind of each datum */ + bool lower; /* this is the lower (vs upper) bound */ +} PartitionRangeBound; + +/* + * Entry of a hash table used in find_all_inheritors. See below. + */ +typedef struct SeenRelsEntry +{ + Oid rel_id; /* relation oid */ + ListCell *numparents_cell; /* corresponding list cell */ +} SeenRelsEntry; + +PartitionRangeBound *make_one_range_bound(PartitionKey key, int index, + List *datums, bool lower); +int partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo, + void *probe, bool probe_is_bound, bool *is_equal); +int32 partition_bound_cmp(PartitionKey key, + PartitionBoundInfo boundinfo, + int offset, void *probe, bool probe_is_bound); + +int32 partition_rbound_cmp(PartitionKey key, + Datum *datums1, PartitionRangeDatumKind *kind1, + bool lower1, PartitionRangeBound *b2); +#endif /* pg10 only imports */ + +/* Context info needed for invoking a recursive querytree display routine */ +typedef struct +{ + StringInfo buf; /* output buffer to append to */ + // List *namespaces; /* List of deparse_namespace nodes */ + // List *windowClause; /* Current query level's WINDOW clause */ + // List *windowTList; /* targetlist for resolving WINDOW clause */ + // int prettyFlags; /* enabling of pretty-print functions */ + // int wrapColumn; /* max line length, or -1 for no limit */ + // int indentLevel; /* current indent level for prettyprint */ + // bool varprefix; /* true to print prefixes on Vars */ + // ParseExprKind special_exprkind; /* set only for exprkinds needing special + // * handling */ +} deparse_context; + + +PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, + char *strategy); +void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber + *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, + char strategy); +char *get_relation_name(Oid relid); +bool looks_like_function(Node *node); +int32 qsort_partition_hbound_cmp(const void *a, const void *b); +int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg); +int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg); +void get_const_expr(Const *constval, deparse_context *context, int + showtype); +void get_const_collation(Const *constval, deparse_context *context); +void simple_quote_literal(StringInfo buf, const char *val); +Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con, + const char *colName, Oid colType, int32 colTypmod); +void validateInfiniteBounds(ParseState *pstate, List *blist); +Oid get_partition_operator(PartitionKey key, int col, StrategyNumber strategy, + bool *need_relabel); +Expr *make_partition_op_expr(PartitionKey key, int keynum, + uint16 strategy, Expr *arg1, Expr *arg2); +void get_range_key_properties(PartitionKey key, int keynum, + PartitionRangeDatum *ldatum, + PartitionRangeDatum *udatum, + ListCell **partexprs_item, + Expr **keyCol, + Const **lower_val, Const **upper_val); +List *get_range_nulltest(PartitionKey key); +void make_inh_translation_list(Relation oldrelation, Relation newrelation, + Index newvarno, + List **translated_vars); +/* Copied from src/backend/catalog/partition.c, not exported */ +#define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1) + +#endif /* _HYPOPG_IMPORT_TABLE_H_ */