public inbox for [email protected]help / color / mirror / Atom feed
[PATCH 2/9] psql and pg_dump support for partitioned tables. 111+ messages / 3 participants [nested] [flat]
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 51b8a1a..854dddc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5211,6 +5215,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5296,7 +5301,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5308,7 +5314,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5322,7 +5328,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5884,6 +5891,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5954,6 +5962,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5998,7 +6007,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6100,7 +6111,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15453,6 +15467,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15513,6 +15530,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15531,7 +15549,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15748,6 +15767,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..db6dc5c 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 1394362..260ec34 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -407,3 +407,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index d383f58..7a1f8de 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -401,3 +401,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------C3235EE96C69D8DAE4DB543E Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-6.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-6.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/8] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 147 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 +++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 267 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ec966c7..4691b5b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,151 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_partexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_partexprs, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + appendStringInfo(&buf, "PARTITION BY "); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + prettyFlags, 0); + + /* Need parens if it's not a bare function call */ + if (partkey && IsA(partkey, FuncExpr) && + ((FuncExpr *) partkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfoString(&buf, str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 4ee10fc..a127672 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_REL); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_REL) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6083,7 +6094,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15436,6 +15450,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_REL) + appendPQExpBuffer(q, "\n%s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15496,6 +15513,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15514,7 +15532,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15731,6 +15750,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_REL || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 27be102..3ba8a90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -856,6 +856,7 @@ permissionsList(const char *pattern) " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " ", gettext_noop("Schema"), @@ -865,6 +866,7 @@ permissionsList(const char *pattern) gettext_noop("materialized view"), gettext_noop("sequence"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type")); printACLColumn(&buf, "c.relacl"); @@ -911,7 +913,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1555,8 +1557,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1621,6 +1623,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1634,8 +1644,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1655,12 +1665,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1760,7 +1770,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1768,14 +1778,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (pset.sversion >= 90600 && tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1914,7 +1943,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2473,7 +2502,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2684,7 +2713,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3018,6 +3047,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " WHEN 'S' THEN '%s'" " WHEN 's' THEN '%s'" " WHEN 'f' THEN '%s'" + " WHEN 'P' THEN '%s'" " END as \"%s\",\n" " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"", gettext_noop("Schema"), @@ -3029,6 +3059,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("sequence"), gettext_noop("special"), gettext_noop("foreign table"), + gettext_noop("partitioned table"), gettext_noop("Type"), gettext_noop("Owner")); @@ -3067,7 +3098,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 8469d9f..cd4eb7c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 270dd21..5a444d9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1969,6 +1969,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8cebc86..351bfd3 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -722,6 +722,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index fb7a1b3..4fc8c06 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -413,3 +413,29 @@ SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::reg (1 row) DROP TABLE no_inh_con_parted; +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Partitioned table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY RANGE (((a + b))) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Partitioned table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: PARTITION BY LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index cb17517..7c7a18a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -404,3 +404,16 @@ CREATE TABLE no_inh_con_parted ( ) PARTITION BY RANGE (a); SELECT connoinherit FROM pg_constraint WHERE conrelid = 'no_inh_con_parted'::regclass; DROP TABLE no_inh_con_parted; + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------8C4C48E640631A327D3D2826 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 1345e4e..4686cd1 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------954458C17BBEB1BF6FC758E7 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-2.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-2.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------4012470FC48539D6F6714CEA Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-4.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-4.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..77ce807 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relationId = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relationId, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relId, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relId); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partedrelid == relId); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relId), relId); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relId, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relId, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a5c2d09..c418ba7 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2117,6 +2118,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5195,6 +5199,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5280,7 +5285,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5292,7 +5298,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5306,7 +5312,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5868,6 +5875,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5938,6 +5946,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5982,7 +5991,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6084,7 +6095,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15437,6 +15451,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15497,6 +15514,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15515,7 +15533,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15732,6 +15751,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 019f75a..5e5c370 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 708232d..2fec847 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -408,3 +408,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 6e4a8be..4dd6a0a 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -402,3 +402,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------6EB5B318125CB7DB1A4FD229 Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-3.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-3.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH 2/9] psql and pg_dump support for partitioned tables. @ 2016-07-12 08:20 amit <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: amit @ 2016-07-12 08:20 UTC (permalink / raw) Takes care of both the partition key deparse stuff and the new relkind. --- src/backend/utils/adt/ruleutils.c | 140 ++++++++++++++++++++++++++++ src/bin/pg_dump/pg_dump.c | 36 ++++++-- src/bin/pg_dump/pg_dump.h | 1 + src/bin/psql/describe.c | 61 +++++++++--- src/bin/psql/tab-complete.c | 6 +- src/include/catalog/pg_proc.h | 2 + src/include/utils/builtins.h | 1 + src/test/regress/expected/create_table.out | 26 +++++ src/test/regress/sql/create_table.sql | 13 +++ 9 files changed, 260 insertions(+), 26 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..03be202 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -33,6 +33,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" +#include "catalog/pg_partitioned_table.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -315,6 +316,7 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno, const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags, bool missing_ok); +static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, @@ -1389,6 +1391,144 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, return buf.data; } +/* + * pg_get_partkeydef + * + * Returns the partition key specification, ie, the following: + * + * PARTITION BY { RANGE | LIST } (column [ opclass_name ] [, ...]) + */ +Datum +pg_get_partkeydef(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int prettyFlags; + + prettyFlags = PRETTYFLAG_INDENT; + PG_RETURN_TEXT_P(string_to_text(pg_get_partkeydef_worker(relid, + prettyFlags))); +} + +/* + * Internal workhorse to decompile a partition key definition. + */ +static char * +pg_get_partkeydef_worker(Oid relid, int prettyFlags) +{ + Form_pg_partitioned_table form; + HeapTuple tuple; + oidvector *partclass; + List *partexprs; + ListCell *partexpr_item; + List *context; + Datum datum; + bool isnull; + StringInfoData buf; + int keyno; + char *str; + char *sep; + + tuple = SearchSysCache1(PARTEDRELID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for partition key of %u", relid); + + form = (Form_pg_partitioned_table) GETSTRUCT(tuple); + + Assert(form->partrelid == relid); + + /* Must get partclass, and partexprs the hard way */ + datum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partclass, &isnull); + Assert(!isnull); + partclass = (oidvector *) DatumGetPointer(datum); + + /* + * Get the partition key expressions, if any. (NOTE: we do not use the + * relcache versions of the expressions, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprbin)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(PARTEDRELID, tuple, + Anum_pg_partitioned_table_partexprbin, &isnull); + Assert(!isnull); + exprsString = TextDatumGetCString(exprsDatum); + partexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + partexprs = NIL; + + partexpr_item = list_head(partexprs); + context = deparse_context_for(get_relation_name(relid), relid); + + /* + * Start the partition key definition. + */ + initStringInfo(&buf); + + switch (form->partstrat) + { + case 'l': + appendStringInfo(&buf, "LIST"); + break; + case 'r': + appendStringInfo(&buf, "RANGE"); + break; + } + + /* + * Report the partition key columns + */ + appendStringInfo(&buf, " ("); + sep = ""; + for (keyno = 0; keyno < form->partnatts; keyno++) + { + AttrNumber attnum = form->partattrs.values[keyno]; + Oid keycoltype; + + appendStringInfoString(&buf, sep); + sep = ", "; + if (attnum != 0) + { + /* Simple partition key column */ + char *attname; + + attname = get_relid_attribute_name(relid, attnum); + appendStringInfoString(&buf, quote_identifier(attname)); + keycoltype = get_atttype(relid, attnum); + } + else + { + /* partition key expression */ + Node *partkey; + + if (partexpr_item == NULL) + elog(ERROR, "too few entries in partexprs list"); + partkey = (Node *) lfirst(partexpr_item); + partexpr_item = lnext(partexpr_item); + /* Deparse */ + str = deparse_expression_pretty(partkey, context, false, false, + 0, 0); + + appendStringInfoString(&buf, str); + keycoltype = exprType(partkey); + } + + /* Add the operator class name, if not default */ + get_opclass_name(partclass->values[keyno], keycoltype, &buf); + } + appendStringInfoChar(&buf, ')'); + + /* Clean up */ + ReleaseSysCache(tuple); + + return buf.data; +} /* * pg_get_constraintdef diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..c805a84 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1253,9 +1253,10 @@ expand_table_name_patterns(Archive *fout, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace" - "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c')\n", + "\nWHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c')\n", RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "pg_catalog.pg_table_is_visible(c.oid)"); @@ -2125,6 +2126,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo, bool oids) /* Skip FOREIGN TABLEs (no data to dump) */ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) return; + /* Skip partitioned tables (data in partitions) */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + return; /* Don't dump data in unlogged tables, if so requested */ if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED && @@ -5204,6 +5208,7 @@ getTables(Archive *fout, int *numTables) int i_reloftype; int i_relpages; int i_changed_acl; + int i_partkeydef; /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); @@ -5289,7 +5294,8 @@ getTables(Archive *fout, int *numTables) "OR %s IS NOT NULL " "OR %s IS NOT NULL" "))" - "AS changed_acl " + "AS changed_acl, " + "CASE WHEN c.relkind = 'P' THEN pg_catalog.pg_get_partkeydef(c.oid) ELSE NULL END AS partkeydef " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -5301,7 +5307,7 @@ getTables(Archive *fout, int *numTables) "(c.oid = pip.objoid " "AND pip.classoid = 'pg_class'::regclass " "AND pip.objsubid = 0) " - "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c', '%c') " "ORDER BY c.oid", acl_subquery->data, racl_subquery->data, @@ -5315,7 +5321,8 @@ getTables(Archive *fout, int *numTables) RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, - RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE); + RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE, + RELKIND_PARTITIONED_TABLE); destroyPQExpBuffer(acl_subquery); destroyPQExpBuffer(racl_subquery); @@ -5877,6 +5884,7 @@ getTables(Archive *fout, int *numTables) i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); i_changed_acl = PQfnumber(res, "changed_acl"); + i_partkeydef = PQfnumber(res, "partkeydef"); if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300) { @@ -5947,6 +5955,7 @@ getTables(Archive *fout, int *numTables) else tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef)); /* other fields were zeroed above */ @@ -5991,7 +6000,9 @@ getTables(Archive *fout, int *numTables) * We only need to lock the table for certain components; see * pg_dump.h */ - if (tblinfo[i].dobj.dump && tblinfo[i].relkind == RELKIND_RELATION && + if (tblinfo[i].dobj.dump && + (tblinfo[i].relkind == RELKIND_RELATION || + tblinfo->relkind == RELKIND_PARTITIONED_TABLE) && (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK)) { resetPQExpBuffer(query); @@ -6093,7 +6104,10 @@ getInherits(Archive *fout, int *numInherits) /* find all the inheritance information */ - appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits"); + appendPQExpBufferStr(query, + "SELECT inhrelid, inhparent " + "FROM pg_inherits " + "WHERE inhparent NOT IN (SELECT oid FROM pg_class WHERE relkind = 'P')"); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); @@ -15446,6 +15460,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBufferChar(q, ')'); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE) + appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef); + if (tbinfo->relkind == RELKIND_FOREIGN_TABLE) appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname)); } @@ -15506,6 +15523,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) */ if (dopt->binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_FOREIGN_TABLE)) { for (j = 0; j < tbinfo->numatts; j++) @@ -15524,7 +15542,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendStringLiteralAH(q, fmtId(tbinfo->dobj.name), fout); appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - if (tbinfo->relkind == RELKIND_RELATION) + if (tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", fmtId(tbinfo->dobj.name)); else @@ -15741,6 +15760,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * dump properties we only have ALTER TABLE syntax for */ if ((tbinfo->relkind == RELKIND_RELATION || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE || tbinfo->relkind == RELKIND_MATVIEW) && tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT) { diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 2bfa2d9..0292859 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo bool *inhNotNull; /* true if NOT NULL is inherited */ struct _attrDefInfo **attrdefs; /* DEFAULT expressions */ struct _constraintInfo *checkexprs; /* CHECK constraints */ + char *partkeydef; /* partition key definition */ /* * Stuff computed only for dumpable tables. diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6275a68..10d924a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -861,6 +861,7 @@ permissionsList(const char *pattern) " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'S' THEN '%s'" @@ -870,6 +871,7 @@ permissionsList(const char *pattern) gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("sequence"), @@ -920,7 +922,7 @@ permissionsList(const char *pattern) appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n" " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" - "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f')\n"); + "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n"); /* * Unless a schema pattern is specified, we suppress system and temp @@ -1567,8 +1569,8 @@ describeOneTableDetails(const char *schemaname, * types, and foreign tables (c.f. CommentObject() in comment.c). */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)"); } @@ -1633,6 +1635,14 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""), schemaname, relationname); break; + case 'P': + if (tableinfo.relpersistence == 'u') + printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""), + schemaname, relationname); + else + printfPQExpBuffer(&title, _("Table \"%s.%s\""), + schemaname, relationname); + break; default: /* untranslated unknown relkind */ printfPQExpBuffer(&title, "?%c? \"%s.%s\"", @@ -1646,8 +1656,8 @@ describeOneTableDetails(const char *schemaname, cols = 2; if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'f' || tableinfo.relkind == 'c') + tableinfo.relkind == 'm' || tableinfo.relkind == 'f' || + tableinfo.relkind == 'c' || tableinfo.relkind == 'P') { show_modifiers = true; headers[cols++] = gettext_noop("Modifiers"); @@ -1667,12 +1677,12 @@ describeOneTableDetails(const char *schemaname, { headers[cols++] = gettext_noop("Storage"); if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Stats target"); /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') headers[cols++] = gettext_noop("Description"); } @@ -1772,7 +1782,7 @@ describeOneTableDetails(const char *schemaname, /* Statistics target, if the relkind supports this feature */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1), false, false); @@ -1780,14 +1790,33 @@ describeOneTableDetails(const char *schemaname, /* Column comments, if the relkind supports this feature. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' || - tableinfo.relkind == 'm' || - tableinfo.relkind == 'c' || tableinfo.relkind == 'f') + tableinfo.relkind == 'm' || tableinfo.relkind == 'c' || + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2), false, false); } } /* Make footers */ + if (tableinfo.relkind == 'P') + { + /* Get the partition key information */ + PGresult *result; + char *partkeydef; + + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);", + oid); + result = PSQLexec(buf.data); + if (!result || PQntuples(result) != 1) + goto error_return; + + partkeydef = PQgetvalue(result, 0, 0); + printfPQExpBuffer(&tmpbuf, _("Partition Key: %s"), partkeydef); + printTableAddFooter(&cont, tmpbuf.data); + PQclear(result); + } + if (tableinfo.relkind == 'i') { /* Footer information about an index */ @@ -1926,7 +1955,7 @@ describeOneTableDetails(const char *schemaname, PQclear(result); } else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { /* Footer information about a table */ PGresult *result = NULL; @@ -2485,7 +2514,7 @@ describeOneTableDetails(const char *schemaname, * Finish printing the footer information about a table. */ if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' || - tableinfo.relkind == 'f') + tableinfo.relkind == 'f' || tableinfo.relkind == 'P') { PGresult *result; int tuples; @@ -2696,7 +2725,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline) { /* relkinds for which we support tablespaces */ - if (relkind == 'r' || relkind == 'm' || relkind == 'i') + if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P') { /* * We ignore the database default tablespace so that users not using @@ -3024,6 +3053,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys " c.relname as \"%s\",\n" " CASE c.relkind" " WHEN 'r' THEN '%s'" + " WHEN 'P' THEN '%s'" " WHEN 'v' THEN '%s'" " WHEN 'm' THEN '%s'" " WHEN 'i' THEN '%s'" @@ -3035,6 +3065,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("table"), + gettext_noop("table"), gettext_noop("view"), gettext_noop("materialized view"), gettext_noop("index"), @@ -3079,7 +3110,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN ("); if (showTables) - appendPQExpBufferStr(&buf, "'r',"); + appendPQExpBufferStr(&buf, "'r', 'P',"); if (showViews) appendPQExpBufferStr(&buf, "'v',"); if (showMatViews) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50a45eb..8284a9c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -427,7 +427,7 @@ static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r')", + "c.relkind IN ('r', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -458,7 +458,7 @@ static const SchemaQuery Query_for_list_of_updatables = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f', 'v')", + "c.relkind IN ('r', 'f', 'v', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -488,7 +488,7 @@ static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'm', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f', 'P')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..b45688b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1980,6 +1980,8 @@ DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); +DATA(insert OID = 3352 ( pg_get_partkeydef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ )); +DESCR("partition key description"); DATA(insert OID = 1662 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ )); DESCR("trigger description"); DATA(insert OID = 1387 ( pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2ae212a..e800647 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -729,6 +729,7 @@ extern Datum pg_get_viewdef_wrap(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); +extern Datum pg_get_partkeydef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 0af1422..91ba1be 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -412,3 +412,29 @@ CREATE TABLE no_inh_con_parted ( CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted" +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +Table "public.describe_range_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: RANGE ((a + b)) + +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +Table "public.describe_list_key" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + b | integer | +Partition Key: LIST (a) + +DROP TABLE describe_range_key, describe_list_key; diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 418ac55..b75542d 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -406,3 +406,16 @@ CREATE TABLE no_inh_con_parted ( a int, CONSTRAINT check_a CHECK (a > 0) NO INHERIT ) PARTITION BY RANGE (a); + +-- Partition key in describe output +CREATE TABLE describe_range_key ( + a int, + b int +) PARTITION BY RANGE ((a+b)); +\d describe_range_key +CREATE TABLE describe_list_key ( + a int, + b int +) PARTITION BY LIST (a); +\d describe_list_key +DROP TABLE describe_range_key, describe_list_key; -- 1.7.1 --------------D008BA677674C4836D7B65AD Content-Type: text/x-diff; name="0003-Catalog-and-DDL-for-partitions-5.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-Catalog-and-DDL-for-partitions-5.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH v6 4/7] Row pattern recognition patch (executor). @ 2023-09-12 05:22 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: Tatsuo Ishii @ 2023-09-12 05:22 UTC (permalink / raw) --- src/backend/executor/nodeWindowAgg.c | 842 ++++++++++++++++++++++++++- src/backend/utils/adt/windowfuncs.c | 37 +- src/include/catalog/pg_proc.dat | 6 + src/include/nodes/execnodes.h | 26 + 4 files changed, 898 insertions(+), 13 deletions(-) diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index 310ac23e3a..32270d051a 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -36,6 +36,7 @@ #include "access/htup_details.h" #include "catalog/objectaccess.h" #include "catalog/pg_aggregate.h" +#include "catalog/pg_collation_d.h" #include "catalog/pg_proc.h" #include "executor/executor.h" #include "executor/nodeWindowAgg.h" @@ -48,6 +49,7 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" +#include "utils/fmgroids.h" #include "utils/expandeddatum.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -182,8 +184,9 @@ static void begin_partition(WindowAggState *winstate); static void spool_tuples(WindowAggState *winstate, int64 pos); static void release_partition(WindowAggState *winstate); -static int row_is_in_frame(WindowAggState *winstate, int64 pos, +static int row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot); + static void update_frameheadpos(WindowAggState *winstate); static void update_frametailpos(WindowAggState *winstate); static void update_grouptailpos(WindowAggState *winstate); @@ -195,9 +198,32 @@ static Datum GetAggInitVal(Datum textInitVal, Oid transtype); static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2); -static bool window_gettupleslot(WindowObject winobj, int64 pos, - TupleTableSlot *slot); +static int WinGetSlotInFrame(WindowObject winobj, TupleTableSlot *slot, + int relpos, int seektype, bool set_mark, + bool *isnull, bool *isout); +static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot); + +static void attno_map(Node *node); +static bool attno_map_walker(Node *node, void *context); +static int row_is_in_reduced_frame(WindowObject winobj, int64 pos); +static bool rpr_is_defined(WindowAggState *winstate); + +static void create_reduced_frame_map(WindowAggState *winstate); +static int get_reduced_frame_map(WindowAggState *winstate, int64 pos); +static void register_reduced_frame_map(WindowAggState *winstate, int64 pos, int val); +static void clear_reduced_frame_map(WindowAggState *winstate); +static void update_reduced_frame(WindowObject winobj, int64 pos); + +static int64 evaluate_pattern(WindowObject winobj, int64 current_pos, + char *vname, StringInfo encoded_str, bool *result); + +static bool get_slots(WindowObject winobj, int64 current_pos); + +static int search_str_set(char *pattern, StringInfo *str_set, int set_size); +static void search_str_set_recurse(char *pattern, StringInfo *str_set, int set_size, int set_index, + char *encoded_str, int *resultlen); +static char pattern_initial(WindowAggState *winstate, char *vname); /* * initialize_windowaggregate @@ -673,6 +699,7 @@ eval_windowaggregates(WindowAggState *winstate) WindowObject agg_winobj; TupleTableSlot *agg_row_slot; TupleTableSlot *temp_slot; + bool agg_result_isnull; numaggs = winstate->numaggs; if (numaggs == 0) @@ -778,6 +805,9 @@ eval_windowaggregates(WindowAggState *winstate) * Note that we don't strictly need to restart in the last case, but if * we're going to remove all rows from the aggregation anyway, a restart * surely is faster. + * + * - if RPR is enabled and skip mode is SKIP TO NEXT ROW, + * we restart aggregation too. *---------- */ numaggs_restart = 0; @@ -788,8 +818,11 @@ eval_windowaggregates(WindowAggState *winstate) (winstate->aggregatedbase != winstate->frameheadpos && !OidIsValid(peraggstate->invtransfn_oid)) || (winstate->frameOptions & FRAMEOPTION_EXCLUSION) || - winstate->aggregatedupto <= winstate->frameheadpos) + winstate->aggregatedupto <= winstate->frameheadpos || + (rpr_is_defined(winstate) && + winstate->rpSkipTo == ST_NEXT_ROW)) { + elog(DEBUG1, "peraggstate->restart is set"); peraggstate->restart = true; numaggs_restart++; } @@ -861,8 +894,10 @@ eval_windowaggregates(WindowAggState *winstate) * If we created a mark pointer for aggregates, keep it pushed up to frame * head, so that tuplestore can discard unnecessary rows. */ +#ifdef NOT_USED if (agg_winobj->markptr >= 0) WinSetMarkPosition(agg_winobj, winstate->frameheadpos); +#endif /* * Now restart the aggregates that require it. @@ -917,6 +952,29 @@ eval_windowaggregates(WindowAggState *winstate) { winstate->aggregatedupto = winstate->frameheadpos; ExecClearTuple(agg_row_slot); + + /* + * If RPR is defined, we do not use aggregatedupto_nonrestarted. To + * avoid assertion failure below, we reset aggregatedupto_nonrestarted + * to frameheadpos. + */ + if (rpr_is_defined(winstate)) + aggregatedupto_nonrestarted = winstate->frameheadpos; + } + + agg_result_isnull = false; + /* RPR is defined? */ + if (rpr_is_defined(winstate)) + { + /* + * If the skip mode is SKIP TO PAST LAST ROW and we already know that + * current row is a skipped row or an unmatched row, we don't need to + * accumulate rows, just return NULL. + */ + if (winstate->rpSkipTo == ST_PAST_LAST_ROW && + (get_reduced_frame_map(winstate, winstate->currentpos) == RF_SKIPPED || + get_reduced_frame_map(winstate, winstate->currentpos) == RF_UNMATCHED)) + agg_result_isnull = true; } /* @@ -930,6 +988,11 @@ eval_windowaggregates(WindowAggState *winstate) { int ret; + elog(DEBUG1, "===== loop in frame starts: " INT64_FORMAT, winstate->aggregatedupto); + + if (agg_result_isnull) + break; + /* Fetch next row if we didn't already */ if (TupIsNull(agg_row_slot)) { @@ -945,9 +1008,28 @@ eval_windowaggregates(WindowAggState *winstate) ret = row_is_in_frame(winstate, winstate->aggregatedupto, agg_row_slot); if (ret < 0) break; + if (ret == 0) goto next_tuple; + if (rpr_is_defined(winstate)) + { + /* + * If the row status at currentpos is already decided and current + * row status is not decided yet, it means we passed the last + * reduced frame. Time to break the loop. + */ + if (get_reduced_frame_map(winstate, winstate->currentpos) != RF_NOT_DETERMINED && + get_reduced_frame_map(winstate, winstate->aggregatedupto) == RF_NOT_DETERMINED) + break; + /* + * Otherwise we need to calculate the reduced frame. + */ + ret = row_is_in_reduced_frame(winstate->agg_winobj, winstate->aggregatedupto); + if (ret == -1) /* unmatched row */ + break; + } + /* Set tuple context for evaluation of aggregate arguments */ winstate->tmpcontext->ecxt_outertuple = agg_row_slot; @@ -976,6 +1058,7 @@ next_tuple: ExecClearTuple(agg_row_slot); } + /* The frame's end is not supposed to move backwards, ever */ Assert(aggregatedupto_nonrestarted <= winstate->aggregatedupto); @@ -996,6 +1079,16 @@ next_tuple: peraggstate, result, isnull); + /* + * RPR is enabled and we just return NULL. because skip mode is SKIP + * TO PAST LAST ROW and current row is skipped row or unmatched row. + */ + if (agg_result_isnull) + { + *isnull = true; + *result = (Datum) 0; + } + /* * save the result in case next row shares the same frame. * @@ -1090,6 +1183,7 @@ begin_partition(WindowAggState *winstate) winstate->framehead_valid = false; winstate->frametail_valid = false; winstate->grouptail_valid = false; + create_reduced_frame_map(winstate); winstate->spooled_rows = 0; winstate->currentpos = 0; winstate->frameheadpos = 0; @@ -2053,6 +2147,8 @@ ExecWindowAgg(PlanState *pstate) CHECK_FOR_INTERRUPTS(); + elog(DEBUG1, "ExecWindowAgg called. pos: " INT64_FORMAT , winstate->currentpos); + if (winstate->status == WINDOWAGG_DONE) return NULL; @@ -2221,6 +2317,17 @@ ExecWindowAgg(PlanState *pstate) /* don't evaluate the window functions when we're in pass-through mode */ if (winstate->status == WINDOWAGG_RUN) { + /* + * If RPR is defined and skip mode is next row, we need to clear existing + * reduced frame info so that we newly calculate the info starting from + * current row. + */ + if (rpr_is_defined(winstate)) + { + if (winstate->rpSkipTo == ST_NEXT_ROW) + clear_reduced_frame_map(winstate); + } + /* * Evaluate true window functions */ @@ -2388,6 +2495,9 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) TupleDesc scanDesc; ListCell *l; + TargetEntry *te; + Expr *expr; + /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -2483,6 +2593,16 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc, &TTSOpsMinimalTuple); + winstate->prev_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); + + winstate->next_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); + + winstate->null_slot = ExecInitExtraTupleSlot(estate, scanDesc, + &TTSOpsMinimalTuple); + winstate->null_slot = ExecStoreAllNullTuple(winstate->null_slot); + /* * create frame head and tail slots only if needed (must create slots in * exactly the same cases that update_frameheadpos and update_frametailpos @@ -2667,6 +2787,39 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) winstate->inRangeAsc = node->inRangeAsc; winstate->inRangeNullsFirst = node->inRangeNullsFirst; + /* Set up SKIP TO type */ + winstate->rpSkipTo = node->rpSkipTo; + /* Set up row pattern recognition PATTERN clause */ + winstate->patternVariableList = node->patternVariable; + winstate->patternRegexpList = node->patternRegexp; + + /* Set up row pattern recognition DEFINE clause */ + winstate->defineInitial = node->defineInitial; + winstate->defineVariableList = NIL; + winstate->defineClauseList = NIL; + if (node->defineClause != NIL) + { + /* + * Tweak arg var of PREV/NEXT so that it refers to scan/inner slot. + */ + foreach(l, node->defineClause) + { + char *name; + ExprState *exps; + + te = lfirst(l); + name = te->resname; + expr = te->expr; + + elog(DEBUG1, "defineVariable name: %s", name); + winstate->defineVariableList = lappend(winstate->defineVariableList, + makeString(pstrdup(name))); + attno_map((Node *)expr); + exps = ExecInitExpr(expr, (PlanState *) winstate); + winstate->defineClauseList = lappend(winstate->defineClauseList, exps); + } + } + winstate->all_first = true; winstate->partition_spooled = false; winstate->more_partitions = false; @@ -2674,6 +2827,57 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) return winstate; } +/* + * Rewrite varno of Var node that is the argument of PREV/NET so that it sees + * scan tuple (PREV) or inner tuple (NEXT). + */ +static void +attno_map(Node *node) +{ + (void) expression_tree_walker(node, attno_map_walker, NULL); +} + +static bool +attno_map_walker(Node *node, void *context) +{ + FuncExpr *func; + int nargs; + Expr *expr; + Var *var; + + if (node == NULL) + return false; + + if (IsA(node, FuncExpr)) + { + func = (FuncExpr *)node; + + if (func->funcid == F_PREV || func->funcid == F_NEXT) + { + /* sanity check */ + nargs = list_length(func->args); + if (list_length(func->args) != 1) + elog(ERROR, "PREV/NEXT must have 1 argument but function %d has %d args", func->funcid, nargs); + + expr = (Expr *) lfirst(list_head(func->args)); + if (!IsA(expr, Var)) + elog(ERROR, "PREV/NEXT's arg is not Var"); /* XXX: is it possible that arg type is Const? */ + var = (Var *)expr; + + if (func->funcid == F_PREV) + /* + * Rewrite varno from OUTER_VAR to regular var no so that the + * var references scan tuple. + */ + var->varno = var->varnosyn; + else + var->varno = INNER_VAR; + elog(DEBUG1, "PREV/NEXT's varno is rewritten to: %d", var->varno); + } + } + return expression_tree_walker(node, attno_map_walker, NULL); +} + /* ----------------- * ExecEndWindowAgg * ----------------- @@ -2691,6 +2895,8 @@ ExecEndWindowAgg(WindowAggState *node) ExecClearTuple(node->agg_row_slot); ExecClearTuple(node->temp_slot_1); ExecClearTuple(node->temp_slot_2); + ExecClearTuple(node->prev_slot); + ExecClearTuple(node->next_slot); if (node->framehead_slot) ExecClearTuple(node->framehead_slot); if (node->frametail_slot) @@ -2740,6 +2946,8 @@ ExecReScanWindowAgg(WindowAggState *node) ExecClearTuple(node->agg_row_slot); ExecClearTuple(node->temp_slot_1); ExecClearTuple(node->temp_slot_2); + ExecClearTuple(node->prev_slot); + ExecClearTuple(node->next_slot); if (node->framehead_slot) ExecClearTuple(node->framehead_slot); if (node->frametail_slot) @@ -3100,7 +3308,7 @@ window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot) return false; if (pos < winobj->markpos) - elog(ERROR, "cannot fetch row before WindowObject's mark position"); + elog(ERROR, "cannot fetch row: " INT64_FORMAT " before WindowObject's mark position: " INT64_FORMAT, pos, winobj->markpos ); oldcontext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory); @@ -3420,14 +3628,54 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno, WindowAggState *winstate; ExprContext *econtext; TupleTableSlot *slot; - int64 abs_pos; - int64 mark_pos; Assert(WindowObjectIsValid(winobj)); winstate = winobj->winstate; econtext = winstate->ss.ps.ps_ExprContext; slot = winstate->temp_slot_1; + if (WinGetSlotInFrame(winobj, slot, + relpos, seektype, set_mark, + isnull, isout) == 0) + { + econtext->ecxt_outertuple = slot; + return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno), + econtext, isnull); + } + + if (isout) + *isout = true; + *isnull = true; + return (Datum) 0; +} + +/* + * WinGetSlotInFrame + * slot: TupleTableSlot to store the result + * relpos: signed rowcount offset from the seek position + * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL + * set_mark: If the row is found/in frame and set_mark is true, the mark is + * moved to the row as a side-effect. + * isnull: output argument, receives isnull status of result + * isout: output argument, set to indicate whether target row position + * is out of frame (can pass NULL if caller doesn't care about this) + * + * Returns 0 if we successfullt got the slot. false if out of frame. + * (also isout is set) + */ +static int +WinGetSlotInFrame(WindowObject winobj, TupleTableSlot *slot, + int relpos, int seektype, bool set_mark, + bool *isnull, bool *isout) +{ + WindowAggState *winstate; + int64 abs_pos; + int64 mark_pos; + int num_reduced_frame; + + Assert(WindowObjectIsValid(winobj)); + winstate = winobj->winstate; + switch (seektype) { case WINDOW_SEEK_CURRENT: @@ -3494,11 +3742,21 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno, winstate->frameOptions); break; } + num_reduced_frame = row_is_in_reduced_frame(winobj, winstate->frameheadpos); + if (num_reduced_frame < 0) + goto out_of_frame; + else if (num_reduced_frame > 0) + if (relpos >= num_reduced_frame) + goto out_of_frame; break; case WINDOW_SEEK_TAIL: /* rejecting relpos > 0 is easy and simplifies code below */ if (relpos > 0) goto out_of_frame; + + /* RPR cares about frame head pos. Need to call update_frameheadpos */ + update_frameheadpos(winstate); + update_frametailpos(winstate); abs_pos = winstate->frametailpos - 1 + relpos; @@ -3565,6 +3823,12 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno, mark_pos = 0; /* keep compiler quiet */ break; } + + num_reduced_frame = row_is_in_reduced_frame(winobj, winstate->frameheadpos + relpos); + if (num_reduced_frame < 0) + goto out_of_frame; + else if (num_reduced_frame > 0) + abs_pos = winstate->frameheadpos + relpos + num_reduced_frame - 1; break; default: elog(ERROR, "unrecognized window seek type: %d", seektype); @@ -3583,15 +3847,13 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno, *isout = false; if (set_mark) WinSetMarkPosition(winobj, mark_pos); - econtext->ecxt_outertuple = slot; - return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno), - econtext, isnull); + return 0; out_of_frame: if (isout) *isout = true; *isnull = true; - return (Datum) 0; + return -1; } /* @@ -3622,3 +3884,561 @@ WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull) return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno), econtext, isnull); } + +/* + * rpr_is_defined + * return true if Row pattern recognition is defined. + */ +static +bool rpr_is_defined(WindowAggState *winstate) +{ + return winstate->patternVariableList != NIL; +} + +/* + * row_is_in_reduced_frame + * Determine whether a row is in the current row's reduced window frame according + * to row pattern matching + * + * The row must has been already determined that it is in a full window frame + * and fetched it into slot. + * + * Returns: + * = 0, RPR is not defined. + * >0, if the row is the first in the reduced frame. Return the number of rows in the reduced frame. + * -1, if the row is unmatched row + * -2, if the row is in the reduced frame but needed to be skipped because of + * AFTER MATCH SKIP PAST LAST ROW + */ +static +int row_is_in_reduced_frame(WindowObject winobj, int64 pos) +{ + WindowAggState *winstate = winobj->winstate; + int state; + int rtn; + + if (!rpr_is_defined(winstate)) + { + /* + * RPR is not defined. Assume that we are always in the the reduced + * window frame. + */ + rtn = 0; + elog(DEBUG1, "row_is_in_reduced_frame returns %d: pos: " INT64_FORMAT, rtn, pos); + return rtn; + } + + state = get_reduced_frame_map(winstate, pos); + + if (state == RF_NOT_DETERMINED) + { + update_frameheadpos(winstate); + update_reduced_frame(winobj, pos); + } + + state = get_reduced_frame_map(winstate, pos); + + switch (state) + { + int64 i; + int num_reduced_rows; + + case RF_FRAME_HEAD: + num_reduced_rows = 1; + for (i = pos + 1; get_reduced_frame_map(winstate,i) == RF_SKIPPED; i++) + num_reduced_rows++; + rtn = num_reduced_rows; + break; + + case RF_SKIPPED: + rtn = -2; + break; + + case RF_UNMATCHED: + rtn = -1; + break; + + default: + elog(ERROR, "Unrecognized state: %d at: " INT64_FORMAT, state, pos); + break; + } + + elog(DEBUG1, "row_is_in_reduced_frame returns %d: pos: " INT64_FORMAT, rtn, pos); + return rtn; +} + +#define REDUCED_FRAME_MAP_INIT_SIZE 1024L + +/* + * Create reduced frame map + */ +static +void create_reduced_frame_map(WindowAggState *winstate) +{ + winstate->reduced_frame_map = + MemoryContextAlloc(winstate->partcontext, REDUCED_FRAME_MAP_INIT_SIZE); + winstate->alloc_sz = REDUCED_FRAME_MAP_INIT_SIZE; + clear_reduced_frame_map(winstate); +} + +/* + * Clear reduced frame map + */ +static +void clear_reduced_frame_map(WindowAggState *winstate) +{ + Assert(winstate->reduced_frame_map != NULL); + MemSet(winstate->reduced_frame_map, RF_NOT_DETERMINED, + winstate->alloc_sz); +} + +/* + * Get reduced frame map specified by pos + */ +static +int get_reduced_frame_map(WindowAggState *winstate, int64 pos) +{ + Assert(winstate->reduced_frame_map != NULL); + + if (pos < 0 || pos >= winstate->alloc_sz) + elog(ERROR, "wrong pos: " INT64_FORMAT, pos); + + return winstate->reduced_frame_map[pos]; +} + +/* + * Add/replace reduced frame map member at pos. + * If there's no enough space, expand the map. + */ +static +void register_reduced_frame_map(WindowAggState *winstate, int64 pos, int val) +{ + int64 realloc_sz; + + Assert(winstate->reduced_frame_map != NULL); + + if (pos < 0) + elog(ERROR, "wrong pos: " INT64_FORMAT, pos); + + if (pos > winstate->alloc_sz - 1) + { + realloc_sz = winstate->alloc_sz * 2; + + winstate->reduced_frame_map = + repalloc(winstate->reduced_frame_map, realloc_sz); + + MemSet(winstate->reduced_frame_map + winstate->alloc_sz, + RF_NOT_DETERMINED, realloc_sz - winstate->alloc_sz); + + winstate->alloc_sz = realloc_sz; + } + + winstate->reduced_frame_map[pos] = val; +} + +/* + * update_reduced_frame + * Update reduced frame info. + */ +static +void update_reduced_frame(WindowObject winobj, int64 pos) +{ + WindowAggState *winstate = winobj->winstate; + ListCell *lc1, *lc2; + bool expression_result; + int num_matched_rows; + int64 original_pos; + bool anymatch; + StringInfo encoded_str; + StringInfo pattern_str = makeStringInfo(); + + /* + * Array of pattern variables evaluted to true. + * Each character corresponds to pattern variable. + * Example: + * str_set[0] = "AB"; + * str_set[1] = "AC"; + * In this case at row 0 A and B are true, and A and C are true in row 1. + */ + #define ENCODED_STR_ARRAY_ALLOC_SIZE 128 + StringInfo *str_set = NULL; + int str_set_index; + int str_set_size; + + /* save original pos */ + original_pos = pos; + + /* + * Loop over until none of pattern matches or encounters end of frame. + */ + for (;;) + { + int64 result_pos = -1; + + /* + * Loop over each PATTERN variable. + */ + anymatch = false; + encoded_str = makeStringInfo(); + + forboth(lc1, winstate->patternVariableList, lc2, winstate->patternRegexpList) + { + char *vname = strVal(lfirst(lc1)); + char *quantifier = strVal(lfirst(lc2)); + + elog(DEBUG1, "pos: " INT64_FORMAT " pattern vname: %s quantifier: %s", pos, vname, quantifier); + + expression_result = false; + + /* evaluate row pattern against current row */ + result_pos = evaluate_pattern(winobj, pos, vname, encoded_str, &expression_result); + if (expression_result) + { + elog(DEBUG1, "expression result is true"); + anymatch = true; + } + + /* + * If out of frame, we are done. + */ + if (result_pos < 0) + break; + } + + if (!anymatch) + { + /* none of patterns matched. */ + break; + } + + /* build encoded string array */ + if (str_set == NULL) + { + str_set_index = 0; + str_set_size = ENCODED_STR_ARRAY_ALLOC_SIZE * sizeof(StringInfo); + str_set = palloc(str_set_size); + } + + str_set[str_set_index++] = encoded_str; + + elog(DEBUG1, "pos: " INT64_FORMAT " str_set_index: %d encoded_str: %s", pos, str_set_index, encoded_str->data); + + if (str_set_index >= str_set_size) + { + str_set_size *= 2; + str_set = repalloc(str_set, str_set_size); + } + + /* move to next row */ + pos++; + + if (result_pos < 0) + { + /* out of frame */ + break; + } + } + + if (str_set == NULL) + { + /* no match found in the first row */ + register_reduced_frame_map(winstate, original_pos, RF_UNMATCHED); + return; + } + + elog(DEBUG2, "pos: " INT64_FORMAT " encoded_str: %s", pos, encoded_str->data); + + /* build regular expression */ + pattern_str = makeStringInfo(); + appendStringInfoChar(pattern_str, '^'); + forboth (lc1, winstate->patternVariableList, lc2, winstate->patternRegexpList) + { + char *vname = strVal(lfirst(lc1)); + char *quantifier = strVal(lfirst(lc2)); + char initial; + + initial = pattern_initial(winstate, vname); + Assert(initial != 0); + appendStringInfoChar(pattern_str, initial); + if (quantifier[0]) + appendStringInfoChar(pattern_str, quantifier[0]); + elog(DEBUG1, "vname: %s initial: %c quantifier: %s", vname, initial, quantifier); + } + + elog(DEBUG2, "pos: " INT64_FORMAT " pattern: %s", pos, pattern_str->data); + + /* look for matching pattern variable sequence */ + num_matched_rows = search_str_set(pattern_str->data, str_set, str_set_index); + /* + * We are at the first row in the reduced frame. Save the number of + * matched rows as the number of rows in the reduced frame. + */ + if (num_matched_rows <= 0) + { + /* no match */ + register_reduced_frame_map(winstate, original_pos, RF_UNMATCHED); + } + else + { + int64 i; + + register_reduced_frame_map(winstate, original_pos, RF_FRAME_HEAD); + + for (i = original_pos + 1; i < original_pos + num_matched_rows; i++) + { + register_reduced_frame_map(winstate, i, RF_SKIPPED); + } + } + + return; +} + +/* + * search set of encode_str. + * set_size: size of set_str array. + */ +static +int search_str_set(char *pattern, StringInfo *str_set, int set_size) +{ + char *encoded_str = palloc0(set_size+1); + int resultlen = 0; + + search_str_set_recurse(pattern, str_set, set_size, 0, encoded_str, &resultlen); + elog(DEBUG1, "search_str_set returns %d", resultlen); + return resultlen; +} + +/* + * Workhorse of search_str_set. + */ +static +void search_str_set_recurse(char *pattern, StringInfo *str_set, + int set_size, int set_index, char *encoded_str, int *resultlen) +{ + char *p; + + if (set_index >= set_size) + { + Datum d; + text *res; + char *substr; + + /* + * We first perform pattern matching using regexp_instr, then call + * textregexsubstr to get matched substring to know how log the + * matched string is. That is the number of rows in the reduced window + * frame. The reason why we can't call textregexsubstr is, it error + * out if pattern is not match. + */ + if (DatumGetInt32(DirectFunctionCall2Coll(regexp_instr, DEFAULT_COLLATION_OID, + PointerGetDatum(cstring_to_text(encoded_str)), + PointerGetDatum(cstring_to_text(pattern)))) > 0) + { + d = DirectFunctionCall2Coll(textregexsubstr, + DEFAULT_COLLATION_OID, + PointerGetDatum(cstring_to_text(encoded_str)), + PointerGetDatum(cstring_to_text(pattern))); + if (d != 0) + { + int len; + + res = DatumGetTextPP(d); + substr = text_to_cstring(res); + len = strlen(substr); + if (len > *resultlen) + /* remember the longest match */ + *resultlen = len; + } + } + return; + } + + p = str_set[set_index]->data; + while (*p) + { + encoded_str[set_index] = *p; + p++; + search_str_set_recurse(pattern, str_set, set_size, set_index + 1, encoded_str, resultlen); + } +} + + +/* + * Evaluate expression associated with PATTERN variable vname. + * relpos is relative row position in a frame (starting from 0). + * "quantifier" is the quatifier part of the PATTERN regular expression. + * Currently only '+' is allowed. + * result is out paramater representing the expression evaluation result + * is true of false. + * Return values are: + * >=0: the last match absolute row position + * other wise out of frame. + */ +static +int64 evaluate_pattern(WindowObject winobj, int64 current_pos, + char *vname, StringInfo encoded_str, bool *result) +{ + WindowAggState *winstate = winobj->winstate; + ExprContext *econtext = winstate->ss.ps.ps_ExprContext; + ListCell *lc1, *lc2, *lc3; + ExprState *pat; + Datum eval_result; + bool out_of_frame = false; + bool isnull; + + forthree (lc1, winstate->defineVariableList, lc2, winstate->defineClauseList, lc3, winstate->defineInitial) + { + char initial; + char *name = strVal(lfirst(lc1)); + + if (strcmp(vname, name)) + continue; + + initial = *(strVal(lfirst(lc3))); + + /* set expression to evaluate */ + pat = lfirst(lc2); + + /* get current, previous and next tuples */ + if (!get_slots(winobj, current_pos)) + { + out_of_frame = true; + } + else + { + /* evaluate the expression */ + eval_result = ExecEvalExpr(pat, econtext, &isnull); + if (isnull) + { + /* expression is NULL */ + elog(DEBUG1, "expression for %s is NULL at row: " INT64_FORMAT, vname, current_pos); + *result = false; + } + else + { + if (!DatumGetBool(eval_result)) + { + /* expression is false */ + elog(DEBUG1, "expression for %s is false at row: " INT64_FORMAT, vname, current_pos); + *result = false; + } + else + { + /* expression is true */ + elog(DEBUG1, "expression for %s is true at row: " INT64_FORMAT, vname, current_pos); + appendStringInfoChar(encoded_str, initial); + *result = true; + } + } + break; + } + + if (out_of_frame) + { + *result = false; + return -1; + } + } + return current_pos; +} + +/* + * Get current, previous and next tuples. + * Returns false if current row is out of partition/full frame. + */ +static +bool get_slots(WindowObject winobj, int64 current_pos) +{ + WindowAggState *winstate = winobj->winstate; + TupleTableSlot *slot; + int ret; + ExprContext *econtext; + + econtext = winstate->ss.ps.ps_ExprContext; + + /* set up current row tuple slot */ + slot = winstate->temp_slot_1; + if (!window_gettupleslot(winobj, current_pos, slot)) + { + elog(DEBUG1, "current row is out of partition at:" INT64_FORMAT, current_pos); + return false; + + ret = row_is_in_frame(winstate, current_pos, slot); + if (ret <= 0) + { + elog(DEBUG1, "current row is out of frame at: " INT64_FORMAT, current_pos); + return false; + } + } + econtext->ecxt_outertuple = slot; + + /* for PREV */ + if (current_pos > 0) + { + slot = winstate->prev_slot; + if (!window_gettupleslot(winobj, current_pos - 1, slot)) + { + elog(DEBUG1, "previous row is out of partition at: " INT64_FORMAT, current_pos - 1); + econtext->ecxt_scantuple = winstate->null_slot; + } + else + { + ret = row_is_in_frame(winstate, current_pos - 1, slot); + if (ret <= 0) + { + elog(DEBUG1, "previous row is out of frame at: " INT64_FORMAT, current_pos - 1); + econtext->ecxt_scantuple = winstate->null_slot; + } + else + { + econtext->ecxt_scantuple = slot; + } + } + } + else + econtext->ecxt_scantuple = winstate->null_slot; + + /* for NEXT */ + slot = winstate->next_slot; + if (!window_gettupleslot(winobj, current_pos + 1, slot)) + { + elog(DEBUG1, "next row is out of partiton at: " INT64_FORMAT, current_pos + 1); + econtext->ecxt_innertuple = winstate->null_slot; + } + else + { + ret = row_is_in_frame(winstate, current_pos + 1, slot); + if (ret <= 0) + { + elog(DEBUG1, "next row is out of frame at: " INT64_FORMAT, current_pos + 1); + econtext->ecxt_innertuple = winstate->null_slot; + } + else + econtext->ecxt_innertuple = slot; + } + return true; +} + +/* + * Return pattern variable initial character + * matching with pattern variable name vname. + * If not found, return 0. + */ +static +char pattern_initial(WindowAggState *winstate, char *vname) +{ + char initial; + char *name; + ListCell *lc1, *lc2; + + forboth (lc1, winstate->defineVariableList, lc2, winstate->defineInitial) + { + name = strVal(lfirst(lc1)); /* DEFINE variable name */ + initial = *(strVal(lfirst(lc2))); /* DEFINE variable initial */ + + + if (!strcmp(name, vname)) + return initial; /* found */ + } + return 0; +} diff --git a/src/backend/utils/adt/windowfuncs.c b/src/backend/utils/adt/windowfuncs.c index b87a624fb2..9ebcc7b5d2 100644 --- a/src/backend/utils/adt/windowfuncs.c +++ b/src/backend/utils/adt/windowfuncs.c @@ -13,6 +13,9 @@ */ #include "postgres.h" +#include "catalog/pg_collation_d.h" +#include "executor/executor.h" +#include "nodes/execnodes.h" #include "nodes/supportnodes.h" #include "utils/builtins.h" #include "windowapi.h" @@ -36,11 +39,19 @@ typedef struct int64 remainder; /* (total rows) % (bucket num) */ } ntile_context; +/* + * rpr process information. + * Used for AFTER MATCH SKIP PAST LAST ROW + */ +typedef struct SkipContext +{ + int64 pos; /* last row absolute position */ +} SkipContext; + static bool rank_up(WindowObject winobj); static Datum leadlag_common(FunctionCallInfo fcinfo, bool forward, bool withoffset, bool withdefault); - /* * utility routine for *_rank functions. */ @@ -673,7 +684,7 @@ window_last_value(PG_FUNCTION_ARGS) bool isnull; result = WinGetFuncArgInFrame(winobj, 0, - 0, WINDOW_SEEK_TAIL, true, + 0, WINDOW_SEEK_TAIL, false, &isnull, NULL); if (isnull) PG_RETURN_NULL(); @@ -713,3 +724,25 @@ window_nth_value(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } + +/* + * prev + * Dummy function to invoke RPR's navigation operator "PREV". + * This is *not* a window function. + */ +Datum +window_prev(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); +} + +/* + * next + * Dummy function to invoke RPR's navigation operation "NEXT". + * This is *not* a window function. + */ +Datum +window_next(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 9805bc6118..d20f803cf5 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -10416,6 +10416,12 @@ { oid => '3114', descr => 'fetch the Nth row value', proname => 'nth_value', prokind => 'w', prorettype => 'anyelement', proargtypes => 'anyelement int4', prosrc => 'window_nth_value' }, +{ oid => '6122', descr => 'previous value', + proname => 'prev', provolatile => 's', prorettype => 'anyelement', + proargtypes => 'anyelement', prosrc => 'window_prev' }, +{ oid => '6123', descr => 'next value', + proname => 'next', provolatile => 's', prorettype => 'anyelement', + proargtypes => 'anyelement', prosrc => 'window_next' }, # functions for range types { oid => '3832', descr => 'I/O', diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index cb714f4a19..63feb68f60 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2471,6 +2471,11 @@ typedef enum WindowAggStatus * tuples during spool */ } WindowAggStatus; +#define RF_NOT_DETERMINED 0 +#define RF_FRAME_HEAD 1 +#define RF_SKIPPED 2 +#define RF_UNMATCHED 3 + typedef struct WindowAggState { ScanState ss; /* its first field is NodeTag */ @@ -2519,6 +2524,15 @@ typedef struct WindowAggState int64 groupheadpos; /* current row's peer group head position */ int64 grouptailpos; /* " " " " tail position (group end+1) */ + /* these fields are used in Row pattern recognition: */ + RPSkipTo rpSkipTo; /* Row Pattern Skip To type */ + List *patternVariableList; /* list of row pattern variables names (list of String) */ + List *patternRegexpList; /* list of row pattern regular expressions ('+' or ''. list of String) */ + List *defineVariableList; /* list of row pattern definition variables (list of String) */ + List *defineClauseList; /* expression for row pattern definition + * search conditions ExprState list */ + List *defineInitial; /* list of row pattern definition variable initials (list of String) */ + MemoryContext partcontext; /* context for partition-lifespan data */ MemoryContext aggcontext; /* shared context for aggregate working data */ MemoryContext curaggcontext; /* current aggregate's working data */ @@ -2555,6 +2569,18 @@ typedef struct WindowAggState TupleTableSlot *agg_row_slot; TupleTableSlot *temp_slot_1; TupleTableSlot *temp_slot_2; + + /* temporary slots for RPR */ + TupleTableSlot *prev_slot; /* PREV row navigation operator */ + TupleTableSlot *next_slot; /* NEXT row navigation operator */ + TupleTableSlot *null_slot; /* all NULL slot */ + + /* + * Each byte corresponds to a row positioned at absolute its pos in + * partition. See above definition for RF_* + */ + char *reduced_frame_map; + int64 alloc_sz; /* size of the map */ } WindowAggState; /* ---------------- -- 2.25.1 ----Next_Part(Tue_Sep_12_15_18_43_2023_359)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v6-0005-Row-pattern-recognition-patch-docs.patch" ^ permalink raw reply [nested|flat] 111+ messages in thread
* [PATCH] Fix tests under wal_level=minimal @ 2026-04-07 11:16 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 111+ messages in thread From: Álvaro Herrera @ 2026-04-07 11:16 UTC (permalink / raw) Buildfarm members which have specifically configured to use wal_level=minimal fail the repack regression tests, which require wal_level=replica. Add a temp config file to fix that. --- src/test/modules/injection_points/Makefile | 3 +++ src/test/modules/injection_points/meson.build | 4 ++++ src/test/modules/injection_points/wal_level.conf | 1 + 3 files changed, 8 insertions(+) create mode 100644 src/test/modules/injection_points/wal_level.conf diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index 2cd7d87c533..2c7abe93632 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -19,6 +19,9 @@ ISOLATION = basic \ syscache-update-pruned \ heap_lock_update +# some isolation tests require wal_level=replica +ISOLATION_OPTS = --temp-config $(top_srcdir)/src/test/modules/injection_points/wal_level.conf + # The injection points are cluster-wide, so disable installcheck NO_INSTALLCHECK = 1 diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build index a414abb924b..7a838259685 100644 --- a/src/test/modules/injection_points/meson.build +++ b/src/test/modules/injection_points/meson.build @@ -53,5 +53,9 @@ tests += { 'runningcheck': false, # see syscache-update-pruned # Some tests wait for all snapshots, so avoid parallel execution 'runningcheck-parallel': false, + # some tests require wal_level=replica + 'regress_args': [ + '--temp-config', files('wal_level.conf'), + ], }, } diff --git a/src/test/modules/injection_points/wal_level.conf b/src/test/modules/injection_points/wal_level.conf new file mode 100644 index 00000000000..010abb193a8 --- /dev/null +++ b/src/test/modules/injection_points/wal_level.conf @@ -0,0 +1 @@ +wal_level=replica -- 2.47.3 --j4ov4p7gfxgiujzz-- ^ permalink raw reply [nested|flat] 111+ messages in thread
end of thread, other threads:[~2026-04-07 11:16 UTC | newest] Thread overview: 111+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/8] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2016-07-12 08:20 [PATCH 2/9] psql and pg_dump support for partitioned tables. amit <[email protected]> 2023-09-12 05:22 [PATCH v6 4/7] Row pattern recognition patch (executor). Tatsuo Ishii <[email protected]> 2026-04-07 11:16 [PATCH] Fix tests under wal_level=minimal Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox