diff options
| author | Alexander Korotkov | 2020-12-20 04:20:33 +0000 |
|---|---|---|
| committer | Alexander Korotkov | 2020-12-20 04:20:33 +0000 |
| commit | 6df7a9698bb036610c1e8c6d375e1be38cb26d5f (patch) | |
| tree | dc5985310422e2b41e06bc1770f9e384e0bd6525 /src/bin | |
| parent | 08b01d4dd982b491a2f9641804b368185b8f4c53 (diff) | |
Multirange datatypes
Multiranges are basically sorted arrays of non-overlapping ranges with
set-theoretic operations defined over them.
Since v14, each range type automatically gets a corresponding multirange
datatype. There are both manual and automatic mechanisms for naming multirange
types. Once can specify multirange type name using multirange_type_name
attribute in CREATE TYPE. Otherwise, a multirange type name is generated
automatically. If the range type name contains "range" then we change that to
"multirange". Otherwise, we add "_multirange" to the end.
Implementation of multiranges comes with a space-efficient internal
representation format, which evades extra paddings and duplicated storage of
oids. Altogether this format allows fetching a particular range by its index
in O(n).
Statistic gathering and selectivity estimation are implemented for multiranges.
For this purpose, stored multirange is approximated as union range without gaps.
This field will likely need improvements in the future.
Catversion is bumped.
Discussion: https://postgr.es/m/CALNJ-vSUpQ_Y%3DjXvTxt1VYFztaBSsWVXeF1y6gTYQ4bOiWDLgQ%40mail.gmail.com
Discussion: https://postgr.es/m/a0b8026459d1e6167933be2104a6174e7d40d0ab.camel%40j-davis.com#fe7218c83b08068bfffb0c5293eceda0
Author: Paul Jungwirth, revised by me
Reviewed-by: David Fetter, Corey Huinker, Jeff Davis, Pavel Stehule
Reviewed-by: Alvaro Herrera, Tom Lane, Isaac Morland, David G. Johnston
Reviewed-by: Zhihong Yu, Alexander Korotkov
Diffstat (limited to 'src/bin')
| -rw-r--r-- | src/bin/pg_dump/pg_dump.c | 166 | ||||
| -rw-r--r-- | src/bin/pg_dump/pg_dump.h | 1 | ||||
| -rw-r--r-- | src/bin/pg_dump/t/002_pg_dump.pl | 2 |
3 files changed, 128 insertions, 41 deletions
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 673a6703475..03023a382c0 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -272,7 +272,8 @@ static void dumpSearchPath(Archive *AH); static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_type_oid, - bool force_array_type); + bool force_array_type, + bool include_multirange_type); static void binary_upgrade_set_type_oids_by_rel_oid(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_rel_oid); static void binary_upgrade_set_pg_class_oids(Archive *fout, @@ -1653,7 +1654,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout) } /* skip auto-generated array types */ - if (tyinfo->isArray) + if (tyinfo->isArray || tyinfo->isMultirange) { tyinfo->dobj.objType = DO_DUMMY_TYPE; @@ -4461,16 +4462,49 @@ append_depends_on_extension(Archive *fout, } } +static Oid +get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query) +{ + /* + * If the old version didn't assign an array type, but the new version + * does, we must select an unused type OID to assign. This currently only + * happens for domains, when upgrading pre-v11 to v11 and up. + * + * Note: local state here is kind of ugly, but we must have some, since we + * mustn't choose the same unused OID more than once. + */ + static Oid next_possible_free_oid = FirstNormalObjectId; + PGresult *res; + bool is_dup; + + do + { + ++next_possible_free_oid; + printfPQExpBuffer(upgrade_query, + "SELECT EXISTS(SELECT 1 " + "FROM pg_catalog.pg_type " + "WHERE oid = '%u'::pg_catalog.oid);", + next_possible_free_oid); + res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); + is_dup = (PQgetvalue(res, 0, 0)[0] == 't'); + PQclear(res); + } while (is_dup); + + return next_possible_free_oid; +} static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_type_oid, - bool force_array_type) + bool force_array_type, + bool include_multirange_type) { PQExpBuffer upgrade_query = createPQExpBuffer(); PGresult *res; Oid pg_type_array_oid; + Oid pg_type_multirange_oid; + Oid pg_type_multirange_array_oid; appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n"); appendPQExpBuffer(upgrade_buffer, @@ -4491,33 +4525,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout, PQclear(res); if (!OidIsValid(pg_type_array_oid) && force_array_type) - { - /* - * If the old version didn't assign an array type, but the new version - * does, we must select an unused type OID to assign. This currently - * only happens for domains, when upgrading pre-v11 to v11 and up. - * - * Note: local state here is kind of ugly, but we must have some, - * since we mustn't choose the same unused OID more than once. - */ - static Oid next_possible_free_oid = FirstNormalObjectId; - bool is_dup; - - do - { - ++next_possible_free_oid; - printfPQExpBuffer(upgrade_query, - "SELECT EXISTS(SELECT 1 " - "FROM pg_catalog.pg_type " - "WHERE oid = '%u'::pg_catalog.oid);", - next_possible_free_oid); - res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); - is_dup = (PQgetvalue(res, 0, 0)[0] == 't'); - PQclear(res); - } while (is_dup); - - pg_type_array_oid = next_possible_free_oid; - } + pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query); if (OidIsValid(pg_type_array_oid)) { @@ -4528,6 +4536,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout, pg_type_array_oid); } + /* + * Pre-set the multirange type oid and its own array type oid. + */ + if (include_multirange_type) + { + if (fout->remoteVersion >= 130000) + { + appendPQExpBuffer(upgrade_query, + "SELECT t.oid, t.typarray " + "FROM pg_catalog.pg_type t " + "JOIN pg_catalog.pg_range r " + "ON t.oid = r.rngmultitypid " + "WHERE r.rngtypid = '%u'::pg_catalog.oid;", + pg_type_oid); + + res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); + + pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid"))); + pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray"))); + + PQclear(res); + } + else + { + pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query); + pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query); + } + + appendPQExpBufferStr(upgrade_buffer, + "\n-- For binary upgrade, must preserve multirange pg_type oid\n"); + appendPQExpBuffer(upgrade_buffer, + "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n", + pg_type_multirange_oid); + appendPQExpBufferStr(upgrade_buffer, + "\n-- For binary upgrade, must preserve multirange pg_type array oid\n"); + appendPQExpBuffer(upgrade_buffer, + "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n", + pg_type_multirange_array_oid); + } + destroyPQExpBuffer(upgrade_query); } @@ -4552,7 +4600,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout, if (OidIsValid(pg_type_oid)) binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer, - pg_type_oid, false); + pg_type_oid, false, false); PQclear(upgrade_res); destroyPQExpBuffer(upgrade_query); @@ -5097,6 +5145,11 @@ getTypes(Archive *fout, int *numTypes) else tyinfo[i].isArray = false; + if (tyinfo[i].typtype == 'm') + tyinfo[i].isMultirange = true; + else + tyinfo[i].isMultirange = false; + /* Decide whether we want to dump it */ selectDumpableType(&tyinfo[i], fout); @@ -8326,9 +8379,12 @@ getProcLangs(Archive *fout, int *numProcLangs) /* * getCasts - * get basic information about every cast in the system + * get basic information about most casts in the system * * numCasts is set to the number of casts read in + * + * Skip casts from a range to its multirange, since we'll create those + * automatically. */ CastInfo * getCasts(Archive *fout, int *numCasts) @@ -8346,7 +8402,20 @@ getCasts(Archive *fout, int *numCasts) int i_castcontext; int i_castmethod; - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 130000) + { + appendPQExpBufferStr(query, "SELECT tableoid, oid, " + "castsource, casttarget, castfunc, castcontext, " + "castmethod " + "FROM pg_cast c " + "WHERE NOT EXISTS ( " + "SELECT 1 FROM pg_range r " + "WHERE c.castsource = r.rngtypid " + "AND c.casttarget = r.rngmultitypid " + ") " + "ORDER BY 3,4"); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBufferStr(query, "SELECT tableoid, oid, " "castsource, casttarget, castfunc, castcontext, " @@ -10496,7 +10565,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) if (dopt->binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid, - false); + false, false); appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (", qualtypname); @@ -10594,7 +10663,17 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) char *procname; appendPQExpBuffer(query, - "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " + "SELECT "); + + if (fout->remoteVersion >= 140000) + appendPQExpBuffer(query, + "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, "); + else + appendPQExpBuffer(query, + "NULL AS rngmultitype, "); + + appendPQExpBuffer(query, + "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " "opc.opcname AS opcname, " "(SELECT nspname FROM pg_catalog.pg_namespace nsp " " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " @@ -10622,7 +10701,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) if (dopt->binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid, - false); + false, true); appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (", qualtypname); @@ -10630,6 +10709,10 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(q, "\n subtype = %s", PQgetvalue(res, 0, PQfnumber(res, "rngsubtype"))); + if (PQgetvalue(res, 0, PQfnumber(res, "rngmultitype"))) + appendPQExpBuffer(q, ",\n multirange_type_name = %s", + PQgetvalue(res, 0, PQfnumber(res, "rngmultitype"))); + /* print subtype_opclass only if not default for subtype */ if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't') { @@ -10728,7 +10811,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo) if (dopt->binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid, - false); + false, false); appendPQExpBuffer(q, "CREATE TYPE %s;\n", qualtypname); @@ -10913,7 +10996,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) if (dopt->binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid, - false); + false, false); appendPQExpBuffer(q, "CREATE TYPE %s (\n" @@ -11103,7 +11186,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) if (dopt->binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid, - true); /* force array type */ + true, /* force array type */ + false); /* force multirange type */ qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo)); @@ -11291,7 +11375,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) { binary_upgrade_set_type_oids_by_type_oid(fout, q, tyinfo->dobj.catId.oid, - false); + false, false); binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false); } @@ -11565,7 +11649,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) if (dopt->binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(fout, q, stinfo->baseType->dobj.catId.oid, - false); + false, false); appendPQExpBuffer(q, "CREATE TYPE %s;\n", fmtQualifiedDumpable(stinfo)); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 317bb839702..d7f77f1d3e0 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -176,6 +176,7 @@ typedef struct _typeInfo char typrelkind; /* 'r', 'v', 'c', etc */ char typtype; /* 'b', 'c', etc */ bool isArray; /* true if auto-generated array type */ + bool isMultirange; /* true if auto-generated multirange type */ bool isDefined; /* true if typisdefined */ /* If needed, we'll create a "shell type" entry for it; link that here: */ struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */ diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index ec636620601..11dc98ee0a5 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1601,6 +1601,7 @@ my %tests = ( \QOPERATOR 3 dump_test.~~(integer,integer);\E\n.+ \QCREATE TYPE dump_test.range_type_custom AS RANGE (\E\n\s+ \Qsubtype = integer,\E\n\s+ + \Qmultirange_type_name = dump_test.multirange_type_custom,\E\n\s+ \Qsubtype_opclass = dump_test.op_class_custom\E\n \Q);\E /xms, @@ -1698,6 +1699,7 @@ my %tests = ( regexp => qr/^ \QCREATE TYPE dump_test.textrange AS RANGE (\E \n\s+\Qsubtype = text,\E + \n\s+\Qmultirange_type_name = dump_test.textmultirange,\E \n\s+\Qcollation = pg_catalog."C"\E \n\);/xm, like => |
